ensi/laravel-elastic-query


README

Latest Version on Packagist Tests Total Downloads

以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_onefield_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')
);

第二个属性是方向。它支持ascdesc值。默认为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);

在这种情况下返回currentnextprevious而不是totalsizeoffset。您可以在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有自己的查询日志,但您需要手动启用它。每条消息包含indexNamequerytimestamp

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以获取详细信息。

测试

  1. composer install
  2. 以您喜欢的任何方式启动Elasticsearch
  3. 如果您需要更改ELASTICSEARCH_HOSTS,请将phpunit.xml.dist复制到phpunit.xml并填写值
  4. composer test

安全漏洞

请参阅我们的安全策略了解如何报告安全漏洞。

许可

MIT许可(MIT)。请参阅许可文件以获取更多信息。