From 0608997cb149a03c6b70e3121be308e1aa087f66 Mon Sep 17 00:00:00 2001
From: Pavel Piligrimov
Date: Fri, 13 Jan 2023 11:29:40 +0500
Subject: [PATCH] updates for iqdev/search-dc
---
.../Config/BaseConfiguration.php | 7 +-
src/ElasticSearch/Config/MappingValidator.php | 23 +
src/ElasticSearch/Config/product.mappings.php | 33 +-
src/ElasticSearch/Config/product.settings.php | 64 +++
src/ElasticSearch/Configuration.php | 4 +-
.../Converter/CriteriaToEsRequest.php | 398 ++++++++++++++++++
.../Converter/EsResponseToResult.php | 156 +++++++
src/ElasticSearch/Document/Document.php | 2 +-
.../Document/ProductDocument.php | 74 +++-
.../Domain/SearchResultFactory.php | 124 ------
src/ElasticSearch/Domain/SearchService.php | 183 --------
src/ElasticSearch/Facet/Facet.php | 2 +-
src/ElasticSearch/Facet/FacetCategory.php | 8 +-
src/ElasticSearch/Facet/FacetCollection.php | 2 +-
src/ElasticSearch/Facet/FacetKeyword.php | 15 +-
src/ElasticSearch/Facet/FacetNumber.php | 17 +-
src/ElasticSearch/Helper/ArrayHelper.php | 30 ++
src/ElasticSearch/Indexer/AddIndex.php | 37 ++
.../Indexer/BaseIndexProvider.php | 31 +-
src/ElasticSearch/Indexer/BulkIndex.php | 41 ++
src/ElasticSearch/Indexer/DeleteIndex.php | 31 ++
.../Indexer/EsHelperEndpoint.php | 180 ++++++++
src/ElasticSearch/Indexer/Index.php | 30 +-
src/ElasticSearch/Indexer/IndexProvider.php | 39 +-
src/ElasticSearch/Indexer/IndexRunner.php | 82 +++-
src/ElasticSearch/Indexer/UpdateIndex.php | 39 ++
src/ElasticSearch/Order/Order.php | 24 ++
src/ElasticSearch/Order/OrderAscType.php | 8 +
src/ElasticSearch/Order/OrderCollection.php | 26 ++
src/ElasticSearch/Order/OrderDescType.php | 8 +
src/ElasticSearch/Order/OrderField.php | 7 +
.../Order/OrderKeywordProperty.php | 29 ++
.../Order/OrderNumberProperty.php | 29 ++
src/ElasticSearch/Order/OrderType.php | 13 +
src/ElasticSearch/Search/Aggs/Aggs.php | 48 +--
.../Search/Aggs/AggsCollection.php | 18 +-
.../Search/Aggs/AggsFacetStats.php | 31 ++
...ggsKeyWordFacet.php => AggsFacetTerms.php} | 3 +-
.../Search/Aggs/AggsNumberFacet.php | 31 --
.../Search/Aggs/BoolQueryCollection.php | 19 -
.../Aggs/{ExtremumTerms.php => Stats.php} | 8 +-
src/ElasticSearch/Search/Aggs/Terms.php | 9 +-
.../Search/BoolQuery/BoolQueryCollection.php | 4 +-
.../Search/BoolQuery/FilterKeywordFacet.php | 7 +-
.../Search/BoolQuery/FilterNumberFacet.php | 40 +-
src/ElasticSearch/Search/BoolQuery/Query.php | 39 +-
.../Search/BoolQuery/RangeTerms.php | 38 --
src/ElasticSearch/Search/BoolQuery/Stats.php | 34 ++
src/ElasticSearch/Search/BoolQuery/Terms.php | 12 +-
src/ElasticSearch/Search/Nested.php | 9 +-
src/ElasticSearch/Search/Pagination.php | 4 +-
src/ElasticSearch/Search/Request.php | 78 ++--
src/ElasticSearch/Search/Sorting.php | 28 --
src/ElasticSearch/Search/Sorting/Sorting.php | 10 -
.../Search/Sorting/SortingCollection.php | 20 -
.../Search/Sorting/SortingField.php | 18 -
.../Search/Sorting/SortingFieldsPair.php | 8 -
.../Search/Sorting/SortingPair.php | 20 -
.../Sorting/SortingPropertyKeywordPair.php | 27 --
.../Sorting/SortingPropertyNumberPair.php | 27 --
src/ElasticSearch/SearchService.php | 44 ++
61 files changed, 1641 insertions(+), 789 deletions(-)
create mode 100644 src/ElasticSearch/Config/MappingValidator.php
create mode 100644 src/ElasticSearch/Config/product.settings.php
create mode 100644 src/ElasticSearch/Converter/CriteriaToEsRequest.php
create mode 100644 src/ElasticSearch/Converter/EsResponseToResult.php
delete mode 100644 src/ElasticSearch/Domain/SearchResultFactory.php
delete mode 100644 src/ElasticSearch/Domain/SearchService.php
create mode 100644 src/ElasticSearch/Helper/ArrayHelper.php
create mode 100644 src/ElasticSearch/Indexer/AddIndex.php
create mode 100644 src/ElasticSearch/Indexer/BulkIndex.php
create mode 100644 src/ElasticSearch/Indexer/DeleteIndex.php
create mode 100644 src/ElasticSearch/Indexer/EsHelperEndpoint.php
create mode 100644 src/ElasticSearch/Indexer/UpdateIndex.php
create mode 100644 src/ElasticSearch/Order/Order.php
create mode 100644 src/ElasticSearch/Order/OrderAscType.php
create mode 100644 src/ElasticSearch/Order/OrderCollection.php
create mode 100644 src/ElasticSearch/Order/OrderDescType.php
create mode 100644 src/ElasticSearch/Order/OrderField.php
create mode 100644 src/ElasticSearch/Order/OrderKeywordProperty.php
create mode 100644 src/ElasticSearch/Order/OrderNumberProperty.php
create mode 100644 src/ElasticSearch/Order/OrderType.php
create mode 100644 src/ElasticSearch/Search/Aggs/AggsFacetStats.php
rename src/ElasticSearch/Search/Aggs/{AggsKeyWordFacet.php => AggsFacetTerms.php} (96%)
delete mode 100644 src/ElasticSearch/Search/Aggs/AggsNumberFacet.php
delete mode 100644 src/ElasticSearch/Search/Aggs/BoolQueryCollection.php
rename src/ElasticSearch/Search/Aggs/{ExtremumTerms.php => Stats.php} (79%)
delete mode 100644 src/ElasticSearch/Search/BoolQuery/RangeTerms.php
create mode 100644 src/ElasticSearch/Search/BoolQuery/Stats.php
delete mode 100644 src/ElasticSearch/Search/Sorting.php
delete mode 100644 src/ElasticSearch/Search/Sorting/Sorting.php
delete mode 100644 src/ElasticSearch/Search/Sorting/SortingCollection.php
delete mode 100644 src/ElasticSearch/Search/Sorting/SortingField.php
delete mode 100644 src/ElasticSearch/Search/Sorting/SortingFieldsPair.php
delete mode 100644 src/ElasticSearch/Search/Sorting/SortingPair.php
delete mode 100644 src/ElasticSearch/Search/Sorting/SortingPropertyKeywordPair.php
delete mode 100644 src/ElasticSearch/Search/Sorting/SortingPropertyNumberPair.php
create mode 100644 src/ElasticSearch/SearchService.php
diff --git a/src/ElasticSearch/Config/BaseConfiguration.php b/src/ElasticSearch/Config/BaseConfiguration.php
index 3ed3ea6..73e561c 100644
--- a/src/ElasticSearch/Config/BaseConfiguration.php
+++ b/src/ElasticSearch/Config/BaseConfiguration.php
@@ -15,4 +15,9 @@ class BaseConfiguration implements Configuration
{
return include __DIR__.'/product.mappings.php';
}
-}
\ No newline at end of file
+
+ public function getSettings(): array
+ {
+ return include __DIR__.'/product.settings.php';
+ }
+}
diff --git a/src/ElasticSearch/Config/MappingValidator.php b/src/ElasticSearch/Config/MappingValidator.php
new file mode 100644
index 0000000..e8d6b30
--- /dev/null
+++ b/src/ElasticSearch/Config/MappingValidator.php
@@ -0,0 +1,23 @@
+getMapping()['properties'] ?? []);
+
+ return in_array($property, $properties, true);
+ }
+}
diff --git a/src/ElasticSearch/Config/product.mappings.php b/src/ElasticSearch/Config/product.mappings.php
index 2d73e84..bd51078 100644
--- a/src/ElasticSearch/Config/product.mappings.php
+++ b/src/ElasticSearch/Config/product.mappings.php
@@ -9,8 +9,21 @@ return [
'full_search_content' => [
'type' => 'text',
],
+ 'suggest_search_content' => [
+ 'type' => 'text',
+ 'analyzer' => 'autocomplete',
+ 'search_analyzer' => 'standard',
+ ],
'category_id' => [
- 'type' => 'keyword',
+ 'type' => 'keyword',
+ 'index' => false,
+ ],
+ 'rating' => [
+ 'type' => 'double',
+ 'index' => false,
+ ],
+ 'popular' => [
+ 'type' => 'double',
'index' => false,
],
'search_data' => [
@@ -20,28 +33,28 @@ return [
'type' => 'nested',
'properties' => [
'facet_code' => [
- 'type' => 'keyword',
- 'index' => true
+ 'type' => 'keyword',
+ 'index' => true
],
'facet_value' => [
- 'type' => 'keyword',
- 'index' => true
- ],
+ 'type' => 'keyword',
+ 'index' => true
+ ]
]
],
'number_facet' => [
'type' => 'nested',
'properties' => [
'facet_code' => [
- 'type' => 'keyword',
- 'index' => true
+ 'type' => 'keyword',
+ 'index' => true
],
'facet_value' => [
- 'type' => 'double'
+ 'type' => 'double'
]
]
]
]
]
],
-];
\ No newline at end of file
+];
diff --git a/src/ElasticSearch/Config/product.settings.php b/src/ElasticSearch/Config/product.settings.php
new file mode 100644
index 0000000..6d176f5
--- /dev/null
+++ b/src/ElasticSearch/Config/product.settings.php
@@ -0,0 +1,64 @@
+ [
+ 'analysis' => [
+ 'char_filter' => [
+ 'eCharFilter' => [
+ 'type' => 'mapping',
+ 'mappings' => [
+ 'Ё=>Е',
+ 'ё=>е',
+ ',=>.',
+ ]
+ ]
+ ],
+ 'analyzer' => [
+ 'search_analyzer' => [
+ 'type' => 'custom',
+ 'tokenizer' => 'standard',
+ 'filter' => [
+ 'lowercase',
+ 'russian_stemmer',
+ 'russian_stop',
+ 'synonym_filter',
+ 'shingle',
+ ],
+ 'char_filter' => ['eCharFilter']
+ ],
+ 'autocomplete' => [
+ 'type' => 'custom',
+ 'tokenizer' => 'standard',
+ 'filter' => [
+ 'lowercase',
+ 'autocomplete_filter'
+ ]
+ ]
+ ],
+ 'filter' => [
+ 'shingle' => [
+ 'type' => 'shingle',
+ 'min_shingle_size' => 2,
+ 'max_shingle_size' => 3
+ ],
+ 'russian_stop' => [
+ 'type' => 'stop',
+ ],
+ 'russian_stemmer' => [
+ 'type' => 'stemmer',
+ 'language' => 'russian'
+ ],
+ 'synonym_filter' => [
+ 'type' => 'synonym_graph',
+ 'expand' => false,
+ 'synonyms' => []
+ ],
+ 'autocomplete_filter' => [
+ 'type' => 'edge_ngram',
+ 'min_gram' => 1,
+ 'max_gram' => 10,
+ ]
+ ]
+ ],
+ ]
+];
\ No newline at end of file
diff --git a/src/ElasticSearch/Configuration.php b/src/ElasticSearch/Configuration.php
index ef48509..da7c7d2 100644
--- a/src/ElasticSearch/Configuration.php
+++ b/src/ElasticSearch/Configuration.php
@@ -7,4 +7,6 @@ interface Configuration
public function getIndexName(): string;
public function getMapping(): array;
-}
\ No newline at end of file
+
+ public function getSettings(): array;
+}
diff --git a/src/ElasticSearch/Converter/CriteriaToEsRequest.php b/src/ElasticSearch/Converter/CriteriaToEsRequest.php
new file mode 100644
index 0000000..2c8e274
--- /dev/null
+++ b/src/ElasticSearch/Converter/CriteriaToEsRequest.php
@@ -0,0 +1,398 @@
+pagination($request, $criteria);
+ $request = $this->order($request, $criteria);
+ $request = $this->filter($request, $criteria);
+ $request = $this->aggs($request, $criteria);
+
+ return $request;
+ }
+
+ private function pagination(Request $request, Criteria $criteria): Request
+ {
+ $request = clone $request;
+
+ $request->setPagination(new Pagination($criteria->pagination()->limit, $criteria->pagination()->offset));
+
+ return $request;
+ }
+
+ private function order(Request $request, Criteria $criteria): Request
+ {
+ $request = clone $request;
+
+ if (true === $criteria->sorting()->isEmpty()) {
+ return $request;
+ }
+
+ foreach ($criteria->sorting() as $order) {
+ /** @var Order $order */
+ $direction = null;
+ if ($order->orderType() instanceof SOrderAscType) {
+ $direction = new OrderAscType();
+ }
+
+ if ($order->orderType() instanceof SOrderDescType) {
+ $direction = new OrderDescType();
+ }
+
+ if ($order->orderBy() instanceof AttrType) {
+ $request->getSort()->add(new OrderField($order->orderBy()->key(), $direction));
+ } elseif ($order->orderBy() instanceof PropertyType) {
+ if ($order->orderBy()->type() === PropertyType::TYPE_KEYWORD) {
+ $request->getSort()->add(new OrderKeywordProperty($order->orderBy()->key(), $direction));
+ } elseif ($order->orderBy()->type() === PropertyType::TYPE_NUMBER) {
+ $request->getSort()->add(new OrderNumberProperty($order->orderBy()->key(), $direction));
+ }
+ }
+ }
+
+ return $request;
+ }
+
+ private function filter(Request $request, Criteria $criteria): Request
+ {
+ $request = clone $request;
+ if ($criteria->filters()->isEmpty()) {
+ return $request;
+ }
+
+ foreach ($criteria->filters() as $filter) {
+ /** @var Filter $filter */
+ $value = $filter->value()->value();
+ $field = $filter->field()->value();
+
+ if ('search' === $field) {
+ if ($filter->operator()->value() === FilterOperator::CONTAINS) {
+ $request->addMatch(
+ 'suggest_search_content',
+ [
+ 'query' => $value,
+ ],
+ );
+ } else {
+ $request->addMatch(
+ 'full_search_content',
+ [
+ 'query' => $value,
+ ],
+ );
+ }
+ continue;
+ }
+
+ if ('category_id' === $field) {
+ $request->getQuery()->must(
+ new Terms('category_id', $filter->value()->value())
+ );
+ continue;
+ }
+ }
+
+ $keywordFilter = $this->getKeywordFilter($criteria);
+ if (false === $keywordFilter->isEmpty()) {
+ $keywordNestedFilter = new Nested();
+ $keywordNestedFilter->setPath('search_data')
+ ->setQuery($keywordFilter);
+
+ $request->getPostFilter()->filter($keywordNestedFilter);
+ }
+
+ $numberFilter = $this->getNumberFilter($criteria);
+ if (false === $numberFilter->isEmpty()) {
+ $numberNestedFilter = new Nested();
+ $numberNestedFilter->setPath('search_data')
+ ->setQuery($numberFilter);
+
+ $request->getPostFilter()->filter($numberNestedFilter);
+ }
+
+ return $request;
+ }
+
+ private function getNumberFilter(Criteria $criteria, array $excludeFilter = []): Query
+ {
+ $numberFilter = new Query();
+
+ $ranges = [];
+
+ foreach ($criteria->filters() as $filter) {
+ /** @var Filter $filter */
+ $value = $filter->value()->value();
+ $field = $filter->field()->value();
+
+ if (in_array($field, $excludeFilter, true)) {
+ continue;
+ }
+ if (in_array($filter->operator()->value(), [FilterOperator::LT, FilterOperator::LTE], true)) {
+ $ranges[$field][$filter->operator()->value()] = $value;
+ continue;
+ }
+
+ if (in_array($filter->operator()->value(), [FilterOperator::GT, FilterOperator::GTE], true)) {
+ $ranges[$field][$filter->operator()->value()] = $value;
+ }
+ }
+
+ if (false === empty($ranges)) {
+ foreach ($ranges as $field => $range) {
+ $numberFilter->filter(
+ new FilterNumberFacet(
+ $field,
+ $range
+ )
+ );
+ }
+ }
+
+ return $numberFilter;
+ }
+
+ private function getKeywordFilter(Criteria $criteria, array $excludeFilter = []): Query
+ {
+ $keywordFilter = new Query();
+ foreach ($criteria->filters() as $filter) {
+ /** @var Filter $filter */
+ $value = $filter->value()->value();
+ $field = $filter->field()->value();
+
+ if (in_array($field, $excludeFilter, true)) {
+ continue;
+ }
+
+ if (in_array($filter->operator()->value(), [FilterOperator::LT, FilterOperator::LTE], true)) {
+ continue;
+ }
+
+ if (in_array($filter->operator()->value(), [FilterOperator::GT, FilterOperator::GTE], true)) {
+ continue;
+ }
+
+ if ('search' === $field) {
+ continue;
+ }
+
+ if ('category_id' === $field) {
+ continue;
+ }
+
+ if (is_array($value)) {
+ $value = array_map(static fn($v) => (string)$v, $value);
+ } else {
+ $value = (string)$value;
+ }
+
+ $keywordFilter->filter(new FilterKeywordFacet($field, $value));
+ }
+
+ return $keywordFilter;
+ }
+
+ private function aggs(Request $request, Criteria $criteria): Request
+ {
+ $request = clone $request;
+
+ if ($criteria->filters()->isEmpty()) {
+ return $request;
+ }
+
+ $request->getAggs()->add(
+ AggsFacetTerms::create(
+ 'keyword_facet',
+ 'keyword_facet'
+ )
+ );
+
+ $request->getAggs()->add(
+ AggsFacetStats::create(
+ 'number_facet',
+ 'number_facet'
+ )
+ );
+
+ $getKey = static fn(string $type, string $name): string => sprintf('%s_facet_%s', $type, $name);
+ foreach ($criteria->filters() as $filter) {
+ /** @var Filter $filter */
+ $field = $filter->field()->value();
+
+ if (in_array(
+ $filter->operator()->value(),
+ [
+ FilterOperator::LT,
+ FilterOperator::LTE,
+ FilterOperator::GT,
+ FilterOperator::GTE,
+ ],
+ true
+ )
+ ) {
+ $aggsFiltered = new Aggs($getKey('number', $field));
+ $aggsFiltered->addAggs(
+ AggsFacetStats::create(
+ 'agg_special',
+ 'number_facet'
+ )
+ );
+
+ $queryNumberFiltered = new Query();
+ $keywordFilter = $this->getKeywordFilter($criteria);
+ $numberFilter = $this->getNumberFilter($criteria, [$field]);
+
+ if (false === $keywordFilter->isEmpty()) {
+ $nestedFilterKeyword = new Nested();
+ $nestedFilterKeyword->setPath('search_data')
+ ->setQuery($keywordFilter);
+ $queryNumberFiltered->filter($nestedFilterKeyword);
+ }
+
+ if (false === $numberFilter->isEmpty()) {
+ $nestedFilterNumber = new Nested();
+ $nestedFilterNumber->setPath('search_data')
+ ->setQuery($numberFilter);
+ $queryNumberFiltered->filter($nestedFilterNumber);
+ }
+
+ if ($queryNumberFiltered->isEmpty() === false) {
+ $aggsFiltered->setQuery($queryNumberFiltered);
+ } else {
+ $aggsFiltered->setNested((new Nested())->setPath('search_data'));
+ }
+
+ $request->getAggs()->add($aggsFiltered);
+ continue;
+ }
+
+ if (in_array($filter->operator()->value(), [], true)) {
+ continue;
+ }
+
+ if ('search' === $field) {
+ continue;
+ }
+
+ if ('category_id' === $field) {
+ continue;
+ }
+
+ $aggsFiltered = new Aggs($getKey('keyword', $field));
+ $aggsFiltered->addAggs(
+ AggsFacetTerms::create(
+ 'agg_special',
+ 'keyword_facet'
+ )
+ );
+ $queryKeywordFiltered = new Query();
+ $keywordFilter = $this->getKeywordFilter($criteria, [$field]);
+ $numberFilter = $this->getNumberFilter($criteria);
+ if (false === $keywordFilter->isEmpty()) {
+ $nestedFilterKeyword = new Nested();
+ $nestedFilterKeyword->setPath('search_data')
+ ->setQuery($keywordFilter);
+ $queryKeywordFiltered->filter($nestedFilterKeyword);
+ }
+
+ if (false === $numberFilter->isEmpty()) {
+ $nestedFilterNumber = new Nested();
+ $nestedFilterNumber->setPath('search_data')
+ ->setQuery($numberFilter);
+ $queryKeywordFiltered->filter($nestedFilterNumber);
+ }
+
+ if ($queryKeywordFiltered->isEmpty() === false) {
+ $aggsFiltered->setQuery($queryKeywordFiltered);
+ } else {
+ $aggsFiltered->setNested((new Nested())->setPath('search_data'));
+ }
+
+ $request->getAggs()->add($aggsFiltered);
+ }
+
+ $keywordFilter = $this->getKeywordFilter($criteria);
+ $numberFilter = $this->getNumberFilter($criteria);
+
+ $aggsKeywordFiltered = new Aggs('keyword_facet_filtered');
+ $aggsKeywordFiltered->addAggs(
+ AggsFacetTerms::create(
+ 'all_keyword_facet_filtered',
+ 'keyword_facet'
+ )
+ );
+ $queryKeywordFiltered = new Query();
+
+ $aggsNumberFiltered = new Aggs('number_facet_filtered');
+ $aggsNumberFiltered->addAggs(
+ AggsFacetStats::create(
+ 'all_number_facet_filtered',
+ 'number_facet'
+ )
+ );
+ $queryNumberFiltered = new Query();
+
+ if (false === $keywordFilter->isEmpty()) {
+ $nestedFilterKeyword = new Nested();
+ $nestedFilterKeyword->setPath('search_data')
+ ->setQuery($keywordFilter);
+
+ $queryKeywordFiltered->filter($nestedFilterKeyword);
+ $queryNumberFiltered->filter($nestedFilterKeyword);
+ }
+
+ if (false === $numberFilter->isEmpty()) {
+ $nestedFilterNumber = new Nested();
+ $nestedFilterNumber->setPath('search_data')
+ ->setQuery($numberFilter);
+
+ $queryKeywordFiltered->filter($nestedFilterNumber);
+ $queryNumberFiltered->filter($nestedFilterNumber);
+ }
+
+ if (false === $queryKeywordFiltered->isEmpty()) {
+ $aggsKeywordFiltered->setQuery($queryKeywordFiltered);
+ } else {
+ $aggsKeywordFiltered->setNested((new Nested())->setPath('search_data'));
+ }
+
+ if (false === $queryNumberFiltered->isEmpty()) {
+ $aggsNumberFiltered->setQuery($queryNumberFiltered);
+ } else {
+ $aggsNumberFiltered->setNested((new Nested())->setPath('search_data'));
+ }
+
+ $request->getAggs()
+ ->add($aggsKeywordFiltered)
+ ->add($aggsNumberFiltered);
+
+ return $request;
+ }
+}
diff --git a/src/ElasticSearch/Converter/EsResponseToResult.php b/src/ElasticSearch/Converter/EsResponseToResult.php
new file mode 100644
index 0000000..e1e89c4
--- /dev/null
+++ b/src/ElasticSearch/Converter/EsResponseToResult.php
@@ -0,0 +1,156 @@
+asArray();
+ if (isset($data['hits']['hits'])) {
+ foreach ($data['hits']['hits'] as $hit) {
+ if (isset($hit['_source'])) {
+ try {
+ $product = $this->productFromArray($hit['_source']);
+ $catalogSearchResult->getProducts()->add($product);
+ } catch (\Throwable $ex) {
+ continue;
+ }
+ }
+ }
+
+ $catalogSearchResult->setTotal((int)$data['hits']['total']['value']);
+ }
+
+ if (isset($data['aggregations']['keyword_facet']['agg_keyword_facet_code']['buckets'])) {
+ $buckets = $data['aggregations']['keyword_facet']['agg_keyword_facet_code']['buckets'];
+ $bucketsFiltered = [];
+ if (isset($data['aggregations']['keyword_facet_filtered']['all_keyword_facet_filtered']['agg_keyword_facet_code']['buckets'])) {
+ foreach ($data['aggregations']['keyword_facet_filtered']['all_keyword_facet_filtered']['agg_keyword_facet_code']['buckets'] as $bucket) {
+ $bucketsFiltered[$bucket['key']] = [];
+ foreach ($bucket['agg_keyword_facet_value']['buckets'] as $values) {
+ $bucketsFiltered[$bucket['key']][$values['key']] = $values;
+ }
+ }
+ }
+
+ foreach ($buckets as $bucket) {
+ $code = $bucket['key'];
+ if (isset($data['aggregations']["keyword_facet_$code"]['agg_special']['agg_keyword_facet_code']['buckets'])) {
+ $bucketsFiltered[$code] = [];
+ foreach ($data['aggregations']["keyword_facet_$code"]['agg_special']['agg_keyword_facet_code']['buckets'] as $filteredBucket) {
+ if ($filteredBucket['key'] === $code) {
+ foreach ($filteredBucket['agg_keyword_facet_value']['buckets'] as $values) {
+ $bucketsFiltered[$code][$values['key']] = $values;
+ }
+ }
+ }
+ }
+ }
+ $bucketsFiltered = array_filter($bucketsFiltered);
+
+ foreach ($buckets as $bucket) {
+ $code = $bucket['key'];
+ $valueBucket = $bucket['agg_keyword_facet_value']['buckets'];
+
+ $facet = new Facet(new FacetListType(), $code);
+
+ foreach ($valueBucket as $value) {
+ $count = 0;
+
+ if (isset($bucketsFiltered[$code][$value['key']])) {
+ $count = $bucketsFiltered[$code][$value['key']]['doc_count'];
+ }
+
+ $facet->products->add(
+ FacetItemList::create(
+ $value['key'],
+ $count,
+ isset($bucketsFiltered[$code][$value['key']])
+ )
+ );
+ }
+
+ $catalogSearchResult->getFacets()->add($facet);
+ }
+ }
+
+ if (isset($data['aggregations']['number_facet']['agg_number_facet_code']['buckets'])) {
+ $buckets = $data['aggregations']['number_facet']['agg_number_facet_code']['buckets'];
+ $bucketsFiltered = [];
+
+ if (isset($data['aggregations']['number_facet_filtered']['all_number_facet_filtered']['agg_number_facet_code']['buckets'])) {
+ foreach ($data['aggregations']['number_facet_filtered']['all_number_facet_filtered']['agg_number_facet_code']['buckets'] as $bucket) {
+ $bucketsFiltered[$bucket['key']] = $bucket['agg_number_facet_value'];
+ }
+ }
+
+ foreach ($buckets as $bucket) {
+ $code = $bucket['key'];
+ if (isset($data['aggregations']["number_facet_$code"]['agg_special']['agg_number_facet_code']['buckets'])) {
+ $bucketsFiltered[$code] = [];
+ foreach ($data['aggregations']["number_facet_$code"]['agg_special']['agg_number_facet_code']['buckets'] as $filteredBucked) {
+ if ($filteredBucked['key'] === $code) {
+ $bucketsFiltered[$code] = $filteredBucked['agg_number_facet_value'];
+ }
+ }
+ }
+ }
+ $bucketsFiltered = array_filter($bucketsFiltered);
+
+ foreach ($buckets as $bucket) {
+ $code = $bucket['key'];
+ $workBucket = $bucket['agg_number_facet_value'];
+ $selectedBuket = $bucketsFiltered[$code] ?? null;
+
+ $facet = new Facet(new FacetRangeType(), $code);
+ $facetItem = FacetItemRange::create(
+ FacetItemRangeDTO::create(
+ $workBucket['min'],
+ $workBucket['max'],
+ $workBucket['avg'],
+ $workBucket['sum']
+ ),
+ isset($selectedBuket) ? FacetItemRangeDTO::create(
+ $selectedBuket['min'],
+ $selectedBuket['max'],
+ $selectedBuket['avg'],
+ $selectedBuket['sum']
+ ) : FacetItemRangeDTO::create(),
+ $selectedBuket['count'] ?? $workBucket['count'],
+ isset($selectedBuket)
+ );
+ $facet->products->add($facetItem);
+
+ $catalogSearchResult->getFacets()->add($facet);
+ }
+ }
+
+ return $catalogSearchResult;
+ }
+
+ private function productFromArray(array $data): Product
+ {
+ if (!isset($data['data']['id'])) {
+ throw new \RuntimeException('id is not set');
+ }
+ $id = $data['data']['id'];
+
+ $title = $data['title'] ?? '';
+ $info = $data['data'] ?? [];
+
+ return new Product($id, $title, $info);
+ }
+}
diff --git a/src/ElasticSearch/Document/Document.php b/src/ElasticSearch/Document/Document.php
index 9922ebc..62944ad 100644
--- a/src/ElasticSearch/Document/Document.php
+++ b/src/ElasticSearch/Document/Document.php
@@ -6,4 +6,4 @@ use IQDEV\ElasticSearch\Esable;
interface Document extends Esable
{
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Document/ProductDocument.php b/src/ElasticSearch/Document/ProductDocument.php
index d6071e8..87752e8 100644
--- a/src/ElasticSearch/Document/ProductDocument.php
+++ b/src/ElasticSearch/Document/ProductDocument.php
@@ -2,24 +2,38 @@
namespace IQDEV\ElasticSearch\Document;
+use IQDEV\ElasticSearch\Config\MappingValidator;
+use IQDEV\ElasticSearch\Configuration;
use IQDEV\ElasticSearch\Facet\FacetCategory;
use IQDEV\ElasticSearch\Facet\FacetCollection;
+use IQDEV\ElasticSearch\Helper\ArrayHelper;
class ProductDocument implements Document
{
+ private array $properties = [];
private FacetCollection $keywordFacets;
private FacetCollection $numberFacets;
- private ?string $fullSearchContent = null;
- private array $info;
+ private ?string $searchContent = null;
+ private array $info = [];
+ private bool $skipEmpty = false;
private FacetCategory $categoryFacet;
- public function __construct(FacetCategory $categoryFacet, array $info = [])
+
+ public function __construct(
+ FacetCategory $categoryFacet
+ )
{
$this->keywordFacets = new FacetCollection();
$this->numberFacets = new FacetCollection();
+
$this->categoryFacet = $categoryFacet;
- $this->info = $info;
+
+ }
+
+ public static function create(FacetCategory $categoryFacet): self
+ {
+ return new self($categoryFacet);
}
/**
@@ -47,11 +61,45 @@ class ProductDocument implements Document
}
/**
- * @param string|null $fullSearchContent
+ * @param string|null $searchContent
*/
- public function setFullSearchContent(?string $fullSearchContent): void
+ public function setSearchContent(?string $searchContent): void
{
- $this->fullSearchContent = $fullSearchContent;
+ $this->searchContent = $searchContent;
+ }
+
+ public function setAdditionData(array $info): self
+ {
+ $this->info = $info;
+ return $this;
+ }
+
+ /**
+ * Установка значения свойства документа индекса по параметрам конфигурации.
+ * Имеет приоритет по сравнению с вызовами функций для установки данных.
+ *
+ * @param Configuration $configuration
+ * @param string $property
+ * @param $value
+ *
+ * @return $this
+ */
+ public function setByConfiguration(Configuration $configuration, string $property, $value): self
+ {
+ if (!MappingValidator::isPropertyExists($configuration, $property)) {
+ throw new \InvalidArgumentException('Property ' . $property . ' doesnt exist');
+ }
+
+ $this->properties[$property] = $value;
+
+ return $this;
+ }
+
+ public function skipEmpty(bool $skipEmpty = false): self
+ {
+ $this->skipEmpty = $skipEmpty;
+
+ return $this;
}
public function es(): array
@@ -65,10 +113,16 @@ class ProductDocument implements Document
'data' => $this->info
];
- if ($this->fullSearchContent) {
- $document['full_search_content'] = $this->fullSearchContent;
+ if (isset($this->searchContent)) {
+ $document['full_search_content'] = $this->searchContent;
+ $document['suggest_search_content'] = $this->searchContent;
+ }
+
+ $result = array_replace_recursive($document, $this->properties);
+ if (true === $this->skipEmpty) {
+ $result = ArrayHelper::array_filter_recursive($result);
}
- return $document;
+ return $result;
}
}
diff --git a/src/ElasticSearch/Domain/SearchResultFactory.php b/src/ElasticSearch/Domain/SearchResultFactory.php
deleted file mode 100644
index 363abe4..0000000
--- a/src/ElasticSearch/Domain/SearchResultFactory.php
+++ /dev/null
@@ -1,124 +0,0 @@
-getBody(), true);
-
- if (isset($data['hits']['hits'])) {
- foreach ($data['hits']['hits'] as $hit) {
- $result->hits->add(new Document($hit['_id'], $hit));
- }
- $result->numProduct = (int)$data['hits']['total']['value'];
- }
-
- if ($request->getPagination()) {
- $result->numPages = ceil($result->numProduct / $request->getPagination()->size);
- }
-
- if (isset($data['aggregations']['keyword_facet']['agg_keyword_facet_code']['buckets'])) {
- $buckets = $data['aggregations']['keyword_facet']['agg_keyword_facet_code']['buckets'];
- $bucketsFiltered = [];
- if (isset($data['aggregations']['keyword_facet_filtered']['all_keyword_facet_filtered']['agg_keyword_facet_code']['buckets'])) {
- foreach ($data['aggregations']['keyword_facet_filtered']['all_keyword_facet_filtered']['agg_keyword_facet_code']['buckets'] as $bucket) {
- $bucketsFiltered[$bucket['key']] = [];
- foreach ($bucket['agg_keyword_facet_value']['buckets'] as $values) {
- $bucketsFiltered[$bucket['key']][$values['key']] = $values;
- }
- }
- }
-
- foreach ($buckets as $bucket) {
- $code = $bucket['key'];
- if (isset($data['aggregations']["keyword_facet_$code"]['agg_special']['agg_keyword_facet_code']['buckets'])) {
- $bucketsFiltered[$code] = [];
- foreach ($data['aggregations']["keyword_facet_$code"]['agg_special']['agg_keyword_facet_code']['buckets'] as $filtredBucket) {
- foreach ($filtredBucket['agg_keyword_facet_value']['buckets'] as $values) {
- if ($filtredBucket['key'] === $code) {
- $bucketsFiltered[$code][$values['key']] = $values;
- }
- }
- }
- }
- }
-
- foreach ($buckets as $bucket) {
- $code = $bucket['key'];
- $valueBucket = $bucket['agg_keyword_facet_value']['buckets'];
-
- $facet = new Facet(new ListFacetType, $code);
-
- foreach ($valueBucket as $value) {
- $count = !empty($bucketsFiltered) || $result->numProduct === 0 ? 0 : $value['doc_count'];
- if (isset($bucketsFiltered[$code][$value['key']])) {
- $count = $bucketsFiltered[$code][$value['key']]['doc_count'];
- }
- $facet->items->add(FacetItemList::createFromValue($value['key'], $count));
- }
-
- $result->facets->add($facet);
- }
- }
-
- if (isset($data['aggregations']['number_facet']['agg_number_facet_code']['buckets'])) {
- $buckets = $data['aggregations']['number_facet']['agg_number_facet_code']['buckets'];
- $bucketsFiltered = [];
- if (isset($data['aggregations']['number_facet_filtered']['all_number_facet_filtered']['agg_number_facet_code']['buckets'])) {
- foreach ($data['aggregations']['number_facet_filtered']['all_number_facet_filtered']['agg_number_facet_code']['buckets'] as $bucket) {
- $bucketsFiltered[$bucket['key']] = [
- 'data' => $bucket,
- 'min' => $bucket['agg_number_facet_value']['min'],
- 'max' => $bucket['agg_number_facet_value']['max']
- ];
- }
- }
-
- foreach ($buckets as $bucket) {
- $code = $bucket['key'];
-
- $facet = new Facet(new RangeFacetType, $code);
- $count = !empty($bucketsFiltered) || $result->numProduct === 0 ? 0 : $bucket['doc_count'];
- $selectedMin = $selectedMax = null;
- if (isset($bucketsFiltered[$code])) {
- $count = $bucketsFiltered[$code]['data']['doc_count'];
- $selectedMin = $bucketsFiltered[$code]['min'];
- $selectedMax = $bucketsFiltered[$code]['max'];
- }
-
- $facet->items->add(FacetItemRange::createFromRange(
- $bucket['agg_number_facet_value']['min'],
- $bucket['agg_number_facet_value']['max'],
- $count,
- $selectedMin,
- $selectedMax
- )
- );
-
- $result->facets->add($facet);
- }
- }
-
- return $result;
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Domain/SearchService.php b/src/ElasticSearch/Domain/SearchService.php
deleted file mode 100644
index ffa88f7..0000000
--- a/src/ElasticSearch/Domain/SearchService.php
+++ /dev/null
@@ -1,183 +0,0 @@
-esClient = $esClient;
- $this->sIndex = $sIndex;
- }
-
- public function search(DQuery $q): Result
- {
- $request = new Request();
- $commonQuery = new Query();
-
- if ($q->filters) {
- foreach ($q->filters as $filter) {
- /** @var Filter $filter */
- if ($filter instanceof FilterCategory) {
- $request->getQuery()->must(
- (new Terms('category_id', $filter->value))
- );
- break;
- }
- }
- $commonQuery = $this->getQuery($q->filters);
- }
- $commonFilter = clone $commonQuery;
- $commonFilter->setType(Query::TYPE_FILTER);
-
- if ($q->pagination) {
- $request->setPagination(
- new Pagination($q->pagination->limit, $q->pagination->page)
- );
- }
-
- if ($q->sorting && !$q->sorting->isEmpty()) {
- $oSortingCollection = new SortingCollection();
- foreach ($q->sorting as $sorting) {
- if ($sorting instanceof DSortingFieldPair) {
- $oSortingCollection->add(new SortingFieldsPair($sorting->by, $sorting->direction));
- }
-
- if ($sorting instanceof DSortingPropertyKeywordPair) {
- $oSortingCollection->add(new SortingPropertyKeywordPair($sorting->by, $sorting->direction));
- }
-
- if ($sorting instanceof DSortingPropertyNumberPair) {
- $oSortingCollection->add(new SortingPropertyNumberPair($sorting->by, $sorting->direction));
- }
- }
- $request->setSorting($oSortingCollection);
- }
-
- if ($q->query) {
- $request->addMatch('full_search_content', ['query' => $q->query]);
- }
-
- $getKey = static fn (string $type, string $name): string => sprintf('%s_facet_%s', $type, $name);
- if ($commonQuery->isEmpty() === false) {
- foreach ($q->filters as $filter) {
- /** @var Filter $filter */
- if ($filter instanceof FilterCategory || $filter instanceof FilterNumber) {
- continue;
- }
-
- $oFilters = new FilterCollection();
-
- if ($filter instanceof FilterKeyword) {
- foreach ($q->filters as $filter2) {
- if (!($filter2 instanceof Filter) || $filter2 instanceof FilterCategory) {
- continue;
- }
-
- if ($filter->key() === $filter2->key()) {
- continue;
- }
- $oFilters->add($filter2);
- }
- }
-
- $aggsFiltered = new Aggs($getKey('keyword', $filter->key()));
- $aggsFiltered->addAggs(
- AggsKeyWordFacet::create(
- 'agg_special',
- 'keyword_facet'
- )
- );
-
- if (false === $oFilters->isEmpty()) {
- $aggsFiltered->setQuery($this->getQuery($oFilters));
- } else {
- $aggsFiltered->setNested((new Nested())->setPath('search_data'));
- }
-
- $request->getAggs()->add($aggsFiltered);
- }
-
- $nestedFilter = new Nested();
- $nestedFilter->setPath('search_data')
- ->setQuery($commonQuery);
-
- $request->getPostFilter()->filter($nestedFilter);
-
- $aggsKeywordFiltered = new Aggs('keyword_facet_filtered');
- $aggsKeywordFiltered->addAggs(AggsKeyWordFacet::create('all_keyword_facet_filtered', 'keyword_facet'))
- ->setQuery($commonFilter);
- $request->getAggs()->add($aggsKeywordFiltered);
-
- $aggsNumberFiltered = new Aggs('number_facet_filtered');
- $aggsNumberFiltered->addAggs(AggsNumberFacet::create('all_number_facet_filtered', 'number_facet'))
- ->setQuery($commonFilter);
- $request->getAggs()->add($aggsNumberFiltered);
- }
-
- $aggsKeyword = AggsKeyWordFacet::create('keyword_facet', 'keyword_facet');
- $request->getAggs()->add($aggsKeyword);
- $aggsNumber = AggsNumberFacet::create('number_facet', 'number_facet');
- $request->getAggs()->add($aggsNumber);
-
- $response = $this->esClient->search(
- [
- 'index' => $this->sIndex,
- 'body' => $request->es(),
- ]
- );
-
-
- return SearchResultFactory::createFromResponse($response, $request);
- }
-
- private function getQuery(FilterCollection $filters): Query
- {
- $commonQuery = new Query();
- foreach ($filters as $filter) {
- if ($filter instanceof FilterNumber) {
- $oFacet = new FilterNumberFacet($filter->key, $filter->min, $filter->max);
- $commonQuery->filter($oFacet);
- continue;
- }
-
- if ($filter instanceof FilterKeyword) {
- $oFacet = new FilterKeywordFacet($filter->key, $filter->value);
- $commonQuery->filter($oFacet);
- continue;
- }
- }
-
- return $commonQuery;
- }
-}
diff --git a/src/ElasticSearch/Facet/Facet.php b/src/ElasticSearch/Facet/Facet.php
index 11b2a39..4cce190 100644
--- a/src/ElasticSearch/Facet/Facet.php
+++ b/src/ElasticSearch/Facet/Facet.php
@@ -6,4 +6,4 @@ use IQDEV\ElasticSearch\Esable;
interface Facet extends Esable
{
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Facet/FacetCategory.php b/src/ElasticSearch/Facet/FacetCategory.php
index 48bb468..6e286b1 100644
--- a/src/ElasticSearch/Facet/FacetCategory.php
+++ b/src/ElasticSearch/Facet/FacetCategory.php
@@ -6,21 +6,17 @@ use IQDEV\ElasticSearch\Esable;
final class FacetCategory implements Esable
{
- public string $category;
+ private string $category;
- /**
- * @param string $category
- */
public function __construct(string $category)
{
$this->category = $category;
}
-
public function es(): array
{
return [
- 'category_id' => $this->category
+ 'category_id' => $this->category,
];
}
}
diff --git a/src/ElasticSearch/Facet/FacetCollection.php b/src/ElasticSearch/Facet/FacetCollection.php
index 6d82851..e84af39 100644
--- a/src/ElasticSearch/Facet/FacetCollection.php
+++ b/src/ElasticSearch/Facet/FacetCollection.php
@@ -16,4 +16,4 @@ final class FacetCollection extends AbstractCollection implements Esable
{
return array_map(static fn(Facet $facet) => $facet->es(), $this->toArray());
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Facet/FacetKeyword.php b/src/ElasticSearch/Facet/FacetKeyword.php
index 448f427..fc43162 100644
--- a/src/ElasticSearch/Facet/FacetKeyword.php
+++ b/src/ElasticSearch/Facet/FacetKeyword.php
@@ -4,17 +4,14 @@ namespace IQDEV\ElasticSearch\Facet;
final class FacetKeyword implements Facet
{
- public string $key;
- public $value;
-
+ private string $key;
+ /** @var string|string[] */
+ private $value;
+
/**
- * @param string $key
* @param string|string[] $value
*/
- public function __construct(
- string $key,
- $value
- )
+ public function __construct(string $key, $value)
{
$this->key = $key;
$this->value = $value;
@@ -27,4 +24,4 @@ final class FacetKeyword implements Facet
'facet_value' => $this->value,
];
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Facet/FacetNumber.php b/src/ElasticSearch/Facet/FacetNumber.php
index 947d4cd..953dfca 100644
--- a/src/ElasticSearch/Facet/FacetNumber.php
+++ b/src/ElasticSearch/Facet/FacetNumber.php
@@ -4,17 +4,10 @@ namespace IQDEV\ElasticSearch\Facet;
final class FacetNumber implements Facet
{
- public string $key;
- public float $value;
-
- /**
- * @param string $key
- * @param float $value
- */
- public function __construct(
- string $key,
- float $value
- )
+ private string $key;
+ private float $value;
+
+ public function __construct(string $key, float $value)
{
$this->key = $key;
$this->value = $value;
@@ -27,4 +20,4 @@ final class FacetNumber implements Facet
'facet_value' => $this->value,
];
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Helper/ArrayHelper.php b/src/ElasticSearch/Helper/ArrayHelper.php
new file mode 100644
index 0000000..e050625
--- /dev/null
+++ b/src/ElasticSearch/Helper/ArrayHelper.php
@@ -0,0 +1,30 @@
+name = $name;
+ $this->body = $body;
+ $this->id = $id;
+ }
+
+ public function es(): array
+ {
+ $es = [
+ 'index' => $this->name,
+ 'body' => $this->body->es(),
+ ];
+
+ if ($this->id) {
+ $es['id'] = $this->id;
+ }
+
+ return $es;
+ }
+}
diff --git a/src/ElasticSearch/Indexer/BaseIndexProvider.php b/src/ElasticSearch/Indexer/BaseIndexProvider.php
index d8bafd1..2a5aee2 100644
--- a/src/ElasticSearch/Indexer/BaseIndexProvider.php
+++ b/src/ElasticSearch/Indexer/BaseIndexProvider.php
@@ -11,6 +11,8 @@ use IQDEV\ElasticSearch\Facet\FacetNumber;
class BaseIndexProvider implements IndexProvider
{
private array $products;
+ private ?int $size = null;
+ private ?int $limit = null;
private Configuration $configuration;
public function __construct($products, $configuration)
@@ -22,7 +24,8 @@ class BaseIndexProvider implements IndexProvider
public function get(): \Generator
{
foreach ($this->products as $product) {
- $document = new ProductDocument(new FacetCategory($product['category']), $product['data'] ?? []);
+ $document = new ProductDocument(new FacetCategory($product['category']));
+ $document->setAdditionData($product['data'] ?? []);
foreach ($product['properties'] as $type => $values) {
foreach ($values as $key => $prop) {
if ($type === 'number') {
@@ -32,13 +35,33 @@ class BaseIndexProvider implements IndexProvider
}
}
}
- $document->setFullSearchContent($product['name']);
+ $document->setSearchContent($product['name']);
- yield new Index(
+ yield new AddIndex(
$this->configuration->getIndexName(),
$document,
$product['id']
);
}
}
-}
\ No newline at end of file
+
+ public function setBatchSize(int $size): void
+ {
+ $this->size = $size;
+ }
+
+ public function getBatchSize(): ?int
+ {
+ return $this->size;
+ }
+
+ public function setLimit(int $limit): void
+ {
+ $this->limit = $limit;
+ }
+
+ public function getLimit(): ?int
+ {
+ return $this->limit;
+ }
+}
diff --git a/src/ElasticSearch/Indexer/BulkIndex.php b/src/ElasticSearch/Indexer/BulkIndex.php
new file mode 100644
index 0000000..94134a2
--- /dev/null
+++ b/src/ElasticSearch/Indexer/BulkIndex.php
@@ -0,0 +1,41 @@
+name = $name;
+ $this->body = $body;
+ $this->id = $id;
+ }
+
+ public function es(): array
+ {
+ $es = [
+ [
+ 'index' => [
+ '_index' => $this->name
+ ]
+ ]
+ ];
+ if ($this->id) {
+ $es[0]['index']['_id'] = $this->id;
+ }
+
+ $es[] = $this->body->es();
+
+ return $es;
+ }
+}
diff --git a/src/ElasticSearch/Indexer/DeleteIndex.php b/src/ElasticSearch/Indexer/DeleteIndex.php
new file mode 100644
index 0000000..df4b049
--- /dev/null
+++ b/src/ElasticSearch/Indexer/DeleteIndex.php
@@ -0,0 +1,31 @@
+name = $name;
+ $this->id = $id;
+ }
+
+ public function es(): array
+ {
+ $es = [
+ 'index' => $this->name
+ ];
+
+ if ($this->id) {
+ $es['id'] = $this->id;
+ }
+
+ return $es;
+ }
+}
diff --git a/src/ElasticSearch/Indexer/EsHelperEndpoint.php b/src/ElasticSearch/Indexer/EsHelperEndpoint.php
new file mode 100644
index 0000000..bc37f33
--- /dev/null
+++ b/src/ElasticSearch/Indexer/EsHelperEndpoint.php
@@ -0,0 +1,180 @@
+esClient = $esClient;
+ $this->configuration = $configuration;
+ $this->logger = $logger;
+ }
+
+ public function isIndexExists(): bool
+ {
+ $response = $this->esClient
+ ->indices()
+ ->exists(
+ [
+ 'index' => $this->configuration->getIndexName(),
+ ]
+ );
+
+ return $response instanceof Elasticsearch && true === $response->asBool();
+ }
+
+ /**
+ * Создание индекса
+ *
+ * @return void
+ *
+ * @throws \Elastic\Elasticsearch\Exception\ClientResponseException
+ * @throws \Elastic\Elasticsearch\Exception\MissingParameterException
+ * @throws \Elastic\Elasticsearch\Exception\ServerResponseException
+ */
+ public function create(): void
+ {
+ if (false === $this->isIndexExists()) {
+ $this->logger->info(sprintf('Index %s was created', $this->configuration->getIndexName()));
+ $this->esClient->indices()->create(
+ [
+ 'index' => $this->configuration->getIndexName(),
+ 'body' => [
+ 'mappings' => $this->configuration->getMapping(),
+ 'settings' => $this->configuration->getSettings(),
+ ],
+ ]
+ );
+ }
+ }
+
+ /**
+ * Обновление конфигурации индекса
+ *
+ * @throws ContentTypeException
+ */
+ public function reconfigurate(): void
+ {
+ $this->esClient->sendRequest(
+ $this->createRequest(
+ 'POST',
+ '/' . $this->encode($this->configuration->getIndexName()) . '/_close',
+ [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json',
+ ],
+ [],
+ )
+ );
+ $this->esClient->sendRequest(
+ $this->createRequest(
+ 'PUT',
+ '/' . $this->encode($this->configuration->getIndexName()) . '/_settings',
+ [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json',
+ ],
+ $this->configuration->getSettings(),
+ )
+ );
+
+ $this->esClient->sendRequest(
+ $this->createRequest(
+ 'PUT',
+ '/' . $this->encode($this->configuration->getIndexName()) . '/_mapping',
+ [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json',
+ ],
+ $this->configuration->getMapping(),
+ )
+ );
+
+ $this->esClient->sendRequest(
+ $this->createRequest(
+ 'POST',
+ '/' . $this->encode($this->configuration->getIndexName()) . '/_open',
+ [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json',
+ ],
+ [],
+ )
+ );
+ $this->logger->info(sprintf('Index %s was reconfigurated', $this->configuration->getIndexName()));
+ }
+
+ /**
+ * Полная очистка документов индекса
+ *
+ * @throws ContentTypeException
+ */
+ public function clear(): void
+ {
+ if ($this->isIndexExists()) {
+ $this->esClient->sendRequest(
+ $this->createRequest(
+ 'POST',
+ '/' . $this->encode($this->configuration->getIndexName()) . '/_delete_by_query',
+ [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json',
+ ],
+ '{"query": {"match_all": {}}}'
+ )
+ );
+ $this->logger->info(sprintf('Index %s was cleared', $this->configuration->getIndexName()));
+ } else {
+ $this->logger->error(sprintf('Index %s does not exists', $this->configuration->getIndexName()));
+ }
+ }
+
+ /**
+ * Удаление индекса
+ *
+ * @return void
+ *
+ * @throws ClientResponseException
+ * @throws MissingParameterException
+ * @throws ServerResponseException
+ */
+ public function delete(): void
+ {
+ if ($this->isIndexExists()) {
+ $response = $this->esClient
+ ->indices()
+ ->delete(
+ [
+ 'index' => $this->configuration->getIndexName(),
+ ]
+ );
+ if (($response instanceof Elasticsearch) && false === $response->asBool()) {
+ $this->logger->info(sprintf('Index %s was deleted', $this->configuration->getIndexName()));
+ } else {
+ $this->logger->error(sprintf('Index %s was not deleted', $this->configuration->getIndexName()));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ElasticSearch/Indexer/Index.php b/src/ElasticSearch/Indexer/Index.php
index 30d0cea..f6138cc 100644
--- a/src/ElasticSearch/Indexer/Index.php
+++ b/src/ElasticSearch/Indexer/Index.php
@@ -4,33 +4,7 @@ namespace IQDEV\ElasticSearch\Indexer;
use IQDEV\ElasticSearch\Esable;
-final class Index implements Esable
+interface Index extends Esable
{
- private string $name;
- private Esable $body;
- private ?string $id;
-
- public function __construct(
- string $name,
- Esable $body,
- ?string $id = null
- ) {
- $this->name = $name;
- $this->body = $body;
- $this->id = $id;
- }
- public function es(): array
- {
- $es = [
- 'index' => $this->name,
- 'body' => $this->body->es(),
- ];
-
- if ($this->id) {
- $es['id'] = $this->id;
- }
-
- return $es;
- }
-}
+}
\ No newline at end of file
diff --git a/src/ElasticSearch/Indexer/IndexProvider.php b/src/ElasticSearch/Indexer/IndexProvider.php
index 38db55d..83dc1a3 100644
--- a/src/ElasticSearch/Indexer/IndexProvider.php
+++ b/src/ElasticSearch/Indexer/IndexProvider.php
@@ -4,5 +4,42 @@ namespace IQDEV\ElasticSearch\Indexer;
interface IndexProvider
{
+ /**
+ * Итерационное получение элемнтов для обновления
+ *
+ * @return \Generator|Index[]
+ */
public function get(): \Generator;
-}
\ No newline at end of file
+
+ /**
+ * Установка размера пакета для передачив elasticsearch
+ *
+ * @param int $size
+ *
+ * @return void
+ */
+ public function setBatchSize(int $size): void;
+
+ /**
+ * Получение размера пакета для передачив elasticsearch
+ *
+ * @return int|null
+ */
+ public function getBatchSize(): ?int;
+
+ /**
+ * Установка лимита на количество обрабатываемых данных для индексации за один раз
+ *
+ * @param int $limit
+ *
+ * @return void
+ */
+ public function setLimit(int $limit): void;
+
+ /**
+ * Получение лимита на количество обрабатываемых данных для индексации за один раз
+ *
+ * @return int|null
+ */
+ public function getLimit(): ?int;
+}
diff --git a/src/ElasticSearch/Indexer/IndexRunner.php b/src/ElasticSearch/Indexer/IndexRunner.php
index 572f2f1..8ac968b 100644
--- a/src/ElasticSearch/Indexer/IndexRunner.php
+++ b/src/ElasticSearch/Indexer/IndexRunner.php
@@ -4,40 +4,82 @@ namespace IQDEV\ElasticSearch\Indexer;
use Elastic\Elasticsearch\Client;
use IQDEV\ElasticSearch\Configuration;
+use Psr\Log\LoggerInterface;
final class IndexRunner
{
private Client $esClient;
- private Configuration $configuration;
+ private EsHelperEndpoint $helper;
public function __construct(
- Client $esClient,
- Configuration $configuration
- ) {
+ Client $esClient,
+ Configuration $configuration,
+ LoggerInterface $logger
+ )
+ {
$this->esClient = $esClient;
- $this->configuration = $configuration;
+ $this->helper = new EsHelperEndpoint($esClient, $configuration, $logger);
}
public function run(IndexProvider $indexProvider)
{
- if ($this->esClient->indices()->exists(['index' => $this->configuration->getIndexName()])->asBool() === false) {
- $this->esClient->indices()->create(
- [
- 'index' => $this->configuration->getIndexName(),
- 'body' => [
- 'mappings' => $this->configuration->getMapping(),
- ],
- ]
- );
- }
+ $this->helper->create();
+
+ if ($indexProvider->getBatchSize() !== null && $indexProvider->getBatchSize() > 0) {
+ $counter = 0;
+ $params = ['body' => []];
+ foreach ($indexProvider->get() as $index) {
+ if ($index instanceof DeleteIndex) {
+ if (!empty($params['body'])) {
+ $this->esClient->bulk($params);
+ $params = ['body' => []];
+ $counter = 0;
+ }
+ $this->esClient->delete($index->es());
+ continue;
+ }
+
+ if ($index instanceof UpdateIndex) {
+ if (!empty($params['body'])) {
+ $this->esClient->bulk($params);
+ $params = ['body' => []];
+ $counter = 0;
+ }
+ $this->esClient->update($index->es());
+ continue;
+ }
+
+ if (!$index instanceof BulkIndex) {
+ continue;
+ }
+ $esIndex = $index->es();
+ foreach ($esIndex as $indexItem) {
+ $params['body'][] = $indexItem;
+ }
- foreach ($indexProvider->get() as $index) {
- if (!$index instanceof Index) {
- continue;
+ if (++$counter >= $indexProvider->getBatchSize()) {
+ $this->esClient->bulk($params);
+ $params = ['body' => []];
+ $counter = 0;
+ }
}
- $esIndex = $index->es();
- $this->esClient->index($esIndex);
+ if (!empty($params['body'])) {
+ $this->esClient->bulk($params);
+ }
+ } else {
+ foreach ($indexProvider->get() as $index) {
+ if ($index instanceof DeleteIndex) {
+ $this->esClient->delete($index->es());
+ continue;
+ }
+
+ if (!($index instanceof AddIndex) && !($index instanceof UpdateIndex)) {
+ continue;
+ }
+
+ $this->esClient->index($index->es());
+ }
}
}
}
diff --git a/src/ElasticSearch/Indexer/UpdateIndex.php b/src/ElasticSearch/Indexer/UpdateIndex.php
new file mode 100644
index 0000000..5057292
--- /dev/null
+++ b/src/ElasticSearch/Indexer/UpdateIndex.php
@@ -0,0 +1,39 @@
+name = $name;
+ $this->body = $body;
+ $this->id = $id;
+ }
+
+ public function es(): array
+ {
+ $es = [
+ 'index' => $this->name,
+ 'body' => [
+ 'doc' => $this->body->es()
+ ]
+ ];
+
+ if ($this->id) {
+ $es['id'] = $this->id;
+ }
+
+ return $es;
+ }
+}
diff --git a/src/ElasticSearch/Order/Order.php b/src/ElasticSearch/Order/Order.php
new file mode 100644
index 0000000..b23f067
--- /dev/null
+++ b/src/ElasticSearch/Order/Order.php
@@ -0,0 +1,24 @@
+by = $by;
+ $this->direction = $direction;
+ $this->properties = $properties;
+ }
+
+ public function es(): array
+ {
+ return array_merge([$this->by => $this->direction::getType()], $this->properties);
+ }
+}
diff --git a/src/ElasticSearch/Order/OrderAscType.php b/src/ElasticSearch/Order/OrderAscType.php
new file mode 100644
index 0000000..69b8f08
--- /dev/null
+++ b/src/ElasticSearch/Order/OrderAscType.php
@@ -0,0 +1,8 @@
+ $order->es(), $this->toArray());
+ }
+}
diff --git a/src/ElasticSearch/Order/OrderDescType.php b/src/ElasticSearch/Order/OrderDescType.php
new file mode 100644
index 0000000..775fa3c
--- /dev/null
+++ b/src/ElasticSearch/Order/OrderDescType.php
@@ -0,0 +1,8 @@
+ $this->direction::getType(),
+ 'nested' => [
+ 'path' => 'search_data',
+ 'filter' => [
+ 'bool' => [
+ 'must' => [
+ 'term' => [
+ 'search_data.keyword_facet.facet_code' => $this->by,
+ ]
+ ],
+ ],
+ ],
+ ],
+ ];
+ $order = array_merge($order, $this->properties);
+ return [
+ 'search_data.keyword_facet.facet_value' => $order,
+ ];
+ }
+}
diff --git a/src/ElasticSearch/Order/OrderNumberProperty.php b/src/ElasticSearch/Order/OrderNumberProperty.php
new file mode 100644
index 0000000..befc546
--- /dev/null
+++ b/src/ElasticSearch/Order/OrderNumberProperty.php
@@ -0,0 +1,29 @@
+ $this->direction::getType(),
+ 'nested' => [
+ 'path' => 'search_data',
+ 'filter' => [
+ 'bool' => [
+ 'must' => [
+ 'term' => [
+ 'search_data.number_facet.facet_code' => $this->by,
+ ]
+ ],
+ ],
+ ],
+ ],
+ ];
+ $order = array_merge($order, $this->properties);
+ return [
+ 'search_data.number_facet.facet_value' => $order,
+ ];
+ }
+}
diff --git a/src/ElasticSearch/Order/OrderType.php b/src/ElasticSearch/Order/OrderType.php
new file mode 100644
index 0000000..979e079
--- /dev/null
+++ b/src/ElasticSearch/Order/OrderType.php
@@ -0,0 +1,13 @@
+key = $key;
}
- /**
- * @param Aggs|null $aggs
- * @return Aggs
- */
- public function addAggs(?Aggs $aggs): self
+ public function addAggs(Aggs $aggs): self
{
- if ($this->aggs === null) {
+ if (null === $this->aggs) {
$this->aggs = new AggsCollection();
}
$this->aggs->add($aggs);
+
return $this;
}
- /**
- * @param Query|null $query
- * @return Aggs
- */
public function setQuery(?Query $query): self
{
$this->query = $query;
+
return $this;
}
- /**
- * @param Nested|null $nested
- * @return Aggs
- */
public function setNested(?Nested $nested): self
{
$this->nested = $nested;
+
return $this;
}
- /**
- * @param Terms|null $terms
- * @return Aggs
- */
public function setTerms(?Terms $terms): self
{
$this->terms = $terms;
+
return $this;
}
- /**
- * @param ExtremumTerms|null $terms
- * @return Aggs
- */
- public function setExtremumTerms(?ExtremumTerms $terms): self
+ public function setStats(?Stats $stats): self
{
- $this->extremumTerms = $terms;
+ $this->stats = $stats;
+
return $this;
}
- /**
- * @return string
- */
public function getKey(): string
{
return $this->key;
@@ -90,7 +72,7 @@ final class Aggs implements Esable
$agg['aggs'] = array_merge($agg, $this->aggs->es()['aggs']);
}
- if (isset($this->query) && $this->query->isEmpty() === false) {
+ if ($this->query && false === $this->query->isEmpty()) {
$agg['filter'] = $this->query->es()[$this->query->getType()];
}
@@ -101,11 +83,11 @@ final class Aggs implements Esable
if ($this->terms) {
$agg['terms'] = $this->terms->es()['terms'];
}
-
- if ($this->extremumTerms) {
- $agg = array_merge($agg, $this->extremumTerms->es());
+
+ if ($this->stats) {
+ $agg['stats'] = $this->stats->es()['stats'];
}
return $agg;
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/Aggs/AggsCollection.php b/src/ElasticSearch/Search/Aggs/AggsCollection.php
index f7a8006..90ca24d 100644
--- a/src/ElasticSearch/Search/Aggs/AggsCollection.php
+++ b/src/ElasticSearch/Search/Aggs/AggsCollection.php
@@ -3,22 +3,28 @@
namespace IQDEV\ElasticSearch\Search\Aggs;
use IQDEV\ElasticSearch\Esable;
-use Ramsey\Collection\AbstractCollection;
-final class AggsCollection extends AbstractCollection implements Esable
+final class AggsCollection implements Esable
{
- public function getType(): string
+ /**
+ * @var Aggs[]
+ */
+ private array $aggs = [];
+
+ public function add(Aggs $aggs): self
{
- return Aggs::class;
+ $this->aggs[] = $aggs;
+
+ return $this;
}
public function es(): array
{
$aggs = [];
- foreach ($this as $agg) {
+ foreach ($this->aggs as $agg) {
$aggs[$agg->getKey()] = $agg->es();
}
return ['aggs' => $aggs];
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/Aggs/AggsFacetStats.php b/src/ElasticSearch/Search/Aggs/AggsFacetStats.php
new file mode 100644
index 0000000..d44574c
--- /dev/null
+++ b/src/ElasticSearch/Search/Aggs/AggsFacetStats.php
@@ -0,0 +1,31 @@
+setPath($path . '.' . $facet);
+ $aggNumberFacet->setNested($nested);
+
+ $aggNumberFacetCode = new Aggs("agg_{$facet}_code");
+ $aggNumberFacetCode->setTerms(
+ (new Terms("{$path}.{$facet}.facet_code"))
+ ->setSize(250)
+ );
+ $aggKeywordFacetValue = new Aggs("agg_{$facet}_value");
+ $aggKeywordFacetValue->setStats(
+ new Stats("{$path}.{$facet}.facet_value")
+ );
+
+ $aggNumberFacetCode->addAggs($aggKeywordFacetValue);
+ $aggNumberFacet->addAggs($aggNumberFacetCode);
+
+ return $aggNumberFacet;
+ }
+}
diff --git a/src/ElasticSearch/Search/Aggs/AggsKeyWordFacet.php b/src/ElasticSearch/Search/Aggs/AggsFacetTerms.php
similarity index 96%
rename from src/ElasticSearch/Search/Aggs/AggsKeyWordFacet.php
rename to src/ElasticSearch/Search/Aggs/AggsFacetTerms.php
index 482fa16..3f53766 100644
--- a/src/ElasticSearch/Search/Aggs/AggsKeyWordFacet.php
+++ b/src/ElasticSearch/Search/Aggs/AggsFacetTerms.php
@@ -4,7 +4,7 @@ namespace IQDEV\ElasticSearch\Search\Aggs;
use IQDEV\ElasticSearch\Search\Nested;
-final class AggsKeyWordFacet
+final class AggsFacetTerms
{
public static function create(string $code, string $facet, string $path = 'search_data'): Aggs
{
@@ -18,7 +18,6 @@ final class AggsKeyWordFacet
(new Terms("{$path}.{$facet}.facet_code"))
->setSize(250)
);
-
$aggKeywordFacetValue = new Aggs("agg_{$facet}_value");
$aggKeywordFacetValue->setTerms(
(new Terms("{$path}.{$facet}.facet_value"))
diff --git a/src/ElasticSearch/Search/Aggs/AggsNumberFacet.php b/src/ElasticSearch/Search/Aggs/AggsNumberFacet.php
deleted file mode 100644
index 16d0b01..0000000
--- a/src/ElasticSearch/Search/Aggs/AggsNumberFacet.php
+++ /dev/null
@@ -1,31 +0,0 @@
-setPath($path . '.' . $facet);
- $aggFacet->setNested($nested);
-
- $aggFacetCode = new Aggs("agg_{$facet}_code");
- $aggFacetCode->setTerms(
- (new Terms("{$path}.{$facet}.facet_code"))
- );
-
- $aggFacetValue = new Aggs("agg_{$facet}_value");
- $aggFacetValue->setExtremumTerms(
- (new ExtremumTerms("{$path}.{$facet}.facet_value"))
- );
- $aggFacetCode->addAggs($aggFacetValue);
-
- $aggFacet->addAggs($aggFacetCode);
-
- return $aggFacet;
- }
-}
diff --git a/src/ElasticSearch/Search/Aggs/BoolQueryCollection.php b/src/ElasticSearch/Search/Aggs/BoolQueryCollection.php
deleted file mode 100644
index 536f76d..0000000
--- a/src/ElasticSearch/Search/Aggs/BoolQueryCollection.php
+++ /dev/null
@@ -1,19 +0,0 @@
- $facet->es(), $this->toArray());
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Search/Aggs/ExtremumTerms.php b/src/ElasticSearch/Search/Aggs/Stats.php
similarity index 79%
rename from src/ElasticSearch/Search/Aggs/ExtremumTerms.php
rename to src/ElasticSearch/Search/Aggs/Stats.php
index 9de2a5c..344eaf5 100644
--- a/src/ElasticSearch/Search/Aggs/ExtremumTerms.php
+++ b/src/ElasticSearch/Search/Aggs/Stats.php
@@ -2,16 +2,12 @@
namespace IQDEV\ElasticSearch\Search\Aggs;
-
use IQDEV\ElasticSearch\Esable;
-final class ExtremumTerms implements Esable
+final class Stats implements Esable
{
private string $field;
- /**
- * @param string $field
- */
public function __construct(string $field)
{
$this->field = $field;
@@ -25,4 +21,4 @@ final class ExtremumTerms implements Esable
],
];
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/Aggs/Terms.php b/src/ElasticSearch/Search/Aggs/Terms.php
index 582d069..20d53b7 100644
--- a/src/ElasticSearch/Search/Aggs/Terms.php
+++ b/src/ElasticSearch/Search/Aggs/Terms.php
@@ -2,7 +2,6 @@
namespace IQDEV\ElasticSearch\Search\Aggs;
-
use IQDEV\ElasticSearch\Esable;
final class Terms implements Esable
@@ -10,9 +9,6 @@ final class Terms implements Esable
private array $options = [];
private string $field;
- /**
- * @param string $field
- */
public function __construct(string $field)
{
$this->field = $field;
@@ -21,14 +17,15 @@ final class Terms implements Esable
public function setSize(int $size): self
{
$this->options['size'] = $size;
+
return $this;
}
public function es(): array
{
- $data = $this->options;
+ $data = $this->options;
$data['field'] = $this->field;
return ['terms' => $data];
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/BoolQuery/BoolQueryCollection.php b/src/ElasticSearch/Search/BoolQuery/BoolQueryCollection.php
index d3d901d..7cd83f8 100644
--- a/src/ElasticSearch/Search/BoolQuery/BoolQueryCollection.php
+++ b/src/ElasticSearch/Search/BoolQuery/BoolQueryCollection.php
@@ -14,6 +14,6 @@ final class BoolQueryCollection extends AbstractCollection implements Esable
public function es(): array
{
- return array_map(static fn(Esable $facet) => $facet->es(), $this->toArray());
+ return array_map(static fn (Esable $facet) => $facet->es(), $this->toArray());
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/BoolQuery/FilterKeywordFacet.php b/src/ElasticSearch/Search/BoolQuery/FilterKeywordFacet.php
index cfa5e50..3e20d97 100644
--- a/src/ElasticSearch/Search/BoolQuery/FilterKeywordFacet.php
+++ b/src/ElasticSearch/Search/BoolQuery/FilterKeywordFacet.php
@@ -5,17 +5,16 @@ namespace IQDEV\ElasticSearch\Search\BoolQuery;
use IQDEV\ElasticSearch\Esable;
use IQDEV\ElasticSearch\Search\Nested;
-
final class FilterKeywordFacet implements Esable
{
public string $key;
-
+
/** @var string|string[] */
public $value;
public function __construct(string $key, $value)
{
- $this->key = $key;
+ $this->key = $key;
$this->value = $value;
}
@@ -36,4 +35,4 @@ final class FilterKeywordFacet implements Esable
return $nested->es();
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/BoolQuery/FilterNumberFacet.php b/src/ElasticSearch/Search/BoolQuery/FilterNumberFacet.php
index 178cded..b150931 100644
--- a/src/ElasticSearch/Search/BoolQuery/FilterNumberFacet.php
+++ b/src/ElasticSearch/Search/BoolQuery/FilterNumberFacet.php
@@ -4,20 +4,18 @@ namespace IQDEV\ElasticSearch\Search\BoolQuery;
use IQDEV\ElasticSearch\Esable;
use IQDEV\ElasticSearch\Search\Nested;
-
+use IQDEV\Search\Filter\FilterOperator;
final class FilterNumberFacet implements Esable
{
public string $key;
- public float $min = 0;
- public float $max = PHP_INT_MAX;
+ public array $conditions;
- public function __construct(string $key, float $min, float $max)
+ public function __construct(string $key, array $conditions)
{
$this->key = $key;
- $this->min = $min;
- $this->max = $max;
+ $this->conditions = $conditions;
}
public function es(): array
@@ -28,8 +26,32 @@ final class FilterNumberFacet implements Esable
$query = new Query();
$query
- ->filter(new Terms($path . '.facet_code', $this->key))
- ->filter(new RangeTerms($path . '.facet_value', $this->min, $this->max));
+ ->filter(new Stats($path.'.facet_code', $this->key));
+
+ $conditions = [];
+ foreach ($this->conditions as $operator => $value) {
+ switch ($operator) {
+ case FilterOperator::GTE:
+ $key = 'gte';
+ break;
+ case FilterOperator::LTE:
+ $key = 'lte';
+ break;
+ case FilterOperator::GT:
+ $key = 'gt';
+ break;
+ case FilterOperator::LT:
+ $key = 'lt';
+ break;
+ default:
+ $key = null;
+ break;
+ }
+ if (isset($key)) {
+ $conditions[$key] = $value;
+ }
+ }
+ $query->filter(new Stats($path.'.facet_value', $conditions));
$nested
->setPath($path)
@@ -37,4 +59,4 @@ final class FilterNumberFacet implements Esable
return $nested->es();
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/BoolQuery/Query.php b/src/ElasticSearch/Search/BoolQuery/Query.php
index ba44f6e..7a235b1 100644
--- a/src/ElasticSearch/Search/BoolQuery/Query.php
+++ b/src/ElasticSearch/Search/BoolQuery/Query.php
@@ -5,16 +5,15 @@ namespace IQDEV\ElasticSearch\Search\BoolQuery;
use IQDEV\ElasticSearch\Esable;
use IQDEV\ElasticSearch\Search\Nested;
-
final class Query implements Esable
{
protected BoolQueryCollection $must;
protected BoolQueryCollection $filter;
protected BoolQueryCollection $should;
protected BoolQueryCollection $mustNot;
-
+
protected string $type;
-
+
public const TYPE_QUERY = 'query';
public const TYPE_FILTER = 'filter';
@@ -24,9 +23,21 @@ final class Query implements Esable
$this->filter = new BoolQueryCollection();
$this->should = new BoolQueryCollection();
$this->mustNot = new BoolQueryCollection();
+ $this->match = new BoolQueryCollection();
$this->type = self::TYPE_QUERY;
}
+ /**
+ * @param Terms|Nested $item
+ * @return $this
+ */
+ public function match($item): self
+ {
+ $this->match->add($item);
+
+ return $this;
+ }
+
/**
* @param Terms|Nested $item
* @return $this
@@ -34,24 +45,28 @@ final class Query implements Esable
public function must($item): self
{
$this->must->add($item);
+
return $this;
}
public function filter(Esable $item): self
{
$this->filter->add($item);
+
return $this;
}
public function should(Esable $item): self
{
$this->should->add($item);
+
return $this;
}
public function mustNot(Esable $item): self
{
$this->mustNot->add($item);
+
return $this;
}
@@ -62,7 +77,7 @@ final class Query implements Esable
&& $this->filter->isEmpty()
&& $this->should->isEmpty();
}
-
+
public function setType(string $type): self
{
switch($type) {
@@ -84,26 +99,30 @@ final class Query implements Esable
public function es(): array
{
$bool = [];
- if ($this->filter->isEmpty() === false) {
+ if (false === $this->filter->isEmpty()) {
$bool['filter'] = $this->filter->es();
}
- if ($this->must->isEmpty() === false) {
+ if (false === $this->must->isEmpty()) {
$bool['must'] = $this->must->es();
}
- if ($this->mustNot->isEmpty() === false) {
+ if (false === $this->mustNot->isEmpty()) {
$bool['must_not'] = $this->mustNot->es();
}
- if ($this->should->isEmpty() === false) {
+ if (false === $this->should->isEmpty()) {
$bool['should'] = $this->should->es();
}
+ if (false === $this->match->isEmpty()) {
+ $bool['match'] = $this->match->es();
+ }
+
return [
$this->type => [
'bool' => $bool,
- ]
+ ],
];
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/BoolQuery/RangeTerms.php b/src/ElasticSearch/Search/BoolQuery/RangeTerms.php
deleted file mode 100644
index 8491942..0000000
--- a/src/ElasticSearch/Search/BoolQuery/RangeTerms.php
+++ /dev/null
@@ -1,38 +0,0 @@
-key = $key;
- $this->min = $min;
- $this->max = $max;
- }
-
- public function es(): array
- {
- return [
- 'range' => [
- $this->key => [
- 'gte' => $this->min,
- 'lte' => $this->max
- ]
- ]
- ];
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Search/BoolQuery/Stats.php b/src/ElasticSearch/Search/BoolQuery/Stats.php
new file mode 100644
index 0000000..c0e54f7
--- /dev/null
+++ b/src/ElasticSearch/Search/BoolQuery/Stats.php
@@ -0,0 +1,34 @@
+key = $key;
+ $this->value = $value;
+ }
+
+ public function es(): array
+ {
+ $keyStats = is_array($this->value) ? 'range' : 'term';
+
+ return [
+ $keyStats => [
+ $this->key => $this->value,
+ ],
+ ];
+ }
+}
diff --git a/src/ElasticSearch/Search/BoolQuery/Terms.php b/src/ElasticSearch/Search/BoolQuery/Terms.php
index c4ad517..8f66de9 100644
--- a/src/ElasticSearch/Search/BoolQuery/Terms.php
+++ b/src/ElasticSearch/Search/BoolQuery/Terms.php
@@ -2,22 +2,22 @@
namespace IQDEV\ElasticSearch\Search\BoolQuery;
-
use IQDEV\ElasticSearch\Esable;
final class Terms implements Esable
{
private string $key;
- /** @var string|float|string[]|float[] */
+ /**
+ * @var string|float|string[]|float[]
+ */
private $value;
/**
- * @param string $key
* @param string|float|string[]|float[] $value
*/
public function __construct(string $key, $value)
{
- $this->key = $key;
+ $this->key = $key;
$this->value = $value;
}
@@ -27,8 +27,8 @@ final class Terms implements Esable
return [
$keyTerms => [
- $this->key => $this->value
+ $this->key => $this->value,
],
];
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/Nested.php b/src/ElasticSearch/Search/Nested.php
index f0bc805..c2e6c4d 100644
--- a/src/ElasticSearch/Search/Nested.php
+++ b/src/ElasticSearch/Search/Nested.php
@@ -17,15 +17,22 @@ class Nested implements Esable
public function setPath(string $p): self
{
$this->path = $p;
+
return $this;
}
public function setQuery(Query $q): self
{
$this->query = $q;
+
return $this;
}
+ public function getQuery(): ?Query
+ {
+ return $this->query;
+ }
+
public function es(): array
{
$nested = [];
@@ -34,7 +41,7 @@ class Nested implements Esable
$nested['path'] = $this->path;
}
- if (isset($this->query) && $this->query->isEmpty() === false) {
+ if ($this->query && false === $this->query->isEmpty()) {
$nested['query'] = $this->query->es()['query'];
}
diff --git a/src/ElasticSearch/Search/Pagination.php b/src/ElasticSearch/Search/Pagination.php
index 1135315..56832d4 100644
--- a/src/ElasticSearch/Search/Pagination.php
+++ b/src/ElasticSearch/Search/Pagination.php
@@ -6,8 +6,8 @@ use IQDEV\ElasticSearch\Esable;
class Pagination implements Esable
{
- public ?int $size;
- public ?int $from;
+ private ?int $size;
+ private ?int $from;
public function __construct(?int $size = null, ?int $from = null)
{
diff --git a/src/ElasticSearch/Search/Request.php b/src/ElasticSearch/Search/Request.php
index ab423a9..99eb6a0 100644
--- a/src/ElasticSearch/Search/Request.php
+++ b/src/ElasticSearch/Search/Request.php
@@ -3,9 +3,9 @@
namespace IQDEV\ElasticSearch\Search;
use IQDEV\ElasticSearch\Esable;
+use IQDEV\ElasticSearch\Order\OrderCollection;
use IQDEV\ElasticSearch\Search\Aggs\AggsCollection;
use IQDEV\ElasticSearch\Search\BoolQuery\Query;
-use IQDEV\ElasticSearch\Search\Sorting\SortingCollection;
final class Request implements Esable
{
@@ -13,47 +13,29 @@ final class Request implements Esable
private ?Query $postFilter = null;
private ?AggsCollection $aggs = null;
private ?Pagination $pagination = null;
- private ?SortingCollection $sorting = null;
+ private ?OrderCollection $sort = null;
private array $match = [];
+ private ?array $source = null;
- /**
- * @param Pagination|null $pagination
- * @return Request
- */
public function setPagination(?Pagination $pagination): self
{
$this->pagination = $pagination;
- return $this;
- }
- /**
- * @param SortingCollection|null $sorting
- * @return $this
- */
- public function setSorting(?SortingCollection $sorting): self
- {
- $this->sorting = $sorting;
return $this;
}
- /**
- * @return Query
- */
public function getQuery(): Query
{
- if ($this->query === null) {
+ if (null === $this->query) {
$this->query = new Query();
}
return $this->query;
}
- /**
- * @return Query
- */
public function getPostFilter(): Query
{
- if ($this->postFilter === null) {
+ if (null === $this->postFilter) {
$this->postFilter = new Query();
}
@@ -62,50 +44,58 @@ final class Request implements Esable
public function getAggs(): AggsCollection
{
- if ($this->aggs === null) {
+ if (null === $this->aggs) {
$this->aggs = new AggsCollection();
}
return $this->aggs;
}
- /**
- * @return Pagination|null
- */
public function getPagination(): ?Pagination
{
return $this->pagination;
}
- /**
- * @return SortingCollection|null
- */
- public function getSorting(): ?SortingCollection
+ public function addMatch(string $key, array $param): self
{
- return $this->sorting;
+ $this->match[$key] = $param;
+
+ return $this;
}
- public function addMatch(string $key, array $param): self
+ public function setSource(array $s): self
{
- $this->match[$key] = $param;
+ $this->source = $s;
+
return $this;
}
+ public function getSort(): OrderCollection
+ {
+ if (null === $this->sort) {
+ $this->sort = new OrderCollection();
+ }
+
+ return $this->sort;
+ }
+
public function es(): array
{
- $request = [
- '_source' => ['id', 'data.*']
- ];
+ $request = [];
+
+ if ($this->source) {
+ $request['_source'] = $this->source;
+ }
- if (isset($this->postFilter) && $this->postFilter->isEmpty() === false) {
+ if ($this->postFilter && false === $this->postFilter->isEmpty()) {
$request['post_filter'] = $this->postFilter->es()['query'];
}
- if (isset($this->query) && $this->query->isEmpty() === false) {
+ if ($this->query && false === $this->query->isEmpty()) {
$request['query'] = $this->query->es()['query'];
}
- if (empty($this->match) === false) {
+ if (false === empty($this->match)) {
foreach ($this->match as $key => $value) {
$request['query']['match'][$key] = $value;
}
@@ -118,11 +108,11 @@ final class Request implements Esable
if ($this->pagination) {
$request = array_merge($request, $this->pagination->es());
}
-
- if ($this->sorting) {
- $request['sort'] = $this->sorting->es();
+
+ if ($this->sort) {
+ $request['sort'] = $this->sort->es();
}
return $request;
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticSearch/Search/Sorting.php b/src/ElasticSearch/Search/Sorting.php
deleted file mode 100644
index 9f6bb33..0000000
--- a/src/ElasticSearch/Search/Sorting.php
+++ /dev/null
@@ -1,28 +0,0 @@
-by = $by;
- $this->direction = $direction;
- }
-
- public function es(): array
- {
- $sorting = [];
-
- if ($this->by) {
- $sorting['sort'] = [$this->by => $this->direction];
- }
-
- return $sorting;
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Search/Sorting/Sorting.php b/src/ElasticSearch/Search/Sorting/Sorting.php
deleted file mode 100644
index 91f455a..0000000
--- a/src/ElasticSearch/Search/Sorting/Sorting.php
+++ /dev/null
@@ -1,10 +0,0 @@
- $sorting->es(), $this->toArray());
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Search/Sorting/SortingField.php b/src/ElasticSearch/Search/Sorting/SortingField.php
deleted file mode 100644
index 39864c2..0000000
--- a/src/ElasticSearch/Search/Sorting/SortingField.php
+++ /dev/null
@@ -1,18 +0,0 @@
-by = $by;
- }
-
- public function es(): array
- {
- return [$this->by];
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Search/Sorting/SortingFieldsPair.php b/src/ElasticSearch/Search/Sorting/SortingFieldsPair.php
deleted file mode 100644
index dca8569..0000000
--- a/src/ElasticSearch/Search/Sorting/SortingFieldsPair.php
+++ /dev/null
@@ -1,8 +0,0 @@
-by = $by;
- $this->direction = $direction;
- }
-
- public function es(): array
- {
- return [$this->by => $this->direction];
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Search/Sorting/SortingPropertyKeywordPair.php b/src/ElasticSearch/Search/Sorting/SortingPropertyKeywordPair.php
deleted file mode 100644
index cbcd660..0000000
--- a/src/ElasticSearch/Search/Sorting/SortingPropertyKeywordPair.php
+++ /dev/null
@@ -1,27 +0,0 @@
- [
- 'order' => $this->direction,
- 'nested' => [
- 'path' => 'search_data',
- 'filter' => [
- 'bool' => [
- 'must' => [
- 'term' => [
- 'search_data.keyword_facet.facet_code' => $this->by,
- ]
- ],
- ],
- ],
- ],
- ],
- ];
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/Search/Sorting/SortingPropertyNumberPair.php b/src/ElasticSearch/Search/Sorting/SortingPropertyNumberPair.php
deleted file mode 100644
index 2058e72..0000000
--- a/src/ElasticSearch/Search/Sorting/SortingPropertyNumberPair.php
+++ /dev/null
@@ -1,27 +0,0 @@
- [
- 'order' => $this->direction,
- 'nested' => [
- 'path' => 'search_data',
- 'filter' => [
- 'bool' => [
- 'must' => [
- 'term' => [
- 'search_data.number_facet.facet_code' => $this->by,
- ]
- ],
- ],
- ],
- ],
- ],
- ];
- }
-}
\ No newline at end of file
diff --git a/src/ElasticSearch/SearchService.php b/src/ElasticSearch/SearchService.php
new file mode 100644
index 0000000..a1319b9
--- /dev/null
+++ b/src/ElasticSearch/SearchService.php
@@ -0,0 +1,44 @@
+esClient = $esClient;
+ $this->configuration = $configuration;
+
+ $this->criteriaToEsRequest = new CriteriaToEsRequest();
+ $this->esResponseToResult = new EsResponseToResult();
+ }
+
+ /**
+ * @throws ServerResponseException
+ * @throws ClientResponseException
+ */
+ public function search(Criteria $criteria): Result
+ {
+ $request = $this->criteriaToEsRequest->fromCriteria($criteria);
+
+ $response = $this->esClient->search([
+ 'index' => $this->configuration->getIndexName(),
+ 'body' => $request->es(),
+ ]);
+
+ return $this->esResponseToResult->fromResponse($response);
+ }
+}
--
GitLab