how to implement Elastic Search in Laravel for auto complete

Viewed 1155

I followed this wonderful tutorial Made with love ES laravel tutorial to implement ES in my Laravel Ecommerce app i am building. I got it to work but i wanted to tweak it a little because as of now, the results only work when i my query term matches exactly an entire word i have in one of my products.

i have a health nut bar called bear naked almond bar, but when i type "bea" it doesn't match anything, but when i type "bear" then it works.

This is my search query as of now.

$model = new ProductsListing;

$items = $this->elasticsearch->search([
    'index' => $model->getSearchIndex(),
    'type' => $model->getSearchType(),
    'body' => [
        'query' => [
            'multi_match' => [
                'fields' => ['brand', 'name^5', 'description'],
                'query' => $query,
            ],
        ],
    ],
]);

I would need some help to adjust the query so that i get results as i type.

3 Answers

I am not familiar with PHP and laravel, but the reason why it's not giving result on bea b/c you are using match query which applies the same analyzer which is used at index time, and your text bear naked almond bar creates bear, naked, almond and bar tokens and bea doesn't match any token.

You can change your query below(not sure about the correct syntax for the prefix in laravel).

$model = new ProductsListing;
        
$items = $this->elasticsearch->search([
    'index' => $model->getSearchIndex(),
    'type' => $model->getSearchType(),
    'body' => [
        'query' => [
            'prefix' => [. --> changed to prefix query.
                'fields' => ['brand', 'name^5', 'description'],
                'query' => $query,
            ],
        ],
    ],
]);

I am assuming that you are using the text field with default standard analyzer and using the analyze API, you can check the tokens generated for your text.

POST your-index/_analyze

{
    "text": "bear naked almond bar",
    "analyzer" : "standard"
}

{
    "tokens": [
        {
            "token": "bear",
            "start_offset": 0,
            "end_offset": 4,
            "type": "<ALPHANUM>",
            "position": 0
        },
        {
            "token": "naked",
            "start_offset": 5,
            "end_offset": 10,
            "type": "<ALPHANUM>",
            "position": 1
        },
        {
            "token": "almond",
            "start_offset": 11,
            "end_offset": 17,
            "type": "<ALPHANUM>",
            "position": 2
        },
        {
            "token": "bar",
            "start_offset": 18,
            "end_offset": 21,
            "type": "<ALPHANUM>",
            "position": 3
        }
    ]
}

Try match_phrase_prefix with must query inside bool query instead of multi_match ... you will start getting result as you type...

e.g

$query = '{
          "size": 10, //size of return array 
          "query": {
            "bool": {
              "must": [
                {
                  "match_phrase_prefix": {
                    "tag":"' . $filter_name . '"
                    }
                }
              ],
            }
          }
        }';

//Now pass this $query variable to your ES request

there are two methods 1- prefix , 2- match_phrase_prefix

Please see documentation for prefix and match_phrase_prefix

I was able to get help here: github

and this was the perfect solution. Thank you Enricco Zimuel.

        $elasticsearch = ClientBuilder::create()
        ->setHosts(config('services.search.hosts'))
        ->build();

        $model = new ProductsListing;


        $items = $elasticsearch->search([
            'index' => $model->getSearchIndex(),
            'type' => $model->getSearchType(),
            'body' => [

                //this helped a lot
                // https://stackoverflow.com/questions/60716639/how-to-implement-elastic-search-in-laravel-for-auto-complete

                //using query_string
                'query' => [
                    'query_string' => [
                        // 'fields' => ['brand', 'name^5', 'description'], //notice the weight operator ^5
                        'fields' => ['name'],
                        'query' => $query.'*',
                    ],
                ],

            ],
        ]);
Related