Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] how to include custom attributes in Eloquent model of search results #149

Closed
freeku opened this issue Dec 30, 2020 · 3 comments
Closed
Labels
enhancement New feature or request

Comments

@freeku
Copy link

freeku commented Dec 30, 2020

Does anyone know how to include custom attributes from the index into the model when returning search results?

I've added custom fields in the index using toSearchableArray and these are showing up when returning the raw results. However, when I use the ->get(), the custom fields are not included in the model objects (as they are not in the underlying database table), and I can't seem to find out how to include them.

@freeku freeku added the enhancement New feature or request label Dec 30, 2020
@ametad
Copy link
Contributor

ametad commented Jan 4, 2021

Two options:

Eloquent accessor

https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor

When you can generate / build up the data again on runtime, just do so with an accessor in an Eloquent model. Now you have the data again from the model.

Implement your own EloquentHitsIteratorAggregate

Or, you enrich the model with your own EloquentHitsIteratorAggregate class. This is described here: #28

I use this to include the _score and highlight, that Elasticsearch gave back, unto the model. Like so:

class EloquentHitsIteratorAggregate implements IteratorAggregate
{
    /**
     * @var array
     */
    private $results;
    /**
     * @var callable|null
     */
    private $callback;

    /**
     * @param array $results
     * @param callable|null $callback
     */
    public function __construct(array $results, callable $callback = null)
    {
        $this->results = $results;
        $this->callback = $callback;
    }

    /**
     * @inheritDoc
     */
    public function getIterator()
    {
        if (! $this->results['hits']['total']) {
            return new \ArrayIterator(collect());
        }

        /** @var Collection $models */
        $models = collect($this->results['hits']['hits'])->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 $model->getScoutModelsByIds(
                    $builder, $results->pluck('_id')->all()
                );
            })
            ->flatten()->keyBy(function ($model) {
                return get_class($model).'::'.$model->getScoutKey();
            });

        /** @var Collection $hits */
        $hits = collect($this->results['hits']['hits'])->map(function ($hit) use ($models) {
            $key = $hit['_source']['__class_name'].'::'.$hit['_id'];

            if (!isset($models[$key])) {
                return null;
            }

            $model = $models[$key];

            // Enrich the model with data from Elasticsearch
            $model->_score = $hit['_score'];
            $model->_highlight = $hit['highlight'] ?? null;

            return $model;

        })->filter()->all();

        return new \ArrayIterator($hits);
    }
}

@ametad
Copy link
Contributor

ametad commented Jan 4, 2021

PS. You can implement your own class by binding it in the ioc. This can be done in AppServiceProvider::register():

        $this->app->bind(
            'Matchish\ScoutElasticSearch\ElasticSearch\HitsIteratorAggregate',
            'App\Elasticsearch\EloquentHitsIteratorAggregate'
        );

@freeku
Copy link
Author

freeku commented Jan 5, 2021

Great, thanks for the extensive reply @ametad! I've been able to add the required fields, thanks!

@freeku freeku closed this as completed Jan 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants