ensi/laravel-elastic-query-specification

8.1.0 2024-06-26 12:49 UTC

This package is auto-updated.

Last update: 2024-08-27 08:44:24 UTC


README

Latest Version on Packagist Tests Total Downloads

用于描述查询的声明式方法的 ensi/laravel-elastic-query 扩展。

安装

  1. 安装 ensi/laravel-elastic-query https://github.com/ensi-platform/laravel-elastic-query#installation
  2. 通过 composer 安装此包
composer require ensi/laravel-elastic-query-specification

版本兼容性

基本用法

所有声明式查询类型均基于规范。它包含可用过滤器、排序和聚合的定义。

use Ensi\LaravelElasticQuerySpecification\Agregating\AllowedAggregate;
use Ensi\LaravelElasticQuerySpecification\Filtering\AllowedFilter;
use Ensi\LaravelElasticQuerySpecification\Sorting\AllowedSort;
use Ensi\LaravelElasticQuerySpecification\Specification\CompositeSpecification;
use Ensi\LaravelElasticQuerySpecification\Specification\Specification;

class ProductSpecification extends CompositeSpecification
{
    public function __construct()
    {
        parent::__construct();
        
        $this->allowedFilters([
            'package',
            'active',
            AllowedFilter::exact('cashback', 'cashback.active')->default(true)
        ]);
        
        $this->allowedSorts(['name', 'rating']);
        
        $this->allowedAggregates([
            'package',
            AllowedAggregate::minmax('rating')
        ]);
        
        $this->allowedFacets([
            'package'
        ]);
        
        $this->whereNotNull('package');
        
        $this->nested('offers', function (Specification $spec) {
            $spec->allowedFilters(['seller_id', 'active']);
            
            $spec->allowedAggregates([
                'seller_id',
                AllowedAggregate::minmax('price')
            ]);
            
            $spec->allowedSorts([
                AllowedSort::field('price')->byMin()
            ]);
            
            $spec->allowedFacets([
                AllowedFacet::terms('seller_id')
            ]);
        });
    }
}

以下是该规范的查询示例。

{
 "sort": ["+price", "-rating"],
 "filter": {
    "active": true,
    "seller_id": 10
 }
}
{
 "aggregate": ["price", "rating"],
 "filter": {
    "package": "bottle",
    "seller_id": 10
 }
}
{
  "facet": ["seller_id", "package"],
  "filter": {
    "package": "bottle",
    "seller_id": [10, 20, 50, 90]
  }
}

使用 nested 方法为嵌套文档添加规范。过滤器、聚合和排序的名称从它们导出到全局作用域,不添加任何前缀。对于过滤器可以使用相同的名称,但不能用于其他组件。

$this->nested('nested_field', function (Specification $spec) { ... })
$this->nested('nested_field', new SomeSpecificationImpl());

在嵌套文档的规范中,只能使用这些文档的字段。

对于相同的 nested 字段添加多个规范是可接受的。

where* 约束允许您设置客户端无法更改的额外程序选择条件。根规范中指定的约束始终应用。嵌套规范中的约束仅用作查询中添加的过滤器、聚合或排序的附加。

allowedFilters 方法确定客户端可用的过滤器。每个过滤器必须在规范中具有唯一名称。同时,在根和嵌套规范或不同的嵌套规范中,名称可能重复。所有具有相同名称的过滤器将使用查询参数中的一个值进行填充。

除了过滤器本身的名称外,您还可以单独指定应用于索引中字段的名称和默认值。

$this->allowedFilters([AllowedFilter::exact('name', 'field')->default(500)]);

// the following statements are equivalent
$this->allowedFilters(['name']);
$this->allowedFilters([AllowedFilter::exact('name', 'name')]);

过滤器类型

AllowedFilter::exact('name', 'field');          // The field value is checked for equality to one of the specified
AllowedFilter::exists('name', 'field');         // There is a check that the field is in the document and has a non-zero value.
AllowedFilter::greater('name', 'field');        // The field value must be greater than the specified one.
AllowedFilter::greaterOrEqual('name', 'field'); // The field value must be greater than or equal to the specified one.
AllowedFilter::less('name', 'field');           // The field value must be less than the specified one.
AllowedFilter::lessOrEqual('name', 'field');    // The field value must be less than or equal to the specified one.
AllowedFilter::match('name', 'field');          // Full text search in the field
AllowedFilter::multiMatch('name', ['field1^3', 'field2']);    // Full text search in the fields

客户端可用的排序是通过 allowedSorts 方法添加的。排序方向由其名称设置。符号 + 或无符号对应于升序,- 对应于降序。默认情况下,使用最小选择进行升序排序,如果字段中有多个值。

$this->allowedSorts([AllowedSort::field('name', 'field')]);

// the following statements are equivalent
$this->allowedSorts(['name']);
$this->allowedSorts([AllowedSort::field('+name', 'name')]);
$this->allowedSorts([AllowedSort::field('+name', 'name')->byMin()]);

// set the sorting mode
$this->allowedSorts([AllowedSort::field('name', 'field')->byMin()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->byMax()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->byAvg()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->bySum()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->byMedian()]);

从嵌套规范进行排序时,将考虑同一规范中的所有约束和活动过滤器。

使用 allowedAggregates 方法声明聚合。客户端在查询参数中指定预期在响应中接收的聚合名称列表。

$this->allowedAggregates([AllowedAggregate::terms('name', 'field')]);

// the following statements are equivalent
$this->allowedAggregates(['name']);
$this->allowedAggregates([AllowedAggregate::terms('name', 'name')]);

聚合类型

AllowedAggregate::terms('name', 'field');   // Get all variants of attribute values
AllowedAggregate::minmax('name', 'field');  // Get min and max attribute values

嵌套规范中的聚合将带有所有约束和活动过滤器添加到 Elasticsearch 查询中。

您可以使用 allowedFacets 方法定义分面。每个分面需要一个聚合和一个或多个过滤器。您可以使用现有的聚合

AllowedFacet::fromAggregate('name', 'filter');

和由分面本身创建的聚合

AllowedFacet::terms('name', 'filter');
AllowedFacet::minmax('name', ['filter1', 'filter2']);

单独在规范中注册过滤器。只有它们的名称传递给分面创建方法。

在计算每个分面可用的值时,除了与该分面关联的过滤器外,所有设置的过滤器都将应用。

搜索文档

use Ensi\LaravelElasticQuerySpecification\SearchQueryBuilder;
use Ensi\LaravelElasticQuerySpecification\QueryBuilderRequest;

class ProductsSearchQuery extends SearchQueryBuilder
{
    public function __construct(QueryBuilderRequest $request)
    {
        parent::__construct(ProductsIndex::query(), new ProductSpecification(), $request);
    }
}
class ProductsController
{
    // ...
    public function index(ProductsSearchQuery $query)
    {
        return ProductResource::collection($query->get());
    }
}

计算汇总指标

use Ensi\LaravelElasticQuerySpecification\AggregateQueryBuilder;
use Ensi\LaravelElasticQuerySpecification\QueryBuilderRequest;

class ProductsAggregateQuery extends AggregateQueryBuilder
{
    public function __construct(QueryBuilderRequest $request)
    {
        parent::__construct(ProductsIndex::aggregate(), new ProductSpecification(), $request);
    }
}
class ProductsController
{
    // ...
    public function totals(ProductsAggregateQuery $query)
    {
        return new ProductAggregateResource($query->get());
    }
}

确定可用的分面值

use Ensi\LaravelElasticQuerySpecification\FacetQueryBuilder;
use Ensi\LaravelElasticQuerySpecification\QueryBuilderRequest;

class ProductsFacetsQuery extends FacetQueryBuilder
{
    public function __construct(QueryBuilderRequest $request)
    {
        parent::__construct(ProductsIndex::aggregate(), new ProductSpecification(), $request);
    }
}
class ProductsController
{
    // ...
    public function facets(ProductsFacetsQuery $query)
    {
        return new ProductFacetsResource($query->get());
    }
}

Elasticsearch 7 和 8 支持。

由于 Elasticsearch 7 和 8 的客户端不兼容,将为这些版本创建单独的发布版本。每个版本的开发都在相应的分支上进行。

要修改 7 版本,您需要基于 v7 创建一个任务分支,并向它提交一个 pull request。对于 8 版本,类似,但基于 v8 分支。

贡献

有关详细信息,请参阅 CONTRIBUTING

测试

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

安全漏洞

请查看我们如何报告安全漏洞的 安全策略

许可

MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件