Highlight
medspec opened this issue ยท 10 comments
Is there a way to add Highlight to search result?
You can use any feature provided by https://github.com/ongr-io/ElasticsearchDSL package
Here an example of using highlight https://stackoverflow.com/questions/56720692/add-highlight-using-ongr-io-elasticsearchdsl-package/56729546#56729546
Just put that code into callback of a search method
Product::search('title:this OR description:this) AND (title:that OR description:that', function ($client, Search $body) {
$higlight = new Highlight();
$higlight->addField('title');
$body->addHighlight($higlight);
return $client->search(['index' => 'products', 'body' => $body->toArray()]);
})->raw();
Is there a way to take this raw query and pass it back into a laravel collection that includes the highlights? Currently the raw query response places the "highlight" outside of the _source field that is aggregated into eloquent models via the map function in the custom scout engine.
You can implement your own EloquentHitsIteratorAggregate
and pass raw results there.
I have plans to inject EloquentHitsIteratorAggregate using DI to be able to use different implementations, but now I'm working on another feature. Feel free to send PR)
An example of merging highlights into a custom hits aggregator. It may not be best practice... but is working. Comments welcome.
public function getIterator()
{
$hits = collect();
if ($this->results['hits']['total']) {
$raw = collect($this->results['hits']['hits']);
$models = $this->collectModels($raw);
$eloquentHits = $this->getEloquentHits($raw, $models);
$hits = $this->mergeHighlightsIntoModels($eloquentHits, $raw);
}
return new \ArrayIterator($hits);
}
private function collectModels($rawHits)
{
return collect($rawHits)->groupBy('_source.__class_name')
->map(function ($results, $class) {
$model = new $class;
$builder = new Builder($model, '');
if (! empty($this->callback)) {
$builder->query($this->callback);
}
/* @var Searchable $model */
return $models = $model->getScoutModelsByIds(
$builder, $results->pluck('_id')->all()
);
})
->flatten()->keyBy(function ($model) {
return get_class($model).'::'.$model->getScoutKey();
});
}
private function getEloquentHits($hits, $models)
{
return collect($hits)->map(function ($hit) use ($models) {
$key = $hit['_source']['__class_name'].'::'.$hit['_id'];
return isset($models[$key]) ? $models[$key] : null;
})->filter()->all();
}
private function mergeHighlightsIntoModels($eloquentHits, $raw)
{
return collect($eloquentHits)->map(function(Model $eloquentHit) use($raw){
$raw = collect($raw)
->where('_source.__class_name', get_class($eloquentHit))
->where('_source.id', $eloquentHit->id)
->first();
foreach($raw['highlight'] ?? [] as $key => $highlight){
$eloquentHit->setAttribute($key, $highlight[0]);
}
return $eloquentHit;
})->all();
}
Search results are sorted by ID but id is segmented. What should I do
how can i add score to fields, best score will be in the begin
Learn it here https://github.com/ongr-io/ElasticsearchDSL
Thank you
Hello, how do I use it?