composer.json
composer require laravel/scout=5.0
註冊服務提供器,你需要將 ScoutServiceProvider 添加到你的配置文件 config/app.php 的 providers 數組中。
'providers' => [
...
Laravel\Scout\ScoutServiceProvider::class,
],
composer require tamayo/laravel-scout-elastic=4.0
安裝Java 自行百度 參考 https://blog.csdn.net/gaokcl/article/details/82289672
參考:https://learnku.com/articles/20311 十分感謝 @沙漠行者 下面也是他的建議:
1. 使用這個包是怎麼同步數據的?是通過使用它的增,刪。也就是說不要手動的添加和刪除數據庫的數據。
2. 如果數據怎麼導入都不一致,這個問題確實出現過,那麼我們就要先徹底的清除 es 中的數據,在從新 import 你的 model 裏的數據。這樣數據也就可以保持同步了。
3. 切忌!如果你手動刪除或者添加了數據庫中的數據,要先執行 scout:flush,再執行 scout:import。
4. 如果覺得這個包不好用,可以直接使用原生的語句去寫,其實 es 就相當於一個數據庫,可以這麼理解。但是其實不一樣的。用途不一樣。開始對它的語法肯定覺得費勁,多閱讀一下官方文檔!慢慢就好了!
es 需要 Java支持的,自行百度安裝(Windows Linux 都是一樣)
修改
/vendor/tamayo/laravel-scout-elastic/src/ElasticsearchEngine.php
<?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
{
/**
* Index where the models will be saved.
*
* @var string
*/
protected $index;
/**
* Elastic where the instance of Elastic|\Elasticsearch\Client is stored.
*
* @var object
*/
protected $elastic;
/**
* Create a new engine instance.
*
* @param \Elasticsearch\Client $elastic
* @return void
*/
public function __construct(Elastic $elastic, $index)
{
$this->elastic = $elastic;
$this->index = $index;
}
/**
* Update the given model in the index.
*
* @param Collection $models
* @return void
*/
public function update($models)
{
$params['body'] = [];
$models->each(function($model) use (&$params)
{
$params['body'][] = [
'update' => [
'_id' => $model->getKey(),
'_index' => $this->index,
'_type' => $model->searchableAs(),
]
];
$params['body'][] = [
'doc' => $model->toSearchableArray(),
'doc_as_upsert' => true
];
});
$this->elastic->bulk($params);
}
/**
* Remove the given model from the index.
*
* @param Collection $models
* @return void
*/
public function delete($models)
{
$params['body'] = [];
$models->each(function($model) use (&$params)
{
$params['body'][] = [
'delete' => [
'_id' => $model->getKey(),
'_index' => $this->index,
'_type' => $model->searchableAs(),
]
];
});
$this->elastic->bulk($params);
}
/**
* Perform the given search on the engine.
*
* @param Builder $builder
* @return mixed
*/
public function search(Builder $builder)
{
return $this->performSearch($builder, array_filter([
'numericFilters' => $this->filters($builder),
'size' => $builder->limit,
]));
}
/**
* Perform the given search on the engine.
*
* @param Builder $builder
* @param int $perPage
* @param int $page
* @return mixed
*/
public function paginate(Builder $builder, $perPage, $page)
{
$result = $this->performSearch($builder, [
'numericFilters' => $this->filters($builder),
'from' => (($page * $perPage) - $perPage),
'size' => $perPage,
]);
$result['nbPages'] = $result['hits']['total']/$perPage;
return $result;
}
/**
* 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}*"]]]
]
]
]
];
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(isset($options['numericFilters'][0]['query_string'])) {
$params['body']['query']['bool']['must'][0]['query_string']['fields'] = $options['numericFilters'][0]['query_string'];
} else {
$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);
}
/**
* Get the filter array for the query.
*
* @param Builder $builder
* @return array
*/
protected function filters(Builder $builder)
{
return collect($builder->wheres)->map(function ($value, $key) {
if (is_array($value)) {
return ['terms' => [$key => $value]];
}
//這裏是修改的地方,$key = 'query',$value =['字段1','字段2']。 就是這裏的where('query', ['字段1','字段2'])。
if ($key == 'query') {
return ['query_string' => $value];
}
return ['match_phrase' => [$key => $value]];
})->values()->all();
}
/**
* Pluck and return the primary keys of the given results.
*
* @param mixed $results
* @return \Illuminate\Support\Collection
*/
public function mapIds($results)
{
return collect($results['hits']['hits'])->pluck('_id')->values();
}
/**
* Map the given results to instances of the given model.
*
* @param \Laravel\Scout\Builder $builder
* @param mixed $results
* @param \Illuminate\Database\Eloquent\Model $model
* @return Collection
*/
public function map(Builder $builder, $results, $model)
{
if ($results['hits']['total'] === 0) {
return Collection::make();
}
$keys = collect($results['hits']['hits'])
->pluck('_id')->values()->all();
$models = $model->getScoutModelsByIds(
$builder, $keys
)->keyBy(function ($model) {
return $model->getScoutKey();
});
return collect($results['hits']['hits'])->map(function ($hit) use ($model, $models) {
return isset($models[$hit['_id']]) ? $models[$hit['_id']] : null;
})->filter()->values();
}
/**
* Get the total count from a raw result returned by the engine.
*
* @param mixed $results
* @return int
*/
public function getTotalCount($results)
{
return $results['hits']['total'];
}
/**
* Generates the sort if theres any.
*
* @param Builder $builder
* @return array|null
*/
protected function sort($builder)
{
if (count($builder->orders) == 0) {
return null;
}
return collect($builder->orders)->map(function($order) {
return [$order['column'] => $order['direction']];
})->toArray();
}
}
/vendor/laravel/scout/src/Searchable.php
<?php
namespace Laravel\Scout;
use Laravel\Scout\Builder;
use Laravel\Scout\Jobs\MakeSearchable;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection as BaseCollection;
trait Searchable
{
/**
* Additional metadata attributes managed by Scout.
*
* @var array
*/
protected $scoutMetadata = [];
/**
* Boot the trait.
*
* @return void
*/
public static function bootSearchable()
{
static::addGlobalScope(new SearchableScope);
static::observe(new ModelObserver);
(new static)->registerSearchableMacros();
}
/**
* Register the searchable macros.
*
* @return void
*/
public function registerSearchableMacros()
{
$self = $this;
BaseCollection::macro('searchable', function () use ($self) {
$self->queueMakeSearchable($this);
});
BaseCollection::macro('unsearchable', function () use ($self) {
$self->queueRemoveFromSearch($this);
});
}
/**
* Dispatch the job to make the given models searchable.
*
* @param \Illuminate\Database\Eloquent\Collection $models
* @return void
*/
public function queueMakeSearchable($models)
{
if ($models->isEmpty()) {
return;
}
if (! config('scout.queue')) {
return $models->first()->searchableUsing()->update($models);
}
dispatch((new MakeSearchable($models))
->onQueue($models->first()->syncWithSearchUsingQueue())
->onConnection($models->first()->syncWithSearchUsing()));
}
/**
* Dispatch the job to make the given models unsearchable.
*
* @param \Illuminate\Database\Eloquent\Collection $models
* @return void
*/
public function queueRemoveFromSearch($models)
{
if ($models->isEmpty()) {
return;
}
return $models->first()->searchableUsing()->delete($models);
}
/**
* Determine if the model should be searchable.
*
* @return bool
*/
public function shouldBeSearchable()
{
return true;
}
/**
* Perform a search against the model's indexed data.
*
* @param string $query
* @param Closure $callback
* @return \Laravel\Scout\Builder
*/
public static function search($query, $callback = null)
{
return new Builder(
new static, $query, $callback, config('scout.soft_delete', false)
);
}
/**
* Make all instances of the model searchable.
*
* @return void
*/
public static function makeAllSearchable()
{
$self = new static();
$softDeletes = in_array(SoftDeletes::class, class_uses_recursive(get_called_class())) &&
config('scout.soft_delete', false);
$self->newQuery()
->when($softDeletes, function ($query) {
$query->withTrashed();
})
->orderBy($self->getKeyName())
->searchable();
}
/**
* Make the given model instance searchable.
*
* @return void
*/
public function searchable()
{
Collection::make([$this])->searchable();
}
/**
* Remove all instances of the model from the search index.
*
* @return void
*/
public static function removeAllFromSearch()
{
$self = new static();
$self->newQuery()
->orderBy($self->getKeyName())
->unsearchable();
}
/**
* Remove the given model instance from the search index.
*
* @return void
*/
public function unsearchable()
{
Collection::make([$this])->unsearchable();
}
/**
* Get the requested models from an array of object IDs;
*
* @param \Laravel\Scout\Builder $builder
* @param array $ids
* @return mixed
*/
public function getScoutModelsByIds(Builder $builder, array $ids)
{
$query = in_array(SoftDeletes::class, class_uses_recursive($this))
? $this->withTrashed() : $this->newQuery();
//把這行代碼註釋掉,不然會報錯:Undefined property: Laravel\Scout\Builder::$queryCallback
// if ($builder->queryCallback) {
// call_user_func($builder->queryCallback, $query);
// }
return $query->whereIn(
$this->getScoutKeyName(), $ids
)->get();
}
/**
* Enable search syncing for this model.
*
* @return void
*/
public static function enableSearchSyncing()
{
ModelObserver::enableSyncingFor(get_called_class());
}
/**
* Disable search syncing for this model.
*
* @return void
*/
public static function disableSearchSyncing()
{
ModelObserver::disableSyncingFor(get_called_class());
}
/**
* Temporarily disable search syncing for the given callback.
*
* @param callable $callback
* @return mixed
*/
public static function withoutSyncingToSearch($callback)
{
static::disableSearchSyncing();
try {
return $callback();
} finally {
static::enableSearchSyncing();
}
}
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs()
{
return config('scout.prefix').$this->getTable();
}
/**
* Get the indexable data array for the model.
*
* @return array
*/
public function toSearchableArray()
{
return $this->toArray();
}
/**
* Get the Scout engine for the model.
*
* @return mixed
*/
public function searchableUsing()
{
return app(EngineManager::class)->engine();
}
/**
* Get the queue connection that should be used when syncing.
*
* @return string
*/
public function syncWithSearchUsing()
{
return config('scout.queue.connection') ?: config('queue.default');
}
/**
* Get the queue that should be used with syncing
*
* @return string
*/
public function syncWithSearchUsingQueue()
{
return config('scout.queue.queue');
}
/**
* Sync the soft deleted status for this model into the metadata.
*
* @return $this
*/
public function pushSoftDeleteMetadata()
{
return $this->withScoutMetadata('__soft_deleted', $this->trashed() ? 1 : 0);
}
/**
* Get all Scout related metadata.
*
* @return array
*/
public function scoutMetadata()
{
return $this->scoutMetadata;
}
/**
* Set a Scout related metadata.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function withScoutMetadata($key, $value)
{
$this->scoutMetadata[$key] = $value;
return $this;
}
/**
* Get the value used to index the model.
*
* @return mixed
*/
public function getScoutKey()
{
return $this->getKey();
}
/**
* Get the key name used to index the model.
*
* @return mixed
*/
public function getScoutKeyName()
{
return $this->getQualifiedKeyName();
}
}
<?php
namespace App\Console\Commands;
use GuzzleHttp\Client;
use Illuminate\Console\Command;
class ESInit extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'es:init'; // 使用command 什麼命令啓動腳本
/**
* The console command description.
*
* @var string
*/
protected $description = 'init laravel es for post'; // 描述
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
* 實際要做的事情
* php artisan es:init
*
* @return mixed
*/
public function handle()
{
$client = new Client();
// 創建模版
$url = config('scout.elasticsearch.hosts')[0] . '/_template/tmp';
$client->delete($url); // 一開始沒有,
$client->put($url, [
'json' => [
'template' => config('scout.elasticsearch.index'),
'settings' => [
'number_of_shards' => 1
],
'mappings' => [
'_default_' => [
'_all' => [
'enabled' => true
],
'dynamic_templates' => [
[
'strings' => [
'match_mapping_type' => 'string',
'mapping' => [
'type' => 'text',
'analyzer' => 'ik_max_word', // 安裝ik中文分詞
'ignore_above' => 256,
'fields' => [
'keyword' => [
'type' => 'keyword'
]
]
]
]
]
]
]
]
]
]);
$this->info('=====創建模板成功=====');
// 創建 index
$url = config('scout.elasticsearch.hosts')[0] . '/' . config('scout.elasticsearch.index');
$client->delete($url); // 一開始沒有,報錯使用
$client->put($url, [
'json' => [
'settings' => [
'refresh_interval' => '5s',
'number_of_shards' => 1,
'number_of_replicas' => 0,
],
'mappings' => [
'_default_' => [
'_all' => [
'enabled' => false
]
]
]
]
]);
$this->info('=====創建索引成功=====');
}
}
ik中文分詞 ===> windows10
https://blog.csdn.net/qq_41676216/article/details/80557066