How to enable substring search and substring highlighting in elasticsearch like solr ngram

I want something like if user search for "alb" it should return "alba", "albert" etc. with highlighting as well.

Here is my current model code:


<?php


namespace app\models;


use Yii;

use yii\db\ActiveRecord;


class ElasticSearch extends \yii\elasticsearch\ActiveRecord

{


    public function attributes()

    {


        return['id', 'name', 'email'];


    }

    

    public static function mapping()

    {

        return [

            static::type() => [

                'properties' => [

                    'id'       => ['type' => 'integer'],

                    'name'     => ['type' => 'string'],

                    'email'    => ['type' => 'string'],

                ]

            ],

        ];

    }

    

    /**

     * Set (update) mappings for this model

     */

    public static function updateMapping()

    {

        $db = static::getDb();

        $command = $db->createCommand();

        $command->setMapping(static::index(), static::type(), static::mapping());

    }


    /**

     * Create this model's index

     */

    public static function createIndex()

    {

        $db = static::getDb();

        $command = $db->createCommand();

        $command->createIndex(static::index(), ['mappings' => static::mapping()]);

    }


    /**

     * Delete this model's index

     */

    public static function deleteIndex()

    {

        $db = static::getDb();

        $command = $db->createCommand();

        $command->deleteIndex(static::index(), static::type());

    }


}



You enable the highlight in the search:

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html

Example using the yii extension ( not tested ):


$result = ElasticSearch::find()->query(

        [

            "match_phrase" =>

                [

                    "name" => "John Doe"

                ]




        ]

    )->highlight([

        "pre_tags" => ['<strong>'],  //default is <em>

        "post_tags" => ['</strong>'],

        'fields' => ['name' => new stdClass() ]

    ])->search();


    // show results

    echo VarDumper::dumpAsString(($result['hits']));

    

About your code you should remove the:


use yii\db\ActiveRecord;

Because you are already using the AR from ElasticSearch

Hi Aryel,

Thanks for your response. Now I understand the difference between index and record. What I’m trying to do is:

  1. I’ve created an index “users” in elasticsearch and I want to add all records from my database users table (fields are ‘id’, ‘name’, ‘email’) to this index (how to add record to a particular index if I have multiple indexes).

  2. Want to create an autocomplete with substring highlighting using n-gram.

Here’s my updated model code (Full word is matching but substring is not matching):


<?php


namespace app\models;


use Yii;


use yii\base\Model;

 

use yii\elasticsearch\ActiveDataProvider;

 

use yii\elasticsearch\Query;

 

use yii\elasticsearch\QueryBuilder;


class Elastic extends \yii\elasticsearch\ActiveRecord

{


    public function attributes()

    {

        return['id', 'name', 'email'];

    }

    

    public static function mapping()

    {

        return [

            static::type() => [

                'properties' => [

                    'id'       => ['type' => 'integer'],

                    'name'     => [

                        'type' => 'string',

                        "analyzer" => "autocomplete",

                        "search_analyzer" => "standard",

                        

                    ],

                    'email'    => ['type' => 'string'],

                ]

            ],

        ];

    }

    

    /**

     * Set (update) mappings for this model

     */

    public static function updateMapping()

    {

        $db = static::getDb();

        $command = $db->createCommand();

        $command->setMapping(static::index(), static::type(), static::mapping());

    }


    /**

     * Create this model's index

     */

    public static function createIndex()

    {

        $db = static::getDb();

        $command = $db->createCommand();

        $command->createIndex('users', [

            'settings' => [

                'index' => [

                    "number_of_shards" => 1,

                    "number_of_replicas" => 1,

                ],

                'analysis' => [

                    'analyzer' => [

                        "autocomplete" => [

                            "type" => "custom",

                            "tokenizer" => "standard",

                            "filter" => [ "lowercase", "autocomplete_filter" ],

                        ],

                    ],

                    'filter' => [

                        "autocomplete_filter" => [

                            "type" => "edge_ngram",

                            "min_gram" => 2,

                            "max_gram" => 15,

                        ],

                    ],

                    

                ],

            ],

            'mappings' => static::mapping(),

            //'warmers' => [ /* ... */ ],

            //'aliases' => [ /* ... */ ],

            //'creation_date' => '...'

        ]);

    }


    /**

     * Delete this model's index

     */

    public static function deleteIndex()

    {

        $db = static::getDb();

        $command = $db->createCommand();

        $command->deleteIndex(static::index(), static::type());

    }

   /**

     * Search index

     */

   public function Searches($value)

   {

        $search      = $value['search'];

        $query        = new Query();

        $db           = Elastic::getDb();

        $queryBuilder = new QueryBuilder($db);

        $match   = ['match' => ['name' =>$search]];

        $query->query = $match;

        $build        = $queryBuilder->build($query);

        $re           = $query->search($db, $build);

        $dataProvider = new ActiveDataProvider([

            'query'      => $query,

            'pagination' => ['pageSize' => 10],

        ]);

        return $dataProvider;

   }

}

?>

And here is my controller code:


<?php

namespace app\controllers;


use app\models\Customer;

use app\models\Search;

use Yii;

use yii\web\Controller;


class ElasticController extends Controller

{

    public function actionCreateIndex(){

        $elastic = new Search();

        if ($elastic->createIndex()) {

            echo "Index created Successfully";

        } else {

            echo "Error";

        }

    }


    public function actionSearch(){


        $elastic = new Search();

        $result  = $elastic->Searches(Yii::$app->request->queryParams);

        $query = Yii::$app->request->queryParams;

        return $this->render('search', [

            'searchModel'  => $elastic,

            'dataProvider' => $result,

            'query'        => $query['search'],

        ]);

    }

}

?>


class Elastic extends \yii\elasticsearch\ActiveRecord

{

.

.

.

Yii creates an index with the name of its class, so "elastic" will be your index. You can override those methods and put the index and type name that you want.

Example:




class UserElastic extends \yii\elasticsearch\ActiveRecord

{


 /**

     * @inheritdoc

     */

    public static function index()

    {

        return 'users';

    }


    /**

     * @inheritdoc

     */

    public static function type()

    {

        return 'user';

    }

.

.

.

// the rest of your code

.

.

.

.

/**

     * Create this model's index

     */

    public static function createIndex()

    {

        $db = static::getDb();

        $command = $db->createCommand();

        $command->createIndex(static::index(), [

        .

        .

        .

// the rest of your code

}




Now you have a index called users and a type called user.

For indexing just do a ->save(). It will be saved in the index users and type user.


$user = new UserElastic();

$user->id = 1;

$user->attributes = ['email' => 'email@email.com', 'name' => 'someone'];

$user->save();



If you have too many data in your DB. You can use the bulk API, you can save time and RAM

https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html

http://www.yiiframework.com/doc-2.0/yii-elasticsearch-bulkcommand.html

You can use the Kartik widget.

http://demos.krajee.com/widget-details/typeahead

In the action controller instead of using the database select you will use an Elastic query.