From e9bc59206c31d9162473b014fd7dd48261545137 Mon Sep 17 00:00:00 2001 From: Sebastian Kurth <1sebastian1@online.de> Date: Mon, 30 Mar 2026 16:52:53 +0200 Subject: [PATCH 1/6] add OpensearchDataSource add require-dev opensearch-project/opensearch-php --- composer.json | 1 + src/DataSource/OpenSearchDataSource.php | 200 ++++++++++++++++++++++++ src/DataSource/SearchParamsBuilder.php | 2 +- 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/DataSource/OpenSearchDataSource.php diff --git a/composer.json b/composer.json index 130c25202..caa766fc4 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "nette/tester": "^2.3.4", "nextras/dbal": "^4.0 || ^5.0", "nextras/orm": "^4.0 || ^5.0", + "opensearch-project/opensearch-php": "^2.6", "phpstan/phpstan": "^2.1", "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-mockery": "^2.0", diff --git a/src/DataSource/OpenSearchDataSource.php b/src/DataSource/OpenSearchDataSource.php new file mode 100644 index 000000000..d99dac9a6 --- /dev/null +++ b/src/DataSource/OpenSearchDataSource.php @@ -0,0 +1,200 @@ +searchParamsBuilder = new SearchParamsBuilder($indexName, true); + + if ($rowFactory === null) { + $rowFactory = static fn (array $hit): array => $hit['_source']; + } + + $this->rowFactory = $rowFactory; + } + + public function getCount(): int + { + $searchResult = $this->client->search($this->searchParamsBuilder->buildParams()); + + if (!isset($searchResult['hits'])) { + throw new UnexpectedValueException(); + } + $count = $this->client->count($this->searchParamsBuilder->buildParams()); + + return $count['count']; + } + + /** + * {@inheritDoc} + */ + public function getData(): array + { + $searchResult = $this->client->search($this->searchParamsBuilder->buildParams()); + + if (!isset($searchResult['hits'])) { + throw new UnexpectedValueException(); + } + + return array_map($this->rowFactory, $searchResult['hits']['hits']); + } + + /** + * {@inheritDoc} + */ + public function filterOne(array $condition): IDataSource + { + foreach ($condition as $value) { + $this->searchParamsBuilder->addIdsQuery($value); + } + + return $this; + } + + public function limit(int $offset, int $limit): IDataSource + { + $this->searchParamsBuilder->setFrom($offset); + $this->searchParamsBuilder->setSize($limit); + + return $this; + } + + public function applyFilterDate(FilterDate $filter): void + { + foreach ($filter->getCondition() as $column => $value) { + $timestampFrom = null; + $timestampTo = null; + + if ($value) { + try { + $dateFrom = DateTimeHelper::tryConvertToDateTime($value, [$filter->getPhpFormat()]); + $dateFrom->setTime(0, 0, 0); + + $timestampFrom = $dateFrom->getTimestamp(); + + $dateTo = DateTimeHelper::tryConvertToDateTime($value, [$filter->getPhpFormat()]); + $dateTo->setTime(23, 59, 59); + + $timestampTo = $dateTo->getTimestamp(); + + $this->searchParamsBuilder->addRangeQuery($column, $timestampFrom, $timestampTo); + } catch (DatagridDateTimeHelperException) { + // ignore the invalid filter value + } + } + } + } + + public function applyFilterDateRange(FilterDateRange $filter): void + { + foreach ($filter->getCondition() as $column => $values) { + $timestampFrom = null; + $timestampTo = null; + + if ($values['from']) { + try { + $dateFrom = DateTimeHelper::tryConvertToDateTime($values['from'], [$filter->getPhpFormat()]); + $dateFrom->setTime(0, 0, 0); + + $timestampFrom = $dateFrom->getTimestamp(); + } catch (DatagridDateTimeHelperException) { + // ignore the invalid filter value + } + } + + if ($values['to']) { + try { + $dateTo = DateTimeHelper::tryConvertToDateTime($values['to'], [$filter->getPhpFormat()]); + $dateTo->setTime(23, 59, 59); + + $timestampTo = $dateTo->getTimestamp(); + } catch (DatagridDateTimeHelperException) { + // ignore the invalid filter value + } + } + + if (is_int($timestampFrom) || is_int($timestampTo)) { + $this->searchParamsBuilder->addRangeQuery($column, $timestampFrom, $timestampTo); + } + } + } + + public function applyFilterRange(FilterRange $filter): void + { + foreach ($filter->getCondition() as $column => $value) { + $this->searchParamsBuilder->addRangeQuery($column, $value['from'] ?? null, $value['to'] ?? null); + } + } + + public function applyFilterText(FilterText $filter): void + { + foreach ($filter->getCondition() as $column => $value) { + if ($filter->isExactSearch()) { + $this->searchParamsBuilder->addMatchQuery($column, $value); + } else { + $this->searchParamsBuilder->addPhrasePrefixQuery($column, $value); + } + } + } + + public function applyFilterMultiSelect(FilterMultiSelect $filter): void + { + foreach ($filter->getCondition() as $column => $values) { + $this->searchParamsBuilder->addBooleanMatchQuery($column, $values); + } + } + + public function applyFilterSelect(FilterSelect $filter): void + { + foreach ($filter->getCondition() as $column => $value) { + $this->searchParamsBuilder->addMatchQuery($column, $value); + } + } + + /** + * {@inheritDoc} + * + * @throws RuntimeException + */ + public function sort(Sorting $sorting): IDataSource + { + if (is_callable($sorting->getSortCallback())) { + throw new RuntimeException('No can do - not implemented yet'); + } + + foreach ($sorting->getSort() as $column => $order) { + $this->searchParamsBuilder->setSort( + [$column => ['order' => strtolower($order)]] + ); + } + + return $this; + } + + public function getDataSource(): SearchParamsBuilder + { + return $this->searchParamsBuilder; + } + +} diff --git a/src/DataSource/SearchParamsBuilder.php b/src/DataSource/SearchParamsBuilder.php index 8320c7b3c..66efc3184 100644 --- a/src/DataSource/SearchParamsBuilder.php +++ b/src/DataSource/SearchParamsBuilder.php @@ -21,7 +21,7 @@ final class SearchParamsBuilder private array $idsQueries = []; - public function __construct(private string $indexName) + public function __construct(private string $indexName, private bool $isOpensearch = false) { } From c5f386a1744ef4059018412736cabbaebeba73ab Mon Sep 17 00:00:00 2001 From: Sebastian Kurth <1sebastian1@online.de> Date: Mon, 30 Mar 2026 18:07:45 +0200 Subject: [PATCH 2/6] add WildCardSearch for Opensearch --- src/DataSource/OpenSearchDataSource.php | 6 ++++++ src/DataSource/SearchParamsBuilder.php | 27 +++++++++++++++++++++++++ src/Filter/FilterText.php | 27 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/DataSource/OpenSearchDataSource.php b/src/DataSource/OpenSearchDataSource.php index d99dac9a6..93eac5d90 100644 --- a/src/DataSource/OpenSearchDataSource.php +++ b/src/DataSource/OpenSearchDataSource.php @@ -152,6 +152,12 @@ public function applyFilterText(FilterText $filter): void foreach ($filter->getCondition() as $column => $value) { if ($filter->isExactSearch()) { $this->searchParamsBuilder->addMatchQuery($column, $value); + } elseif ($filter->isWildCardSearch()) { + $options = []; + if($filter->isCaseInsensitive()) { + $options['case_insensitive'] = true; + } + $this->searchParamsBuilder->addWildCardQuery($column, $value, $options); } else { $this->searchParamsBuilder->addPhrasePrefixQuery($column, $value); } diff --git a/src/DataSource/SearchParamsBuilder.php b/src/DataSource/SearchParamsBuilder.php index 66efc3184..de06f5185 100644 --- a/src/DataSource/SearchParamsBuilder.php +++ b/src/DataSource/SearchParamsBuilder.php @@ -13,6 +13,8 @@ final class SearchParamsBuilder private array $phrasePrefixQueries = []; + private array $wildCardQueries = []; + private array $matchQueries = []; private array $booleanMatchQueries = []; @@ -50,6 +52,11 @@ public function addIdsQuery(array $ids): void $this->idsQueries[] = $ids; } + public function addWildCardQuery(string $field, string $query, array $options): void + { + $this->wildCardQueries[] = [$field => [$query, $options]]; + } + public function setSort(array $sort): void { $this->sort = $sort; @@ -125,6 +132,26 @@ public function buildParams(): array } } + foreach ($this->wildCardQueries as $wildCardQuery) { + foreach ($wildCardQuery as $field => [$query, $options]) { + + $fieldConfig = [ + 'value' => $query + ]; + if(!(str_contains($query, '*') || str_contains($query, '?'))) { + $query .= '*'; + } + if ($this->isOpensearch && !empty($options)) { + $fieldConfig = array_merge($fieldConfig, $options); + } + $return['body']['query']['bool']['must'][] = [ + 'wildcard' => [ + $field => $fieldConfig, + ], + ]; + } + } + foreach ($this->booleanMatchQueries as $booleanMatchQuery) { foreach ($booleanMatchQuery as $field => $queries) { if ($queries === []) { diff --git a/src/Filter/FilterText.php b/src/Filter/FilterText.php index e5dd27fc2..16f1dd9a3 100644 --- a/src/Filter/FilterText.php +++ b/src/Filter/FilterText.php @@ -14,6 +14,10 @@ class FilterText extends Filter protected bool $exact = false; + protected bool $wildCard = false; + + protected bool $caseInsensitive = false; + protected bool $splitWordsSearch = true; protected bool $conjunctionSearch = false; @@ -96,4 +100,27 @@ public function hasConjunctionSearch(): bool return $this->conjunctionSearch; } + public function setWildCard(bool $wildCard = true): self + { + $this->wildCard = $wildCard; + + return $this; + } + + public function isWildCardSearch(): bool + { + return $this->wildCard; + } + + public function setCaseInsensitive(bool $caseInsensitive = true): self + { + $this->caseInsensitive = $caseInsensitive; + return $this; + } + + public function isCaseInsensitive(): bool + { + return $this->caseInsensitive; + } + } From 6ff12a07ab95fd5f95bfe9933045c37395ad035d Mon Sep 17 00:00:00 2001 From: Sebastian Kurth <1sebastian1@online.de> Date: Tue, 31 Mar 2026 10:05:29 +0200 Subject: [PATCH 3/6] add Tests for WildCardSearch add Case_insentive for exactMatch for OpenSearch --- src/DataSource/OpenSearchDataSource.php | 10 +- src/DataSource/SearchParamsBuilder.php | 34 +-- .../DataSources/SearchParamsBuilderTest.phpt | 230 +++++++++++++++++- 3 files changed, 253 insertions(+), 21 deletions(-) diff --git a/src/DataSource/OpenSearchDataSource.php b/src/DataSource/OpenSearchDataSource.php index 93eac5d90..61b50f277 100644 --- a/src/DataSource/OpenSearchDataSource.php +++ b/src/DataSource/OpenSearchDataSource.php @@ -150,13 +150,13 @@ public function applyFilterRange(FilterRange $filter): void public function applyFilterText(FilterText $filter): void { foreach ($filter->getCondition() as $column => $value) { + $options = []; + if($filter->isCaseInsensitive()) { + $options['case_insensitive'] = true; + } if ($filter->isExactSearch()) { - $this->searchParamsBuilder->addMatchQuery($column, $value); + $this->searchParamsBuilder->addMatchQuery($column, $value, $options); } elseif ($filter->isWildCardSearch()) { - $options = []; - if($filter->isCaseInsensitive()) { - $options['case_insensitive'] = true; - } $this->searchParamsBuilder->addWildCardQuery($column, $value, $options); } else { $this->searchParamsBuilder->addPhrasePrefixQuery($column, $value); diff --git a/src/DataSource/SearchParamsBuilder.php b/src/DataSource/SearchParamsBuilder.php index de06f5185..ac77a1122 100644 --- a/src/DataSource/SearchParamsBuilder.php +++ b/src/DataSource/SearchParamsBuilder.php @@ -32,9 +32,9 @@ public function addPhrasePrefixQuery(string $field, string $query): void $this->phrasePrefixQueries[] = [$field => $query]; } - public function addMatchQuery(string $field, mixed $query): void + public function addMatchQuery(string $field, mixed $query, array $options = []): void { - $this->matchQueries[] = [$field => $query]; + $this->matchQueries[] = [$field => [$query, $options]]; } public function addBooleanMatchQuery(string $field, array $queries): void @@ -52,7 +52,7 @@ public function addIdsQuery(array $ids): void $this->idsQueries[] = $ids; } - public function addWildCardQuery(string $field, string $query, array $options): void + public function addWildCardQuery(string $field, string $query, array $options = []): void { $this->wildCardQueries[] = [$field => [$query, $options]]; } @@ -98,7 +98,8 @@ public function buildParams(): array && $this->matchQueries === [] && $this->booleanMatchQueries === [] && $this->rangeQueries === [] - && $this->idsQueries === []) { + && $this->idsQueries === [] + && $this->wildCardQueries === []) { return $return; } @@ -121,12 +122,16 @@ public function buildParams(): array } foreach ($this->matchQueries as $matchQuery) { - foreach ($matchQuery as $field => $query) { + foreach ($matchQuery as $field => [$query, $options]) { + $fieldQueryParams = [ + 'query' => $query + ]; + if ($this->isOpensearch && count($options) > 0) { + $fieldQueryParams = array_merge($fieldQueryParams, $options); + } $return['body']['query']['bool']['must'][] = [ 'match' => [ - $field => [ - 'query' => $query, - ], + $field => $fieldQueryParams, ], ]; } @@ -134,19 +139,18 @@ public function buildParams(): array foreach ($this->wildCardQueries as $wildCardQuery) { foreach ($wildCardQuery as $field => [$query, $options]) { - - $fieldConfig = [ - 'value' => $query - ]; if(!(str_contains($query, '*') || str_contains($query, '?'))) { $query .= '*'; } - if ($this->isOpensearch && !empty($options)) { - $fieldConfig = array_merge($fieldConfig, $options); + $fieldQueryParams = [ + 'value' => $query + ]; + if (count($options) > 0) { + $fieldQueryParams = array_merge($fieldQueryParams, $options); } $return['body']['query']['bool']['must'][] = [ 'wildcard' => [ - $field => $fieldConfig, + $field => $fieldQueryParams, ], ]; } diff --git a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt index 52620af30..f65056317 100644 --- a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt +++ b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt @@ -15,7 +15,7 @@ final class SearchParamsBuilderTest extends TestCase public function setUp(): void { - $this->searchParamsBuilder = new SearchParamsBuilder('users', 'user'); + $this->searchParamsBuilder = new SearchParamsBuilder('users'); } public function testEmptyQuery(): void @@ -279,6 +279,234 @@ final class SearchParamsBuilderTest extends TestCase ); } + public function testWildCardQuery(): void + { + $this->searchParamsBuilder->addWildCardQuery('name', 'john'); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => 'john*', + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } + + public function testWildCardQuery2(): void + { + $this->searchParamsBuilder->addWildCardQuery('name', 'j?hn'); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => 'j?hn', + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } + + public function testWildCardQuery3(): void + { + $this->searchParamsBuilder->addWildCardQuery('name', '*john'); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => '*john', + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } + + public function testWildCardQueryCaseInsensitive(): void + { + $this->searchParamsBuilder->addWildCardQuery('name', 'john', ['case_insensitive' => true]); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => 'john*', + 'case_insensitive' => true + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } + public function testAllTogetherWithWildCard(): void + { + $this->searchParamsBuilder->setSort(['name' => ['order' => 'desc']]); + $this->searchParamsBuilder->setFrom(0); + $this->searchParamsBuilder->setSize(20); + $this->searchParamsBuilder->addPhrasePrefixQuery('name', 'john'); + $this->searchParamsBuilder->addMatchQuery('name', 'john'); + $this->searchParamsBuilder->addBooleanMatchQuery('status', ['active', 'disabled']); + $this->searchParamsBuilder->addRangeQuery('score', 8, 64); + $this->searchParamsBuilder->addIdsQuery([0, 1, 1, 2, 3, 5, 8]); + $this->searchParamsBuilder->addWildCardQuery('name', 'john', ['case_insensitive' => true]); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'sort' => ['name' => ['order' => 'desc']], + 'from' => 0, + 'size' => 20, + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'multi_match' => [ + 'query' => 'john', + 'type' => 'phrase_prefix', + 'fields' => ['name'], + ], + ], + [ + 'match' => ['name' => ['query' => 'john']], + ], + [ + 'wildcard' => ['name' => ['value' => 'john*', 'case_insensitive' => true]], + ], + [ + 'bool' => [ + 'should' => [ + [ + [ + 'match' => ['status' => ['query' => 'active']], + ], + [ + 'match' => ['status' => ['query' => 'disabled']], + ], + ], + ], + ], + ], + [ + 'range' => ['score' => ['gte' => 8, 'lte' => 64]], + ], + [ + 'ids' => [ + 'values' => [0, 1, 1, 2, 3, 5, 8], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } + public function testMatchQueryOpenSearch(): void + { + $searchBuilder = new SearchParamsBuilder('users', true); + $searchBuilder->addMatchQuery('name', 'john', ['case_insensitive' => true]); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'match' => [ + 'name' => [ + 'query' => 'john', + 'case_insensitive' => true + ], + ], + ], + ], + ], + ], + ], + ], + $searchBuilder->buildParams() + ); + } + public function testMatchQueryElasticSearch(): void + { + $this->searchParamsBuilder->addMatchQuery('name', 'john', ['case_insensitive' => true]); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'match' => [ + 'name' => [ + 'query' => 'john', + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } + } From 14fd6b615993a7d215b36da47bc3f9ded820fefc Mon Sep 17 00:00:00 2001 From: Sebastian Kurth <1sebastian1@online.de> Date: Tue, 31 Mar 2026 10:26:25 +0200 Subject: [PATCH 4/6] remove Case_insentive for exactMatch for OpenSearch add term Query with case_insentive for Opensearch --- src/DataSource/OpenSearchDataSource.php | 2 ++ src/DataSource/SearchParamsBuilder.php | 21 +++++++++++++-- src/Filter/FilterText.php | 15 +++++++++++ .../DataSources/SearchParamsBuilderTest.phpt | 27 ++++++++++++------- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/DataSource/OpenSearchDataSource.php b/src/DataSource/OpenSearchDataSource.php index 61b50f277..bbcf17df5 100644 --- a/src/DataSource/OpenSearchDataSource.php +++ b/src/DataSource/OpenSearchDataSource.php @@ -158,6 +158,8 @@ public function applyFilterText(FilterText $filter): void $this->searchParamsBuilder->addMatchQuery($column, $value, $options); } elseif ($filter->isWildCardSearch()) { $this->searchParamsBuilder->addWildCardQuery($column, $value, $options); + } elseif ($filter->isTermSearch()) { + $this->searchParamsBuilder->addTermQuery($column, $value, $options); } else { $this->searchParamsBuilder->addPhrasePrefixQuery($column, $value); } diff --git a/src/DataSource/SearchParamsBuilder.php b/src/DataSource/SearchParamsBuilder.php index ac77a1122..c1ad1ac1b 100644 --- a/src/DataSource/SearchParamsBuilder.php +++ b/src/DataSource/SearchParamsBuilder.php @@ -23,6 +23,8 @@ final class SearchParamsBuilder private array $idsQueries = []; + private array $termQueries = []; + public function __construct(private string $indexName, private bool $isOpensearch = false) { } @@ -56,6 +58,10 @@ public function addWildCardQuery(string $field, string $query, array $options = { $this->wildCardQueries[] = [$field => [$query, $options]]; } + public function addTermQuery(string $field, string $query, array $options = []): void + { + $this->termQueries[] = [$field => [$query, $options]]; + } public function setSort(array $sort): void { @@ -99,7 +105,8 @@ public function buildParams(): array && $this->booleanMatchQueries === [] && $this->rangeQueries === [] && $this->idsQueries === [] - && $this->wildCardQueries === []) { + && $this->wildCardQueries === [] + && $this->termQueries === [] ) { return $return; } @@ -122,6 +129,16 @@ public function buildParams(): array } foreach ($this->matchQueries as $matchQuery) { + foreach ($matchQuery as $field => [$query, $options]) { + $return['body']['query']['bool']['must'][] = [ + 'match' => [ + $field => ['query' => $query], + ], + ]; + } + } + + foreach ($this->termQueries as $matchQuery) { foreach ($matchQuery as $field => [$query, $options]) { $fieldQueryParams = [ 'query' => $query @@ -130,7 +147,7 @@ public function buildParams(): array $fieldQueryParams = array_merge($fieldQueryParams, $options); } $return['body']['query']['bool']['must'][] = [ - 'match' => [ + 'term' => [ $field => $fieldQueryParams, ], ]; diff --git a/src/Filter/FilterText.php b/src/Filter/FilterText.php index 16f1dd9a3..9ae13b1bd 100644 --- a/src/Filter/FilterText.php +++ b/src/Filter/FilterText.php @@ -18,6 +18,8 @@ class FilterText extends Filter protected bool $caseInsensitive = false; + protected bool $term = false; + protected bool $splitWordsSearch = true; protected bool $conjunctionSearch = false; @@ -123,4 +125,17 @@ public function isCaseInsensitive(): bool return $this->caseInsensitive; } + public function setTermSearch(bool $term = true): self + { + $this->term = $term; + return $this; + } + + public function isTermSearch(): bool + { + return $this->term; + } + + + } diff --git a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt index f65056317..9c8b83e54 100644 --- a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt +++ b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt @@ -387,7 +387,7 @@ final class SearchParamsBuilderTest extends TestCase $this->searchParamsBuilder->buildParams() ); } - public function testAllTogetherWithWildCard(): void + public function testAllTogetherWithWildCardAndTermSearch(): void { $this->searchParamsBuilder->setSort(['name' => ['order' => 'desc']]); $this->searchParamsBuilder->setFrom(0); @@ -398,6 +398,7 @@ final class SearchParamsBuilderTest extends TestCase $this->searchParamsBuilder->addRangeQuery('score', 8, 64); $this->searchParamsBuilder->addIdsQuery([0, 1, 1, 2, 3, 5, 8]); $this->searchParamsBuilder->addWildCardQuery('name', 'john', ['case_insensitive' => true]); + $this->searchParamsBuilder->addTermQuery('name', 'john', ['case_insensitive' => true]); Assert::same( [ @@ -419,8 +420,16 @@ final class SearchParamsBuilderTest extends TestCase [ 'match' => ['name' => ['query' => 'john']], ], - [ - 'wildcard' => ['name' => ['value' => 'john*', 'case_insensitive' => true]], + + [ + 'term' => [ + 'name' => [ + 'query' => 'john', + ], + ], + ], + [ + 'wildcard' => ['name' => ['value' => 'john*', 'case_insensitive' => true]], ], [ 'bool' => [ @@ -452,10 +461,10 @@ final class SearchParamsBuilderTest extends TestCase $this->searchParamsBuilder->buildParams() ); } - public function testMatchQueryOpenSearch(): void + public function testTermQueryOpenSearch(): void { $searchBuilder = new SearchParamsBuilder('users', true); - $searchBuilder->addMatchQuery('name', 'john', ['case_insensitive' => true]); + $searchBuilder->addTermQuery('name', 'john', ['case_insensitive' => true]); Assert::same( [ @@ -465,7 +474,7 @@ final class SearchParamsBuilderTest extends TestCase 'bool' => [ 'must' => [ [ - 'match' => [ + 'term' => [ 'name' => [ 'query' => 'john', 'case_insensitive' => true @@ -480,9 +489,9 @@ final class SearchParamsBuilderTest extends TestCase $searchBuilder->buildParams() ); } - public function testMatchQueryElasticSearch(): void + public function testTermQueryElasticSearch(): void { - $this->searchParamsBuilder->addMatchQuery('name', 'john', ['case_insensitive' => true]); + $this->searchParamsBuilder->addTermQuery('name', 'john', ['case_insensitive' => true]); Assert::same( [ @@ -492,7 +501,7 @@ final class SearchParamsBuilderTest extends TestCase 'bool' => [ 'must' => [ [ - 'match' => [ + 'term' => [ 'name' => [ 'query' => 'john', ], From 511b15ded1b1634465264f4d47deba755fb61077 Mon Sep 17 00:00:00 2001 From: Sebastian Kurth <1sebastian1@online.de> Date: Tue, 31 Mar 2026 10:33:29 +0200 Subject: [PATCH 5/6] fix term Query --- src/DataSource/OpenSearchDataSource.php | 2 +- src/DataSource/SearchParamsBuilder.php | 8 ++++---- tests/Cases/DataSources/SearchParamsBuilderTest.phpt | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/DataSource/OpenSearchDataSource.php b/src/DataSource/OpenSearchDataSource.php index bbcf17df5..dc5c1734e 100644 --- a/src/DataSource/OpenSearchDataSource.php +++ b/src/DataSource/OpenSearchDataSource.php @@ -155,7 +155,7 @@ public function applyFilterText(FilterText $filter): void $options['case_insensitive'] = true; } if ($filter->isExactSearch()) { - $this->searchParamsBuilder->addMatchQuery($column, $value, $options); + $this->searchParamsBuilder->addMatchQuery($column, $value); } elseif ($filter->isWildCardSearch()) { $this->searchParamsBuilder->addWildCardQuery($column, $value, $options); } elseif ($filter->isTermSearch()) { diff --git a/src/DataSource/SearchParamsBuilder.php b/src/DataSource/SearchParamsBuilder.php index c1ad1ac1b..dcbcc0dcc 100644 --- a/src/DataSource/SearchParamsBuilder.php +++ b/src/DataSource/SearchParamsBuilder.php @@ -34,9 +34,9 @@ public function addPhrasePrefixQuery(string $field, string $query): void $this->phrasePrefixQueries[] = [$field => $query]; } - public function addMatchQuery(string $field, mixed $query, array $options = []): void + public function addMatchQuery(string $field, mixed $query): void { - $this->matchQueries[] = [$field => [$query, $options]]; + $this->matchQueries[] = [$field => $query ]; } public function addBooleanMatchQuery(string $field, array $queries): void @@ -129,7 +129,7 @@ public function buildParams(): array } foreach ($this->matchQueries as $matchQuery) { - foreach ($matchQuery as $field => [$query, $options]) { + foreach ($matchQuery as $field => $query) { $return['body']['query']['bool']['must'][] = [ 'match' => [ $field => ['query' => $query], @@ -141,7 +141,7 @@ public function buildParams(): array foreach ($this->termQueries as $matchQuery) { foreach ($matchQuery as $field => [$query, $options]) { $fieldQueryParams = [ - 'query' => $query + 'value' => $query ]; if ($this->isOpensearch && count($options) > 0) { $fieldQueryParams = array_merge($fieldQueryParams, $options); diff --git a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt index 9c8b83e54..29844317c 100644 --- a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt +++ b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt @@ -424,7 +424,7 @@ final class SearchParamsBuilderTest extends TestCase [ 'term' => [ 'name' => [ - 'query' => 'john', + 'value' => 'john', ], ], ], @@ -476,7 +476,7 @@ final class SearchParamsBuilderTest extends TestCase [ 'term' => [ 'name' => [ - 'query' => 'john', + 'value' => 'john', 'case_insensitive' => true ], ], @@ -503,7 +503,7 @@ final class SearchParamsBuilderTest extends TestCase [ 'term' => [ 'name' => [ - 'query' => 'john', + 'value' => 'john', ], ], ], From 65738f22ca6907a963fe6825ca9fb8b9d8c9c16d Mon Sep 17 00:00:00 2001 From: Sebastian Kurth <1sebastian1@online.de> Date: Tue, 31 Mar 2026 10:58:43 +0200 Subject: [PATCH 6/6] fix Codesniffer warnings --- src/DataSource/OpenSearchDataSource.php | 6 +- src/DataSource/SearchParamsBuilder.php | 16 +- src/Filter/FilterText.php | 4 +- .../DataSources/SearchParamsBuilderTest.phpt | 227 +++++++++--------- 4 files changed, 131 insertions(+), 122 deletions(-) diff --git a/src/DataSource/OpenSearchDataSource.php b/src/DataSource/OpenSearchDataSource.php index dc5c1734e..3f0611faf 100644 --- a/src/DataSource/OpenSearchDataSource.php +++ b/src/DataSource/OpenSearchDataSource.php @@ -41,7 +41,8 @@ public function getCount(): int if (!isset($searchResult['hits'])) { throw new UnexpectedValueException(); } - $count = $this->client->count($this->searchParamsBuilder->buildParams()); + + $count = $this->client->count($this->searchParamsBuilder->buildParams()); return $count['count']; } @@ -151,9 +152,10 @@ public function applyFilterText(FilterText $filter): void { foreach ($filter->getCondition() as $column => $value) { $options = []; - if($filter->isCaseInsensitive()) { + if ($filter->isCaseInsensitive()) { $options['case_insensitive'] = true; } + if ($filter->isExactSearch()) { $this->searchParamsBuilder->addMatchQuery($column, $value); } elseif ($filter->isWildCardSearch()) { diff --git a/src/DataSource/SearchParamsBuilder.php b/src/DataSource/SearchParamsBuilder.php index dcbcc0dcc..9e52bb34a 100644 --- a/src/DataSource/SearchParamsBuilder.php +++ b/src/DataSource/SearchParamsBuilder.php @@ -36,7 +36,7 @@ public function addPhrasePrefixQuery(string $field, string $query): void public function addMatchQuery(string $field, mixed $query): void { - $this->matchQueries[] = [$field => $query ]; + $this->matchQueries[] = [$field => $query]; } public function addBooleanMatchQuery(string $field, array $queries): void @@ -58,6 +58,7 @@ public function addWildCardQuery(string $field, string $query, array $options = { $this->wildCardQueries[] = [$field => [$query, $options]]; } + public function addTermQuery(string $field, string $query, array $options = []): void { $this->termQueries[] = [$field => [$query, $options]]; @@ -105,8 +106,8 @@ public function buildParams(): array && $this->booleanMatchQueries === [] && $this->rangeQueries === [] && $this->idsQueries === [] - && $this->wildCardQueries === [] - && $this->termQueries === [] ) { + && $this->wildCardQueries === [] + && $this->termQueries === []) { return $return; } @@ -141,11 +142,12 @@ public function buildParams(): array foreach ($this->termQueries as $matchQuery) { foreach ($matchQuery as $field => [$query, $options]) { $fieldQueryParams = [ - 'value' => $query + 'value' => $query, ]; if ($this->isOpensearch && count($options) > 0) { $fieldQueryParams = array_merge($fieldQueryParams, $options); } + $return['body']['query']['bool']['must'][] = [ 'term' => [ $field => $fieldQueryParams, @@ -156,15 +158,17 @@ public function buildParams(): array foreach ($this->wildCardQueries as $wildCardQuery) { foreach ($wildCardQuery as $field => [$query, $options]) { - if(!(str_contains($query, '*') || str_contains($query, '?'))) { + if (!(str_contains($query, '*') || str_contains($query, '?'))) { $query .= '*'; } + $fieldQueryParams = [ - 'value' => $query + 'value' => $query, ]; if (count($options) > 0) { $fieldQueryParams = array_merge($fieldQueryParams, $options); } + $return['body']['query']['bool']['must'][] = [ 'wildcard' => [ $field => $fieldQueryParams, diff --git a/src/Filter/FilterText.php b/src/Filter/FilterText.php index 9ae13b1bd..e4ee7d10e 100644 --- a/src/Filter/FilterText.php +++ b/src/Filter/FilterText.php @@ -117,6 +117,7 @@ public function isWildCardSearch(): bool public function setCaseInsensitive(bool $caseInsensitive = true): self { $this->caseInsensitive = $caseInsensitive; + return $this; } @@ -128,6 +129,7 @@ public function isCaseInsensitive(): bool public function setTermSearch(bool $term = true): self { $this->term = $term; + return $this; } @@ -136,6 +138,4 @@ public function isTermSearch(): bool return $this->term; } - - } diff --git a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt index 29844317c..0e92b6ba9 100644 --- a/tests/Cases/DataSources/SearchParamsBuilderTest.phpt +++ b/tests/Cases/DataSources/SearchParamsBuilderTest.phpt @@ -280,113 +280,114 @@ final class SearchParamsBuilderTest extends TestCase } public function testWildCardQuery(): void - { - $this->searchParamsBuilder->addWildCardQuery('name', 'john'); - - Assert::same( - [ - 'index' => 'users', - 'body' => [ - 'query' => [ - 'bool' => [ - 'must' => [ - [ - 'wildcard' => [ - 'name' => [ - 'value' => 'john*', - ], - ], - ], - ], - ], - ], - ], - ], - $this->searchParamsBuilder->buildParams() - ); - } + { + $this->searchParamsBuilder->addWildCardQuery('name', 'john'); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => 'john*', + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } public function testWildCardQuery2(): void - { - $this->searchParamsBuilder->addWildCardQuery('name', 'j?hn'); - - Assert::same( - [ - 'index' => 'users', - 'body' => [ - 'query' => [ - 'bool' => [ - 'must' => [ - [ - 'wildcard' => [ - 'name' => [ - 'value' => 'j?hn', - ], - ], - ], - ], - ], - ], - ], - ], - $this->searchParamsBuilder->buildParams() - ); - } + { + $this->searchParamsBuilder->addWildCardQuery('name', 'j?hn'); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => 'j?hn', + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } public function testWildCardQuery3(): void - { - $this->searchParamsBuilder->addWildCardQuery('name', '*john'); - - Assert::same( - [ - 'index' => 'users', - 'body' => [ - 'query' => [ - 'bool' => [ - 'must' => [ - [ - 'wildcard' => [ - 'name' => [ - 'value' => '*john', - ], - ], - ], - ], - ], - ], - ], - ], - $this->searchParamsBuilder->buildParams() - ); - } + { + $this->searchParamsBuilder->addWildCardQuery('name', '*john'); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => '*john', + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } public function testWildCardQueryCaseInsensitive(): void - { - $this->searchParamsBuilder->addWildCardQuery('name', 'john', ['case_insensitive' => true]); - - Assert::same( - [ - 'index' => 'users', - 'body' => [ - 'query' => [ - 'bool' => [ - 'must' => [ - [ - 'wildcard' => [ - 'name' => [ - 'value' => 'john*', - 'case_insensitive' => true - ], - ], - ], - ], - ], - ], - ], - ], - $this->searchParamsBuilder->buildParams() - ); - } + { + $this->searchParamsBuilder->addWildCardQuery('name', 'john', ['case_insensitive' => true]); + + Assert::same( + [ + 'index' => 'users', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'wildcard' => [ + 'name' => [ + 'value' => 'john*', + 'case_insensitive' => true, + ], + ], + ], + ], + ], + ], + ], + ], + $this->searchParamsBuilder->buildParams() + ); + } + public function testAllTogetherWithWildCardAndTermSearch(): void { $this->searchParamsBuilder->setSort(['name' => ['order' => 'desc']]); @@ -421,16 +422,16 @@ final class SearchParamsBuilderTest extends TestCase 'match' => ['name' => ['query' => 'john']], ], - [ - 'term' => [ - 'name' => [ - 'value' => 'john', - ], - ], - ], - [ - 'wildcard' => ['name' => ['value' => 'john*', 'case_insensitive' => true]], - ], + [ + 'term' => [ + 'name' => [ + 'value' => 'john', + ], + ], + ], + [ + 'wildcard' => ['name' => ['value' => 'john*', 'case_insensitive' => true]], + ], [ 'bool' => [ 'should' => [ @@ -461,6 +462,7 @@ final class SearchParamsBuilderTest extends TestCase $this->searchParamsBuilder->buildParams() ); } + public function testTermQueryOpenSearch(): void { $searchBuilder = new SearchParamsBuilder('users', true); @@ -477,7 +479,7 @@ final class SearchParamsBuilderTest extends TestCase 'term' => [ 'name' => [ 'value' => 'john', - 'case_insensitive' => true + 'case_insensitive' => true, ], ], ], @@ -489,6 +491,7 @@ final class SearchParamsBuilderTest extends TestCase $searchBuilder->buildParams() ); } + public function testTermQueryElasticSearch(): void { $this->searchParamsBuilder->addTermQuery('name', 'john', ['case_insensitive' => true]);