hocvt / laravel-scout-elasticsearch
使用 ElasticSearch 和 Laravel Scout 在多个模型中进行搜索
Requires
- php: ^8.0.12|^8.1
- elasticsearch/elasticsearch: ^8.0
- handcraftedinthealps/elasticsearch-dsl: ^8.0
- laravel/scout: ^8.0|^9.0|^10.0
- roave/better-reflection: ^4.3|^5.0|^6.18
Requires (Dev)
- laravel/legacy-factories: ^1.0
- nunomaduro/larastan: ^2.4
- orchestra/testbench: ^6.17|^7.0|^8.0
- php-http/guzzle7-adapter: ^1.0
- phpunit/phpunit: ^9.4.0
README
对于 Laravel 框架 < 6.0.0,请使用 3.x 分支
该包为将 ElasticSearch 集成到 Laravel 应用程序提供了完美的起点。它经过精心设计,简化了在 Laravel 框架 中使用 ElasticSearch。
它建立在最新版本的 Laravel Scout 之上,这是官方的 Laravel 搜索包。使用此包,您可以充分利用 Laravel Scout 的所有优秀功能,同时利用 ElasticSearch 的完整搜索体验。
如果您需要任何帮助,请通过 stack overflow 提出支持问题,这是首选和推荐的方式。
💕 功能
如果喜欢这个包,不要忘了 ⭐ 它。🙏
- Laravel Scout 10.x 支持
- Laravel Nova 支持
- 在多个模型中搜索
- 零停机时间 重新导入 - 在生产环境中导入数据变得非常简单。
- 预加载关系 - 加速导入。
- 一次性导入所有可搜索的模型。
- 为每个模型提供完全可配置的映射。
- 在查询中充分利用 ElasticSearch 的全部功能。
⚠️ 要求
- PHP 版本 >= 8.0
- Laravel 框架版本 >= 8.0.0
🚀 安装
使用 composer 安装包
composer require matchish/laravel-scout-elasticsearch
设置环境变量
SCOUT_DRIVER=Matchish\ScoutElasticSearch\Engines\ElasticSearchEngine
该包使用官方包中的 \ElasticSearch\Client
,但不会尝试配置它,因此您可以在应用程序服务提供者中自由配置。但如果您现在不想这样做,可以使用包中的 Matchish\ElasticSearchServiceProvider
。
注册提供者,添加到 config/app.php
'providers' => [ // Other Service Providers \Matchish\ScoutElasticSearch\ElasticSearchServiceProvider::class ],
设置 ELASTICSEARCH_HOST
环境变量
ELASTICSEARCH_HOST=host:port
或使用逗号作为分隔符以分隔其他节点
ELASTICSEARCH_HOST=host:port,host:port
并发布 elasticsearch 的配置示例
php artisan vendor:publish --tag config
💡 使用方法
注意: 此包为 Laravel Scout 添加了功能,因此我们鼓励您首先 阅读 Scout 文档。Scout 的文档可以在 Laravel 网站 上找到。
索引 设置 和 映射
在创建索引时定义映射非常重要——不适当的初步定义和映射可能会导致错误的搜索结果。
要定义索引的映射或设置,请使用正确的值设置配置。
例如,如果方法 searchableAs
返回字符串 products
映射配置的键应为
elasticsearch.indices.mappings.products
或者,您可以使用配置键 elasticsearch.indices.mappings.default
来指定默认映射。
同样的方法,您也可以定义设置。
对于 products
索引,它将是
elasticsearch.indices.settings.products
而对于默认设置
elasticsearch.indices.settings.default
主动加载
为了加快导入速度,您可以在导入时使用全局作用域主动加载关系。
您应该在服务提供者中配置 ImportSourceFactory
(在 register
方法中)
use Matchish\ScoutElasticSearch\Searchable\ImportSourceFactory; ... public function register(): void { $this->app->bind(ImportSourceFactory::class, MyImportSourceFactory::class);
下面是 MyImportSourceFactory
的一个例子
namespace Matchish\ScoutElasticSearch\Searchable; final class MyImportSourceFactory implements ImportSourceFactory { public static function from(string $className): ImportSource { //Add all required scopes return new DefaultImportSource($className, [new WithCommentsScope()]); } } class WithCommentsScope implements Scope { /** * Apply the scope to a given Eloquent query builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function apply(Builder $builder, Model $model) { $builder->with('comments'); } }
当您通过利用 Laravel Scout 提供的 toSearchableArray
方法来保存模型时,您还可以自定义索引数据
示例
class Product extends Model { use Searchable; /** * Get the indexable data array for the model. * * @return array */ public function toSearchableArray() { $with = [ 'categories', ]; $this->loadMissing($with); return $this->toArray(); } }
此示例将确保在保存模型时始终加载类别关系。
零停机重新导入
在生产环境中工作时,为了在重新导入数据的同时保持现有的搜索体验,您还可以使用 scout:import2
Artisan 命令
php artisan scout:import2
该命令会创建一个新的临时索引,将所有模型导入其中,然后切换到该索引并删除旧索引。
搜索
为了与原始 scout 包完全兼容,此包不添加任何新方法。
那么我们如何构建复杂的查询呢?有两种方式。
默认情况下,当您将查询传递给 search
方法时,引擎会构建一个 query_string 查询,因此您可以构建如下查询
Product::search('(title:this OR description:this) AND (title:that OR description:that)')
如果这不足以满足您的需求,您可以将回调函数传递给查询构建器
$results = Product::search('zonga', function(\Elastic\Elasticsearch\Client $client, $body) { $minPriceAggregation = new MinAggregation('min_price'); $minPriceAggregation->setField('price'); $maxPriceAggregation = new MaxAggregation('max_price'); $maxPriceAggregation->setField('price'); $brandTermAggregation = new TermsAggregation('brand'); $brandTermAggregation->setField('brand'); $body->addAggregation($minPriceAggregation); $body->addAggregation($brandTermAggregation); return $client->search(['index' => 'products', 'body' => $body->toArray()])->asArray(); })->raw();
注意:回调函数将获得两个参数。第一个是
$client
,它是来自 elasticsearch/elasticsearch 包的\Elastic\Elasticsearch\Client
类的对象。第二个是$body
,它是来自 ongr/elasticsearch-dsl 包的\ONGR\ElasticsearchDSL\Search
类的对象。所以,如您在上述示例中看到的那样,$client->search(....)
方法将返回一个\Elastic\Elasticsearch\Response\Elasticsearch
对象。您需要使用asArray()
方法来获取数组结果。否则,HitsIteratorAggregate
类将引发错误。您可以在此处检查问题 here。
条件
Scout 仅支持 3 个条件:->where(column, value)
(严格相等),->whereIn(column, array)
和 ->whereNotIn(column, array)
Product::search('(title:this OR description:this) AND (title:that OR description:that)') ->where('price', 100) ->whereIn('type', ['used', 'like new']) ->whereNotIn('type', ['new', 'refurbished']);
Scout 不支持任何运算符,但您可以将 ElasticSearch 术语(如 RangeQuery
)作为值传递给 ->where()
use ONGR\ElasticsearchDSL\Query\TermLevel\RangeQuery; Product::search('(title:this OR description:this) AND (title:that OR description:that)') ->where('price', new RangeQuery('price', [ RangeQuery::GTE => 100, RangeQuery::LTE => 1000, ]);
如果您只想使用 RangeQuery 进行搜索而不使用任何 query_string,可以直接调用 search() 方法并留空参数。
use ONGR\ElasticsearchDSL\Query\TermLevel\RangeQuery; Product::search() ->where('price', new RangeQuery('price', [ RangeQuery::GTE => 100, ]);
ElasticSearch 术语的完整列表在 vendor/handcraftedinthealps/elasticsearch-dsl/src/Query/TermLevel
中。
在多个模型中搜索
您可以使用 MixedSearch
类来完成此操作,只需将索引名称用逗号分隔传递给 within
方法。
MixedSearch::search('title:Barcelona or to:Barcelona') within(implode(',', [ (new Ticket())->searchableAs(), (new Book())->searchableAs(), ])) ->get();
在此示例中,您将获得一个包含 Ticket
和 Book
模型的集合,其中 ticket 的到达城市或 book 的标题为 Barcelona
处理结果
通常您的响应不是模型集合,而是聚合或带有高亮的模型等。在这种情况下,您需要实现自己的 HitsIteratorAggregate
实现并将其绑定到您的服务提供者。
🆓 许可证
Scout ElasticSearch 是一个开源软件,遵循 MIT 许可协议。