'Laravel Scout/Meilisearch - filter by a non-searchable column

I want to make it so that I can filter results based on a column that isn't searchable with Meilisearch and Laravel scout.

So imagine a "Comment" table, with the following searchable columns:

public function toSearchableArray() {
    $array = Arr::only(
        $this->toArray(),
        ['id','title', 'link', 'raw_text', 'subchan', 'nsfw']
    );
    
    return $array;  
}

But only get results past a certain date:

Comment::search($query, ['filters' => 'created_at > 795484800'])

To do this, I need to add created_at scout's toSearchableArray. The problem with this is that when a user searches, results from created_at will also be queried.



Solution 1:[1]

If I understand you correctly you want to be able to filter based on the created_at column, but it shouldn't be searchable, ie entering "795" as a query shouldn't return all results where "795" is part of the timestamp?

I don't think Scout will allow you to achieve this in a simple way at the moment but it should still be possible.

Step 1 is to add the created_at column to the toSearchableArray() method. This will ensure the data is indexed by Meili.

Step 2 is to alter the configuration of the index where your model is searchable in order to exclude created_at from the list of searchable attributes. This is psuedo code and undocumented but it should look something like this:

$dummy = new Comment();

// Should resolve to an engine: https://github.com/laravel/scout/blob/f8aa3c3182fe97f56a6436fd0b28fcacfcbabc11/src/Searchable.php#L279
$engine = $dummy->searchableUsing();

// Should resolve to MeiliSearch\Endpoints\Indexes via a magic method that resolves the underlying Meili driver:
// https://github.com/laravel/scout/blob/33bbff0e3bfb1abd0ea34236c331fc17cdeac0bc/src/Engines/MeiliSearchEngine.php#L298
// ->
// https://github.com/meilisearch/meilisearch-php/blob/f25ee49b658f407af3d3f1f9a402997e7974b6bb/src/Delegates/HandlesIndex.php#L23
$index = $engine->index($dummy->searchableAs());

// https://github.com/meilisearch/meilisearch-php/blob/f25ee49b658f407af3d3f1f9a402997e7974b6bb/src/Endpoints/Delegates/HandlesSettings.php#L55
$index->updateSearchableAttributes(
    ['id','title', 'link', 'raw_text', 'subchan', 'nsfw']
);

Once created_at is indexed but not searchable you want to filter on the value. Meili has operators for numeric values.

Step 3 is to do a custom search using Scout:

Comment::search($query, function (Indexes $meilisearch, $query, $options) {
    $options['filters'] = 'created_at>795484800';

    return $meilisearch->search($query, $options);
});

Again, this is pseudo code – I haven't tested any part of it. I would really appreciate if Scout would implement support for customizing the index' settings on creation or exposing a method for updating the settings, allowing you to add driver specific settings in your configuration file for example.

Solution 2:[2]

I solved my problem by using filterable attributes of Meilisearch. But it needs to be configured before running the search. I used php artisan tinker to solve this as follows, you might want to write an artisan command to do so.

$client = new MeiliSearch\Client('https://url_to_meilisearch_instance:7700');

$client->index('comments_index')->updateFilterableAttributes(['created_at']); // Replace your index_name

And that's about it. If you have a rather large dataset, you might want to run the following command to check the status:

$client->index('comments_index')->stats();

If the response contains isIndexing => false you're good to go. Now you may run the filter as usual,

Comment::search($query)->where('created_at', '>', 795484800)->get();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 DharmanBot