Skip to the content.

Search Responses

Client::search() returns a SearchResponse.

Typed hits

<?php
foreach ($response->hits() as $hit) {
    $hit->id;             // string
    $hit->index;          // string
    $hit->score;          // ?float
    $hit->source;         // array
    $hit->matchedQueries; // list<string> — names given with queryName()
    $hit->sort;           // sort values, for searchAfter()
    $hit->highlight;      // array
    $hit->explanation;    // ?array, with explain()
    $hit->matched('vip'); // bool
}

Response metadata

Method Returns
getTotal() ?int — number of matches; null when trackTotalHits(false)
totalRelation() "eq" exact, "gte" lower bound, or null
maxScore() ?float
took() ?int milliseconds
timedOut() bool — true means the hits are partial
failedShards() int — documents on failed shards are missing
getAggregations() array
getHits(), toArray() the raw hits / raw response

Check timedOut() and failedShards() whenever “nothing found” leads to a decision.

Mapping hits to your classes

documents() builds one object per hit through the class constructor. Parameters are filled from the source field with the same name or its snake_case form; missing fields use the parameter default or null:

<?php
final class Product
{
    public function __construct(
        public readonly string $name,
        public readonly float $price,
        public readonly ?string $createdAt, // from "created_at"
    ) {
    }
}

/** @var list<Product> $products */
$products = $response->documents(Product::class);

A required field that is missing, or a value of the wrong type, throws an UnexpectedValueException naming the field. For mappings that need more than the source, use map():

<?php
use Zvonchuk\Elastic\Search\SearchHit;

$rows = $response->map(fn (SearchHit $hit) => ['id' => $hit->id, 'score' => $hit->score] + $hit->source);

Both methods carry PHPStan generics, so static analysis knows the element type.