美文网首页
laravel Elasticsearch的搜索操作

laravel Elasticsearch的搜索操作

作者: jooohnny | 来源:发表于2018-07-20 17:20 被阅读0次

Elasticsearch 是一个分布式的搜索和分析引擎,可以用于全文检索、结构化检索和分析,并能将这三者结合起来。Elasticsearch 基于 Lucene 开发,是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。现在是使用最广的开源搜索引擎之一,Wikipedia、Stack Overflow、GitHub 等都基于 Elasticsearch 来构建他们的搜索引擎。

安装elasticsearch

https://github.com/medcl/elasticsearch-rtf
当前的版本是 Elasticsearch 5.1.1,ik 插件也是直接自带了。安装好 ElasticSearch,跑起来服务,测试服务安装是否正确:
http://127.0.0.1:9200

安装laravel以及插件

使用的是laravel5.5框架,composer安装或者官网下载

composer create-project laravel/laravel=5.5 blog 

composer安装scout

composer require laravel/scout

安装elasticsearch驱动

composer require tamayo/laravel-scout-elastic

引入GuzzleHttp包

composer require Guzzlehttp/guzzle

将类引到app.php里面

'providers' => [
    ...
    ...
    Laravel\Scout\ScoutServiceProvider::class,
    ScoutEngines\Elasticsearch\ElasticsearchProvider::class,
]

vendor:publish生成scout配置文件

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

config/scout.php设置elasticsearch环境

return [
    'driver' => env('SCOUT_DRIVER', 'elasticsearch'),
    ...
    'elasticsearch' => [
        'index' => env('ELASTICSEARCH_INDEX', 'laravel'),
        'hosts' => [
            env('ELASTICSEARCH_HOST', '127.0.0.1'),
        ],
     ],
];

数据部分

migration创建article表,数据方面可以自己填充

php artisan make:migration create_article_table
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateArticleTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('article', function (Blueprint $table) {
            $table->increments('id');
            $table->text('url');
            $table->string('author', 64)->nullable()->default(null);
            $table->text('title');
            $table->longText('content');
            $table->dateTime('post_date')->nullable()->default(null);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('article');
    }
}

代码部分

创建article的模板和索引

php artisan make:command InitEs

app/console/command/InitEs.php

namespace App\Console\Commands;

use GuzzleHttp\Client;
use Illuminate\Console\Command;

class InitEs extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'es:init';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Init es to create index';

    /**
     * Create a new command instance.
     *
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $client = new Client();
        $this->createTemplate($client);
        $this->createIndex($client);
    }

    protected function createIndex(Client $client)
    {
        $url = config('scout.elasticsearch.hosts')[0] . ':9200/' . config('scout.elasticsearch.index');
        $client->put($url, [
            'json' => [
                'settings' => [
                    'refresh_interval' => '5s',
                    'number_of_shards' => 1,
                    'number_of_replicas' => 0,
                ],
                'mappings' => [
                    '_default_' => [
                        '_all' => [
                            'enabled' => false
                        ]
                    ]
                ]
            ]
        ]);
    }

    protected function createTemplate(Client $client)
    {
        $url = config('scout.elasticsearch.hosts')[0] . ':9200/' . '_template/rtf';
        $client->put($url, [
            'json' => [
                'template' => '*',
                'settings' => [
                    'number_of_shards' => 1
                ],
                'mappings' => [
                    '_default_' => [
                        '_all' => [
                            'enabled' => true
                        ],
                        'dynamic_templates' => [
                            [
                                'strings' => [
                                    'match_mapping_type' => 'string',
                                    'mapping' => [
                                        'type' => 'text',
                                        'analyzer' => 'ik_smart',
                                        'ignore_above' => 256,
                                        'fields' => [
                                            'keyword' => [
                                                'type' => 'keyword'
                                            ]
                                        ]
                                    ]
                                ]
                            ]
                        ]
                    ]
                ]
            ]
        ]);

    }
}

调用es脚本

php artisan es:init

创建Article模型
app/Model/Article.php

<?php

namespace App\Model;

//use App\Libraries\EsSearchable;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

/**
 * Class Post
 * @package App
 * @property string $url
 * @property string $author
 * @property string $content
 * @property string $title
 * @property string $post_date
 * @property string $created_at
 * @property string $updated_at
 */
class Article extends Model
{
    use Searchable;
    protected $table = 'article';

    protected $fillable = [
        'url',
        'author',
        'title',
        'content',
        'post_date'
    ];

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

导入数据模型

php artisan scout:import "App\Model\Article"

新建访问页面的控制器 PostController
app\Http\Controllers\PostController

<?php

namespace App\Http\Controllers;

use App\Model\Article;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function search(Request $request)
    {
        $q = $request->get('keyword');
        $paginator = [];
        if ($q) {
            $paginator = Article::search($q)->paginate();
        }
        return view('search', compact('paginator', 'q'));
    }
}

搜索关键词照顾,变量dump出来的样子

<pre class="sf-dump" id="sf-dump-609623676" >LengthAwarePaginator</abbr> {#338 ▼ <samp data-depth="1" class="sf-dump-expanded">#total: 1
  #lastPage: 1
  #items: <abbr title="Illuminate\Database\Eloquent\Collection" class="sf-dump-note" style="text-decoration: none; border: none; cursor: help; color: rgb(167, 29, 93);">Collection</abbr> {#340 ▼ <samp data-depth="2" > 
        #attributes: array:9 [▼ <samp data-depth="5" class="sf-dump-expanded">"id" => 52
          "url" => "http://www.baidu.com"
          "author" => "johnny"
          "title" => "红包"
          "content" => "谁人曾照顾过我的感受"
          "post_date" => "2018-07-19 10:58:35"
          "updated_at" => "2018-07-19 10:58:40"
          "created_at" => "2018-07-19 10:58:42"
          "highlight" => array:2 [▼ <samp data-depth="6" class="sf-dump-expanded">"content.keyword" => array:1 [▼ <samp data-depth="7" class="sf-dump-expanded">0 => "<em>谁人曾照顾过我的感受</em>"</samp> ]
            "content" => array:1 [▼ <samp data-depth="7" class="sf-dump-expanded">0 => "谁人曾<em>照顾</em>过我的感受"</samp> ]</samp> ]</samp> ]
        #original: array:8 [▶]
        #changes: []
        #casts: []
        #dates: []
        #dateFormat: null
        #appends: []
        #dispatchesEvents: []
        #observables: []
        #relations: []
        #touches: []
        +timestamps: true
        #hidden: []
        #visible: []
        #guarded: array:1 [▶]
        #scoutMetadata: []</samp> }</samp> ]</samp> }
  #perPage: 15
  #currentPage: 1
  #path: "http://blog.com/search"
  #query: array:1 [▶]
  #fragment: null
  #pageName: "page"</samp> }</pre>

highlight,也就是高亮需要修改源代码,把highlight的json加进去
vendor\tamayo\laravel-scout-elastic\src\ElasticsearchEngine

<?php

namespace ScoutEngines\Elasticsearch;

use Laravel\Scout\Builder;
use Laravel\Scout\Engines\Engine;
use Elasticsearch\Client as Elastic;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Collection as BaseCollection;

class ElasticsearchEngine extends Engine
{
    ...
    ...
    ...

    /**
     * Perform the given search on the engine.
     *
     * @param  Builder  $builder
     * @param  array  $options
     * @return mixed
     */
    protected function performSearch(Builder $builder, array $options = [])
    {
        $params = [
            'index' => $this->index,
            'type' => $builder->index ?: $builder->model->searchableAs(),
            'body' => [
                'query' => [
                    'bool' => [
                        'must' => [['query_string' => [ 'query' => "*{$builder->query}*"]]]
                    ]
                ]
            ]
        ];

        $params['body']['highlight']['fields']['*'] = new \stdClass();

        if ($sort = $this->sort($builder)) {
            $params['body']['sort'] = $sort;
        }

        if (isset($options['from'])) {
            $params['body']['from'] = $options['from'];
        }

        if (isset($options['size'])) {
            $params['body']['size'] = $options['size'];
        }

        if (isset($options['numericFilters']) && count($options['numericFilters'])) {
            $params['body']['query']['bool']['must'] = array_merge($params['body']['query']['bool']['must'],
                $options['numericFilters']);
        }
        if ($builder->callback) {
            return call_user_func(
                $builder->callback,
                $this->elastic,
                $builder->query,
                $params
            );
        }

        return $this->elastic->search($params);
    }
    ...
    ...

    /**
     * Map the given results to instances of the given model.
     *
     * @param  mixed  $results
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return Collection
     */
    public function map($results, $model)
    {
        if ($results['hits']['total'] === 0) {
            return Collection::make();
        }

        $keys = collect($results['hits']['hits'])
                        ->pluck('_id')->values()->all();

        $models = $model->whereIn(
            $model->getKeyName(), $keys
        )->get()->keyBy($model->getKeyName());

        return collect($results['hits']['hits'])->map(function ($hit) use ($model, $models) {
            $one = $models[$hit['_id']];
            if (isset($hit['highlight'])) {
                $one->highlight = $hit['highlight'];
            }
            return $one;
        })->filter()->values();
    }
   ...
   ...
 
}

相关文章

网友评论

      本文标题:laravel Elasticsearch的搜索操作

      本文链接:https://www.haomeiwen.com/subject/lftopftx.html