god-jay/scout-elasticsearch

v1.0.1 2021-12-20 11:30 UTC

This package is auto-updated.

Last update: 2024-04-20 17:24:52 UTC


README

Use elasticsearch as easy as using Eloquent ORM in your laravel application.

English | 简体中文

Contents

Installation

You can install the package via composer:

composer require god-jay/scout-elasticsearch

After installing the package, you should publish the Scout configuration using the vendor:publish Artisan command. This command will publish the scout.php configuration file to your config directory:

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

Then add

SCOUT_DRIVER=elastic
ELASTICSEARCH_HOST=your_es_host_ip:port

#Add if should auth es user
ELASTICSEARCH_USER=your_es_user
ELASTICSEARCH_PASS=your_es_pass

Docker compose run es + kibana

If you don't have your own es service, you may install and run es + kibana with docker compose:

  • You should install docker compose first: install docker compose

  • Then run the command in the root of this directory:

    docker-compose up -d
  • You can browse http://localhost:5601 to visit kibana.

  • To stop docker containers, run the command in the root of this directory:

    docker-compose down

in your .env file.

Configuration

Assuming there is a posts table and a Post Model, the simplified table may looks like:

id title content created_at
1 标题 文本内容 2020-01-01 01:01:01

Use GodJay\ScoutElasticsearch\Searchable in your model:

namespace App\Models;

use GodJay\ScoutElasticsearch\Searchable;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use Searchable;
}

Add searchableAs function in the model:

public function searchableAs()
{
    //elasticsearch index name, you can set any name you like in the model
    return 'posts';
}

Usage

Create elasticsearch index

Add getElasticMapping function in the model,

then run php artisan elastic:create-index "App\Models\Post"

For more details, see Create index API

public function getElasticMapping()
{
    return [
        'title' => [
            'type' => 'text',
            'analyzer' => 'ik_max_word',
            'search_analyzer' => 'ik_smart',
        ],
        'content' => [
            'type' => 'text',
            'analyzer' => 'ik_max_word',
            'search_analyzer' => 'ik_smart',
        ],
    ];
}

The elasticsearch index will be like:

{
  "mapping": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        },
        "title": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        }
      }
    }
  }
}

Import the given model into the search index

If there already exist many rows in your table, and you want to import the rows to elasticsearch,

Add toSearchableArray function in the model, then run php artisan scout:import "App\Models\Post"

public function toSearchableArray()
{
    return [
       'id' => $this->attributes['id'],
       'title' => $this->attributes['title'],
       'content' => strip_tags($this->attributes['content']),
       'created_at' => $this->attributes['created_at'],
   ];
}

After import the rows from table above, the elasticsearch index will be like:

{
  "mapping": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        },
        "created_at": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "id": {
          "type": "long"
        },
        "title": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        }
      }
    }
  }
}

Flush all of the model's records from the index

Run php artisan scout:flush "App\Models\Post"

Adding Records

Once you have added the Searchable trait to a model, all you need to do is save a model instance and it will automatically be added to your search index.

$post = new Post();

// ...

$post->save();

Updating Records

To update a searchable model, you only need to update the model instance's properties and save the model to your database.

$post = Post::find(1);

// Update the order...

$post->save();

Removing Records

To remove a record from your index, delete the model from the database. This form of removal is even compatible with soft deleted models:

$post = Post::find(1);

$post->delete();

Searching

Base:

$posts = Post::search('内容')->get();

Paginate:

$posts = Post::search('内容')->paginate(10);

Highlight:

$post = Post::search('内容')->highlight(['title' => null, 'content' => null])->first();

The search result will be:

App\Models\Post Object
(
    [table:protected] => ppp
    ...
    [attributes:protected] => [
        [id] => 1
        [title] => 标题
        [content] => 文本内容
        [created_at] => 2020-01-01 01:01:01
    ]
    [relations:protected] => [
        [highlight] => GodJay\ScoutElasticsearch\Highlight Object
        (
            [attributes:protected] => [
                [content] => [
                    [0] => 文本<em>内容</em>
                ]
            ]
        )
    ]
)

Advanced Usage

ES script sort:

use GodJay\ScoutElasticsearch\ElasticsearchEngine;

$posts = Post::search('', function (ElasticsearchEngine $engine, string $query, array $params) {
    $params['body']['sort'] = array_merge([[
        '_script' => [
            'type' => 'number',
            'script' => ['source' => "doc['field_a'].value * 0.7 + doc['field_b'].value * 0.3"],
            'order' => 'desc'
        ]
    ]], $params['body']['sort'] ?? []);
    $engine->setQueryParams($params);
    return $engine;
})->orderBy('id', 'desc')->where('field_c', 1)->get();

Debug:

use GodJay\ScoutElasticsearch\ElasticsearchEngine;

$debug = Post::search('', function (ElasticsearchEngine $engine, string $query, array $params) {
    $params['body']['sort'] = array_merge([[
        '_script' => [
            'type' => 'number',
            'script' => ['source' => "doc['field_a'].value * 0.7 + doc['field_b'].value * 0.3"],
            'order' => 'desc'
        ]
    ]], $params['body']['sort'] ?? []);
    $engine->setQueryParams($params);
    return $engine;
})->orderBy('id', 'desc')->where('field_c', 1)->where('field_d', ['x', 'y'])->debugSearch();

The result will be:

Array
(
    [result] => Illuminate\Database\Eloquent\Collection Object
    ...
    [query_params] => Array
    ...
    [exception] => 
    ...
)

The json string of $debug['query_params'] will be:

{
  "index": "posts",
  "body": {
    "sort": [
      {
        "_script": {
          "type": "number",
          "script": {
            "source": "doc['field_a'].value * 0.7 + doc['field_b'].value * 0.3"
          },
          "order": "desc"
        }
      },
      {
        "id": "desc"
      }
    ],
    "query": {
      "bool": {
        "must": [
          {
            "match_phrase": {
              "field_c": 1
            }
          },
          {
            "terms": {
              "field_d": [
                "x",
                "y"
              ]
            }
          }
        ]
      }
    }
  }
}