ensi / laravel-elastic-query
laravel elastic query
Requires
- php: ^8.1
- elasticsearch/elasticsearch: ^8.0
- laravel/framework: ^9.0 || ^10.0 || ^11.0
- webmozart/assert: ^1.11
Requires (Dev)
- ensi/laravel-test-factories: ^1.0.0
- friendsofphp/php-cs-fixer: ^3.2
- orchestra/testbench: ^7.0 || ^8.0 || ^9.0
- pestphp/pest: ^1.22 || ^2.0
- pestphp/pest-plugin-laravel: ^1.1 || ^2.0
- phpstan/extension-installer: ^1.3
- phpstan/phpstan: ^1.11
- spaze/phpstan-disallowed-calls: ^2.15
- v8.x-dev
- 8.1.5
- 8.1.4
- 8.1.3
- 8.1.2
- 8.1.1
- 8.1.0
- 8.0.25
- 8.0.24
- 8.0.23
- 8.0.22
- 8.0.21
- 8.0.20
- 8.0.19
- 8.0.18
- 8.0.17
- 8.0.16
- 8.0.15
- 8.0.14
- 8.0.13
- 8.0.12
- 8.0.11
- 8.0.10
- 8.0.9
- 8.0.8
- 8.0.7
- 8.0.6
- 8.0.5
- 8.0.4
- 8.0.3
- 8.0.2
- 8.0.1
- 8.0.0
- v7.x-dev
- 7.1.4
- 7.1.3
- 7.1.2
- 7.1.1
- 7.1.0
- 7.0.23
- 7.0.22
- 7.0.21
- 7.0.20
- 7.0.19
- 7.0.18
- 7.0.17
- 7.0.16
- 7.0.15
- 7.0.14
- 7.0.13
- 7.0.12
- 7.0.11
- 7.0.10
- 7.0.9
- 7.0.8
- 7.0.7
- 7.0.6
- 7.0.5
- 7.0.4
- 7.0.3
- 7.0.2
- 7.0.1
- 7.0.0
- v0.x-dev
- 0.3.14
- 0.3.13
- 0.3.12
- 0.3.11
- 0.3.10
- 0.3.9
- 0.3.8
- 0.3.7
- 0.3.6
- 0.3.5
- 0.3.4
- 0.3.3
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.0
- 0.1.2
- 0.1.1
- 0.1.0
- dev-task-105512
This package is auto-updated.
Last update: 2024-09-09 10:24:56 UTC
README
以Eloquent类似的方式与Elasticsearch协作。
安装
您可以通过composer安装此包
composer require ensi/laravel-elastic-query
像这样发布配置文件
php artisan vendor:publish --provider="Ensi\LaravelElasticQuery\ElasticQueryServiceProvider"
在您的.env
文件中设置ELASTICSEARCH_HOSTS
。逗号可以用作分隔符。
版本兼容性
基本用法
让我们创建和索引一个类。它类似于Eloquent模型。
use Ensi\LaravelElasticQuery\ElasticIndex; class ProductsIndex extends ElasticIndex { protected string $name = 'test_products'; protected string $tiebreaker = 'product_id'; }
您应该在$tiebreaker
中设置一个文档属性的唯一名称。它用于在search_after
中作为额外的排序。
现在我们可以获取一些文档了
$searchQuery = ProductsIndex::query(); $hits = $searchQuery ->where('rating', '>=', 5) ->whereDoesntHave('offers', fn(BoolQuery $query) => $query->where('seller_id', 10)->where('active', false)) ->sortBy('rating', 'desc') ->sortByNested('offers', fn(SortableQuery $query) => $query->where('active', true)->sortBy('price', mode: 'min')) ->take(25) ->get();
过滤
$searchQuery->where('field', 'value'); $searchQuery->where('field', '>', 'value'); // supported operators: `=` `!=` `>` `<` `>=` `<=` $searchQuery->whereNot('field', 'value'); // equals `where('field', '!=', 'value')`
$searchQuery->whereIn('field', ['value1', 'value2']); $searchQuery->whereNotIn('field', ['value1', 'value2']);
$searchQuery->whereNull('field'); $searchQuery->whereNotNull('field');
$searchQuery->whereHas('nested_field', fn(BoolQuery $subQuery) => $subQuery->where('field_in_nested', 'value')); $searchQuery->whereDoesntHave( 'nested_field', function (BoolQuery $subQuery) { $subQuery->whereHas('nested_field', fn(BoolQuery $subQuery2) => $subQuery2->whereNot('field', 'value')); } );
nested_field
必须具有nested
类型。子查询不能使用主文档的字段,只能使用子文档的字段。
全文搜索
$searchQuery->whereMatch('field_one', 'query string'); $searchQuery->whereMultiMatch(['field_one^3', 'field_two'], 'query string', MatchType::MOST_FIELDS); $searchQuery->whereMultiMatch([], 'query string'); // search by all text fields
field_one
和field_two
必须是文本类型。如果没有指定类型,则使用MatchType::BEST_FIELDS
。
排序
$searchQuery->sortBy('field', SortOrder::DESC, SortMode::MAX, MissingValuesMode::FIRST); // field is from main document $searchQuery->sortByNested( 'nested_field', fn(SortableQuery $subQuery) => $subQuery->where('field_in_nested', 'value')->sortBy('field') );
第二个属性是方向。它支持asc
和desc
值。默认为asc
。
第三个属性 - 排序类型。支持的类型列表:min, max, avg, sum, median
。默认为min
。
还有针对每种排序类型的专用排序方法。
$searchQuery->minSortBy('field', 'asc'); $searchQuery->maxSortBy('field', 'asc'); $searchQuery->avgSortBy('field', 'asc'); $searchQuery->sumSortBy('field', 'asc'); $searchQuery->medianSortBy('field', 'asc');
分页
偏移量分页
$page = $searchQuery->paginate(15, 45);
偏移量分页将总文档数作为total
返回,并将当前位置作为size/offset
。
游标分页
$page = $searchQuery->cursorPaginate(10); $pageNext = $searchQuery->cursorPaginate(10, $page->next);
在这种情况下返回current
、next
和previous
而不是total
、size
和offset
。您可以在Laravel文档中了解更多关于游标分页的信息。
聚合
可以像这样创建聚合查询
$aggQuery = ProductsIndex::aggregate(); /** @var \Illuminate\Support\Collection $aggs */ $aggs = $aggQuery ->where('active', true) ->terms('codes', 'code') ->count('product_count', 'product_id') ->nested( 'offers', fn(AggregationsBuilder $builder) => $builder->where('seller_id', 10)->minmax('price', 'price') );
$aggs->price
的类型是MinMax
。$aggs->codes
的类型是BucketCollection
。聚合名称在整个查询中必须是唯一的。
聚合类型
获取属性值的所有变体
$aggQuery->terms('agg_name', 'field', 25);
获取最小和最大属性值。例如,对于日期
$aggQuery->minmax('agg_name', 'field');
获取唯一属性值的计数
$aggQuery->count('agg_name', 'field');
聚合与嵌套文档很好地协作。
$aggQuery->nested('nested_field', function (AggregationsBuilder $builder) { $builder->terms('name', 'field_in_nested'); });
还有一个在根级别的特殊虚拟composite
聚合。您可以使用它设置特殊条件。
$aggQuery->composite(function (AggregationsBuilder $builder) { $builder->where('field', 'value') ->whereHas('nested_field', fn(BoolQuery $query) => $query->where('field_in_nested', 'value2')) ->terms('field1', 'agg_name1') ->minmax('field2', 'agg_name2'); });
建议
可以像这样创建建议查询
$sugQuery = ProductsIndex::suggest(); /** @var \Illuminate\Support\Collection $suggests */ $suggests = $sugQuery->phrase('suggestName', 'name.trigram') ->text('glves') ->size(1) ->shardSize(3) ->get();
全局建议文本
用户可以设置全局文本如下
$sugQuery = ProductsIndex::suggest()->text('glves'); $sugQuery->phrase('suggestName1', 'name.trigram')->size(1)->shardSize(3); $sugQuery->phrase('suggestName2', 'name.trigram'); /** @var \Illuminate\Support\Collection $suggests */ $suggests = $sugQuery->get();
建议器类型
术语建议器
$aggQuery->term('suggestName', 'name.trigram')->text('glves')->...->get();
短语建议器
$aggQuery->phrase('suggestName', 'name.trigram')->text('glves')->...->get();
附加方法
$index = new ProductsIndex(); $index->isCreated(); // Check if index are created $index->create(); // Create index with structure from settings() method $index->bulk(); // Send bulk request $index->get(); // Send get request $index->documentDelete(); // Send documentDelete request $index->deleteByQuery(); // Send deleteByQuery request $index->termvectors(); // Send termvectors request $index->catIndices(); $index->indicesDelete(); $index->indicesRefresh(); $index->indicesReloadSearchAnalyzers();
查询日志
就像Eloquent一样,ElasticQuery有自己的查询日志,但您需要手动启用它。每条消息包含indexName
、query
和timestamp
。
ElasticQuery::enableQueryLog(); /** @var \Illuminate\Support\Collection|Ensi\LaravelElasticQuery\Debug\QueryLogRecord[] $records */ $records = ElasticQuery::getQueryLog(); ElasticQuery::disableQueryLog();
环境变量
以下是可以配置的环境变量及其默认值,主机应该是带有协议前缀和端口号后缀的主机逗号分隔字符串,例如http://localhost:9200,http://localhost:9201
ELASTICSEARCH_HOSTS=https://localhost:9200' ELASTICSEARCH_RETRIES=2 ELASTICSEARCH_USERNAME=admin ELASTICSEARCH_PASSWORD=admin ELASTICSEARCH_SSL_VERIFICATION=true,
异步使用
所有方法都可以返回一个Promise
。
要启用此功能,您需要将http_async_client
添加到您的配置中,然后执行ElasticSearch::getClient()->setAsync(true).
要禁用: ElasticQuery::getClient()->setAsync(false)
。
例如
laravel-elastic-query.php
return [ 'connection' => [ // .. 'http_async_client' => [HttpClientOptionsBuilder::class, 'getAsyncClient'], ], ];
HttpClientOptionsBuilder
use Http\Adapter\Guzzle7\Client as GuzzleAdapter; use Http\Client\HttpAsyncClient; class HttpClientOptionsBuilder { public static function getAsyncClient(): HttpAsyncClient { return GuzzleAdapter::createWithConfig([]); } }
操作
use Ensi\LaravelElasticQuery\ElasticQuery; ElasticQuery::getClient()->setAsync(true); // With async $promises = [ 'key1' => FirstIndex::query()->get(), 'key2' => FirstIndex::suggest()->paginate(/* ... */), ]; $results = []; foreach ($promises as $key => $promise) { $results[$key] = $promise->wait(); } $firstResponse = $results['key1']; ElasticQuery::getClient()->setAsync(false); // Without async $firstResponse = FirstIndex::query()->get()
Elasticsearch 7和8支持。
由于Elasticsearch 7和8客户端的不兼容性,将为这些版本创建单独的版本。每个版本的开发都在相应的分支上进行。
要对版本7进行修改,您需要基于v7创建一个任务分支,并向其发起拉取请求。对于版本8,操作类似,但基于v8分支。
贡献
请参阅CONTRIBUTING以获取详细信息。
测试
- composer install
- 以您喜欢的任何方式启动Elasticsearch
- 如果您需要更改
ELASTICSEARCH_HOSTS
,请将phpunit.xml.dist
复制到phpunit.xml
并填写值 - composer test
安全漏洞
请参阅我们的安全策略了解如何报告安全漏洞。
许可
MIT许可(MIT)。请参阅许可文件以获取更多信息。