From 40f35f14c00e31c437059fe77e61716bf6986b71 Mon Sep 17 00:00:00 2001 From: Dmitry Mamchyts Date: Thu, 18 Nov 2021 01:33:26 +0300 Subject: [PATCH 1/2] Add support of terms set query (fix issues #2019) --- CHANGELOG.md | 1 + src/Query/TermsSet.php | 63 ++++++++++++ tests/Query/TermsSetTest.php | 184 +++++++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 src/Query/TermsSet.php create mode 100644 tests/Query/TermsSetTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index cfd212e945..ed944bfc26 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `bytes` processor [#2008](https://github.com/ruflin/Elastica/pull/2008) * Added `indices_boost` option to `Elastica\Query` [#2018](https://github.com/ruflin/Elastica/pull/2018) * Added `Elastica\Query\Terms::setBoost()` method to configure boost [#2035](https://github.com/ruflin/Elastica/pull/2035) +* Added `Elastica\Query\TermsSet` query [#2020](https://github.com/ruflin/Elastica/pull/2020) * Allowed to configure a sub key when adding a param with `Elastica\Param::addParam()` [#2030](https://github.com/ruflin/Elastica/pull/2030) ### Changed * Triggered deprecation in `Elastica\Result::getType()` method [#2016](https://github.com/ruflin/Elastica/pull/2016) diff --git a/src/Query/TermsSet.php b/src/Query/TermsSet.php new file mode 100644 index 0000000000..aa35d292f8 --- /dev/null +++ b/src/Query/TermsSet.php @@ -0,0 +1,63 @@ + $terms + */ + public function __construct(string $field, array $terms) + { + if ('' === $field) { + throw new InvalidException('Terms field name has to be set'); + } + + if (0 === \count($terms)) { + throw new InvalidException('Unable to build Terms query: terms must contains at least one item'); + } + + $this->setParam($field, ['terms' => $terms]); + } + + public function setMinimumShouldMatchField(string $minimumShouldMatchField): self + { + $params = $this->getParams(); + $field = \array_key_first($params); + + $this->setParam($field, \array_merge($params[$field], ['minimum_should_match_field' => $minimumShouldMatchField])); + + return $this; + } + + public function setMinimumShouldMatchScript(AbstractScript $script): self + { + $params = $this->getParams(); + $field = \array_key_first($params); + + $this->setParam($field, \array_merge($params[$field], ['minimum_should_match_script' => $script->toArray()['script']])); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function toArray(): array + { + $params = $this->getParams(); + $field = \array_key_first($params); + + if (!isset($params[$field]['minimum_should_match_field']) && !isset($params[$field]['minimum_should_match_script'])) { + throw new InvalidException('One minimum should match criteria must be specified'); + } + + return parent::toArray(); + } +} diff --git a/tests/Query/TermsSetTest.php b/tests/Query/TermsSetTest.php new file mode 100644 index 0000000000..9fad10a24f --- /dev/null +++ b/tests/Query/TermsSetTest.php @@ -0,0 +1,184 @@ +expectException(InvalidException::class); + $query = new TermsSet('field', []); + } + + /** + * @group unit + */ + public function testEmptyField(): void + { + $this->expectException(InvalidException::class); + new TermsSet('', [1]); + } + + /** + * @group unit + */ + public function testEmptyMinimumFields(): void + { + $this->expectException(InvalidException::class); + (new TermsSet('field', ['php']))->toArray(); + } + + /** + * @group functional + */ + public function testMinimumShouldMatchScriptSearch(): void + { + $index = $this->_createIndex(); + + $index->addDocuments([ + new Document(1, ['skills' => ['php', 'js']]), + new Document(2, ['skills' => ['php']]), + new Document(3, ['skills' => ['java']]), + ]); + + $index->refresh(); + + $query = new TermsSet('skills', ['php']); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + + $this->assertEquals(2, $resultSet->count()); + + $query = new TermsSet('skills', ['php', 'java']); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + + $this->assertEquals(3, $resultSet->count()); + + $query = new TermsSet('skills', ['php', 'js']); + $query->setMinimumShouldMatchScript(new Script('2')); + $resultSet = $index->search($query); + + $this->assertEquals(1, $resultSet->count()); + + $query = new TermsSet('skills', ['php', 'java']); + $query->setMinimumShouldMatchScript(new Script('2')); + $resultSet = $index->search($query); + + $this->assertEquals(0, $resultSet->count()); + } + + /** + * @group functional + */ + public function testMinimumShouldMatchFieldSearch(): void + { + $index = $this->_createIndex(); + + $index->addDocuments([ + new Document(1, ['skill_count' => 2, 'skills' => ['php', 'js']]), + new Document(2, ['skill_count' => 1, 'skills' => ['php']]), + new Document(3, ['skill_count' => 1, 'skills' => ['java']]), + ]); + + $index->refresh(); + + $query = new TermsSet('skills', ['php']); + $query->setMinimumShouldMatchField('skill_count'); + $resultSet = $index->search($query); + + $this->assertEquals(1, $resultSet->count()); + + $query = new TermsSet('skills', ['php', 'java']); + $query->setMinimumShouldMatchField('skill_count'); + $resultSet = $index->search($query); + + $this->assertEquals(2, $resultSet->count()); + + $query = new TermsSet('skills', ['php', 'js']); + $query->setMinimumShouldMatchField('skill_count'); + $resultSet = $index->search($query); + + $this->assertEquals(2, $resultSet->count()); + + $query = new TermsSet('skills', ['js', 'c++']); + $query->setMinimumShouldMatchField('skill_count'); + $resultSet = $index->search($query); + + $this->assertEquals(0, $resultSet->count()); + } + + /** + * @group functional + */ + public function testVariousDataTypesViaConstructor(): void + { + $index = $this->_createIndex(); + + $index->addDocuments([ + new Document(1, ['some_numeric_field' => 9876]), + ]); + $index->refresh(); + + // string + $query = new TermsSet('some_numeric_field', ['9876']); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + $this->assertEquals(1, $resultSet->count()); + + // int + $query = new TermsSet('some_numeric_field', [9876]); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + $this->assertEquals(1, $resultSet->count()); + + // float + $query = new TermsSet('some_numeric_field', [9876.0]); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + $this->assertEquals(1, $resultSet->count()); + } + + /** + * @group functional + */ + public function testVariousDataTypesViaAddTerm(): void + { + $index = $this->_createIndex(); + + $index->addDocuments([ + new Document(1, ['some_numeric_field' => 9876]), + ]); + $index->refresh(); + + // string + $query = new TermsSet('some_numeric_field', ['9876']); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + $this->assertEquals(1, $resultSet->count()); + + // int + $query = new TermsSet('some_numeric_field', [9876]); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + $this->assertEquals(1, $resultSet->count()); + + // float + $query = new TermsSet('some_numeric_field', [9876.0]); + $query->setMinimumShouldMatchScript(new Script('1')); + $resultSet = $index->search($query); + $this->assertEquals(1, $resultSet->count()); + } +} From b086384abcaa231529a18322bf09181d38bb61a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20de=20Guillebon?= Date: Fri, 3 Dec 2021 16:43:52 +0100 Subject: [PATCH 2/2] Use addParam() with sub key --- src/Query/TermsSet.php | 58 +++++++++++------------ tests/Query/TermsSetTest.php | 91 +++++++++++------------------------- 2 files changed, 52 insertions(+), 97 deletions(-) diff --git a/src/Query/TermsSet.php b/src/Query/TermsSet.php index aa35d292f8..ab52d42db0 100644 --- a/src/Query/TermsSet.php +++ b/src/Query/TermsSet.php @@ -10,54 +10,48 @@ */ class TermsSet extends AbstractQuery { + /** + * @var string + */ + private $field; + /** * @param array $terms + * @param AbstractScript|string $minimumShouldMatch */ - public function __construct(string $field, array $terms) + public function __construct(string $field, array $terms, $minimumShouldMatch) { if ('' === $field) { - throw new InvalidException('Terms field name has to be set'); + throw new InvalidException('TermsSet field name has to be set'); } - if (0 === \count($terms)) { - throw new InvalidException('Unable to build Terms query: terms must contains at least one item'); - } + $this->field = $field; + $this->setTerms($terms); - $this->setParam($field, ['terms' => $terms]); + if (\is_string($minimumShouldMatch)) { + $this->setMinimumShouldMatchField($minimumShouldMatch); + } elseif ($minimumShouldMatch instanceof AbstractScript) { + $this->setMinimumShouldMatchScript($minimumShouldMatch); + } else { + throw new \TypeError(\sprintf('Argument 3 passed to "%s()" must be of type %s|string, %s given.', __METHOD__, AbstractScript::class, \is_object($minimumShouldMatch) ? \get_class($minimumShouldMatch) : \gettype($minimumShouldMatch))); + } } - public function setMinimumShouldMatchField(string $minimumShouldMatchField): self + /** + * @param array $terms + */ + public function setTerms(array $terms): self { - $params = $this->getParams(); - $field = \array_key_first($params); - - $this->setParam($field, \array_merge($params[$field], ['minimum_should_match_field' => $minimumShouldMatchField])); - - return $this; + return $this->addParam($this->field, $terms, 'terms'); } - public function setMinimumShouldMatchScript(AbstractScript $script): self + public function setMinimumShouldMatchField(string $minimumShouldMatchField): self { - $params = $this->getParams(); - $field = \array_key_first($params); - - $this->setParam($field, \array_merge($params[$field], ['minimum_should_match_script' => $script->toArray()['script']])); - - return $this; + return $this->addParam($this->field, $minimumShouldMatchField, 'minimum_should_match_field'); } - /** - * {@inheritdoc} - */ - public function toArray(): array + public function setMinimumShouldMatchScript(AbstractScript $script): self { - $params = $this->getParams(); - $field = \array_key_first($params); - - if (!isset($params[$field]['minimum_should_match_field']) && !isset($params[$field]['minimum_should_match_script'])) { - throw new InvalidException('One minimum should match criteria must be specified'); - } - - return parent::toArray(); + return $this->addParam($this->field, $script->toArray()['script'], 'minimum_should_match_script'); } } diff --git a/tests/Query/TermsSetTest.php b/tests/Query/TermsSetTest.php index 9fad10a24f..ff9343487c 100644 --- a/tests/Query/TermsSetTest.php +++ b/tests/Query/TermsSetTest.php @@ -16,10 +16,20 @@ class TermsSetTest extends BaseTest /** * @group unit */ - public function testEmptyTerms(): void + public function testMinimumShouldMatchField(): void { - $this->expectException(InvalidException::class); - $query = new TermsSet('field', []); + $expected = [ + 'terms_set' => [ + 'field' => [ + 'terms' => ['foo', 'bar'], + 'minimum_should_match_field' => 'match_field', + ], + ], + ]; + + $query = new TermsSet('field', ['foo', 'bar'], 'match_field'); + + $this->assertSame($expected, $query->toArray()); } /** @@ -28,16 +38,9 @@ public function testEmptyTerms(): void public function testEmptyField(): void { $this->expectException(InvalidException::class); - new TermsSet('', [1]); - } + $this->expectExceptionMessage('TermsSet field name has to be set'); - /** - * @group unit - */ - public function testEmptyMinimumFields(): void - { - $this->expectException(InvalidException::class); - (new TermsSet('field', ['php']))->toArray(); + new TermsSet('', ['foo', 'bar'], 'match_field'); } /** @@ -55,26 +58,22 @@ public function testMinimumShouldMatchScriptSearch(): void $index->refresh(); - $query = new TermsSet('skills', ['php']); - $query->setMinimumShouldMatchScript(new Script('1')); + $query = new TermsSet('skills', ['php'], new Script('1')); $resultSet = $index->search($query); $this->assertEquals(2, $resultSet->count()); - $query = new TermsSet('skills', ['php', 'java']); - $query->setMinimumShouldMatchScript(new Script('1')); + $query = new TermsSet('skills', ['php', 'java'], new Script('1')); $resultSet = $index->search($query); $this->assertEquals(3, $resultSet->count()); - $query = new TermsSet('skills', ['php', 'js']); - $query->setMinimumShouldMatchScript(new Script('2')); + $query = new TermsSet('skills', ['php', 'js'], new Script('2')); $resultSet = $index->search($query); $this->assertEquals(1, $resultSet->count()); - $query = new TermsSet('skills', ['php', 'java']); - $query->setMinimumShouldMatchScript(new Script('2')); + $query = new TermsSet('skills', ['php', 'java'], new Script('2')); $resultSet = $index->search($query); $this->assertEquals(0, $resultSet->count()); @@ -95,26 +94,22 @@ public function testMinimumShouldMatchFieldSearch(): void $index->refresh(); - $query = new TermsSet('skills', ['php']); - $query->setMinimumShouldMatchField('skill_count'); + $query = new TermsSet('skills', ['php'], 'skill_count'); $resultSet = $index->search($query); $this->assertEquals(1, $resultSet->count()); - $query = new TermsSet('skills', ['php', 'java']); - $query->setMinimumShouldMatchField('skill_count'); + $query = new TermsSet('skills', ['php', 'java'], 'skill_count'); $resultSet = $index->search($query); $this->assertEquals(2, $resultSet->count()); - $query = new TermsSet('skills', ['php', 'js']); - $query->setMinimumShouldMatchField('skill_count'); + $query = new TermsSet('skills', ['php', 'js'], 'skill_count'); $resultSet = $index->search($query); $this->assertEquals(2, $resultSet->count()); - $query = new TermsSet('skills', ['js', 'c++']); - $query->setMinimumShouldMatchField('skill_count'); + $query = new TermsSet('skills', ['js', 'c++'], 'skill_count'); $resultSet = $index->search($query); $this->assertEquals(0, $resultSet->count()); @@ -133,51 +128,17 @@ public function testVariousDataTypesViaConstructor(): void $index->refresh(); // string - $query = new TermsSet('some_numeric_field', ['9876']); - $query->setMinimumShouldMatchScript(new Script('1')); - $resultSet = $index->search($query); - $this->assertEquals(1, $resultSet->count()); - - // int - $query = new TermsSet('some_numeric_field', [9876]); - $query->setMinimumShouldMatchScript(new Script('1')); - $resultSet = $index->search($query); - $this->assertEquals(1, $resultSet->count()); - - // float - $query = new TermsSet('some_numeric_field', [9876.0]); - $query->setMinimumShouldMatchScript(new Script('1')); - $resultSet = $index->search($query); - $this->assertEquals(1, $resultSet->count()); - } - - /** - * @group functional - */ - public function testVariousDataTypesViaAddTerm(): void - { - $index = $this->_createIndex(); - - $index->addDocuments([ - new Document(1, ['some_numeric_field' => 9876]), - ]); - $index->refresh(); - - // string - $query = new TermsSet('some_numeric_field', ['9876']); - $query->setMinimumShouldMatchScript(new Script('1')); + $query = new TermsSet('some_numeric_field', ['9876'], new Script('1')); $resultSet = $index->search($query); $this->assertEquals(1, $resultSet->count()); // int - $query = new TermsSet('some_numeric_field', [9876]); - $query->setMinimumShouldMatchScript(new Script('1')); + $query = new TermsSet('some_numeric_field', [9876], new Script('1')); $resultSet = $index->search($query); $this->assertEquals(1, $resultSet->count()); // float - $query = new TermsSet('some_numeric_field', [9876.0]); - $query->setMinimumShouldMatchScript(new Script('1')); + $query = new TermsSet('some_numeric_field', [9876.0], new Script('1')); $resultSet = $index->search($query); $this->assertEquals(1, $resultSet->count()); }