greensight / laravel-elastic-query
Installs: 11
Dependents: 1
Suggesters: 0
Security: 0
Stars: 2
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/greensight/laravel-elastic-query
Requires
- php: ^8.0
- elasticsearch/elasticsearch: ^7.13
- illuminate/support: ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.16
- mockery/mockery: ^1.4
- orchestra/testbench: ^6.0
- php-parallel-lint/php-var-dump-check: ^0.5.0
- phpunit/phpunit: ^9.0
README
Deprecated, use https://github.com/ensi-platform/laravel-telemetry instead
Working with Elasticsearch in an Eloquent-like fashion.
Installation
You can install the package via composer:
composer require greensight/laravel-elastic-query- Set
ELASTICSEARCH_HOSTSin your.envfile.,can be used as a delimeter.
Basic usage
Let's create and index class. It's someting like Eloquent model.
use Greensight\LaravelElasticQuery\ElasticIndex; class ProductsIndex extends ElasticIndex { protected string $name = 'test_products'; protected string $tiebreaker = 'product_id'; }
You should set a unique in document attribute name in $tiebreaker. It is used as an additional sort in search_after
Now we can get some documents
$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();
Filtering
$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 must have nested type.
Subqueries cannot use fields of main document only subdocument.
Sorting
$searchQuery->sortBy('field', 'desc', 'max'); // field is from main document $searchQuery->sortByNested( 'nested_field', fn(SortableQuery $subQuery) => $subQuery->where('field_in_nested', 'value')->sortBy('field') );
Second attribute is a direction. It supports asc and desc values. Defaults to asc.
Third attribute - sorting type. List of supporting types: min, max, avg, sum, median. Defaults to min.
There are also dedicated sort methods for each sort type.
$searchQuery->minSortBy('field', 'asc'); $searchQuery->maxSortBy('field', 'asc'); $searchQuery->avgSortBy('field', 'asc'); $searchQuery->sumSortBy('field', 'asc'); $searchQuery->medianSortBy('field', 'asc');
Pagination
Offset Pagination
$page = $searchQuery->paginate(15, 45);
Offset pagination returns total documents count as total and current position as size/offset.
Cursor pagination
$page = $searchQuery->cursorPaginate(10); $pageNext = $searchQuery->cursorPaginate(10, $page->next);
current, next, previous is returned in this case instead of total, size and offset.
You can check Laravel docs for more info about cursor pagination.
Aggregation
Aggregaction queries can be created like this
$aggQuery = ProductsIndex::aggregate(); /** @var \Illuminate\Support\Collection $aggs */ $aggs = $aggQuery ->where('active', true) ->terms('codes', 'code') ->nested( 'offers', fn(AggregationsBuilder $builder) => $builder->where('seller_id', 10)->minmax('price', 'price') ); $aggs
Type of $aggs->price is MinMax.
Type of $aggs->codes is BucketCollection.
Aggregate names must be unique for whole query.
Aggregate types
Get all variants of attribute values:
$aggQuery->terms('agg_name', 'field');
Get min and max attribute values. E.g for date:
$aggQuery->minmax('agg_name', 'field');
Aggregation plays nice with nested documents.
$aggQuery->nested('nested_field', function (AggregationsBuilder $builder) { $builder->terms('name', 'field_in_nested'); });
There is also a special virtual composite aggregate on the root level. You can set special conditions using it.
$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'); });
Query Log
Just like Eloquent ElasticQuery has its own query log, but you need to enable it manually
Each message contains indexName, query and timestamp
ElasticQuery::enableQueryLog(); /** @var \Illuminate\Support\Collection|Greensight\LaravelElasticQuery\Debug\QueryLogRecord[] $records */ $records = ElasticQuery::getQueryLog(); ElasticQuery::disableQueryLog();
Contributing
Please see CONTRIBUTING for details.
Testing
- composer install
- npm i
- Start Elasticsearch in your preferred way.
- Copy
phpunit.xml.disttophpunit.xmland set correct env variables there - composer test
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
License
The MIT License (MIT). Please see License File for more information.