diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec037e..30339f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,14 @@ ## Unreleased ### Changed - **BC BREAK** | `UserRecommendation` now returns new format of response in `->getData()`, which is a list of `stdClass` instances. +- **BC BREAK** | `UserRecommendation` does not have default filter (was previously set to: `valid_to >= NOW`). ### Fixed - Exceptions occurring during async request now generate rejected promise (as they should) and are no longer thrown directly. ### Added - `UserRecommendation` now sends which item properties should be returned alongside with item_ids. +- **BC BREAK** | `UserRecommendation` now uses MQL query language by default for filtering. ## 1.6.0 - 2018-06-01 ### Added diff --git a/README.md b/README.md index f4f6b1a..28f3ed5 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ You can also set more granular options of the recommendation command: ```php $recommendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600); -$recommendation->setFilters(['valid_to >= NOW']) // Note this filter is present by default +$recommendation->setFilters(['for_recommendation = 1']) ->setMinimalRelevance(UserRecommendation::MINIMAL_RELEVANCE_HIGH) ->enableHardRotation(); diff --git a/src/Model/Command/UserRecommendation.php b/src/Model/Command/UserRecommendation.php index d52d313..949076e 100644 --- a/src/Model/Command/UserRecommendation.php +++ b/src/Model/Command/UserRecommendation.php @@ -12,6 +12,8 @@ class UserRecommendation extends AbstractCommand implements UserAwareInterface public const MINIMAL_RELEVANCE_LOW = 'low'; public const MINIMAL_RELEVANCE_MEDIUM = 'medium'; public const MINIMAL_RELEVANCE_HIGH = 'high'; + public const FILTER_TYPE_RGX = 'rgx'; + public const FILTER_TYPE_MQL = 'mql'; /** @var string */ protected $filterOperator = 'and'; @@ -30,7 +32,9 @@ class UserRecommendation extends AbstractCommand implements UserAwareInterface /** @var string */ private $minimalRelevance = self::MINIMAL_RELEVANCE_LOW; /** @var string[] */ - private $filters = ['valid_to >= NOW']; + private $filters = []; + /** @var string */ + private $filterType = self::FILTER_TYPE_MQL; /** @var string|null */ private $modelName = null; /** @var string[] */ @@ -109,7 +113,7 @@ public function setMinimalRelevance(string $minimalRelevance): self } /** - * Add a filter to already added filters (including the default filter). + * Add a filter(s) to recommendation request. This can be called multiple times. * * @return $this */ @@ -121,7 +125,7 @@ public function addFilter(string $filter): self } /** - * Overwrite all filters by custom one. Note this will override also the default filter. + * Overwrite all filters by custom one. * * @return $this */ @@ -134,6 +138,22 @@ public function setFilters(array $filters): self return $this; } + /** + * Specify the filter type being used. + */ + public function setFilterType(string $filterType): self + { + Assertion::typeIdentifier($filterType); + Assertion::choice( + $filterType, + [static::FILTER_TYPE_RGX, static::FILTER_TYPE_MQL] + ); + + $this->filterType = $filterType; + + return $this; + } + /** * Specify which item property you want returned. */ @@ -236,9 +256,14 @@ protected function getCommandParameters(): array 'hard_rotation' => $this->hardRotation, 'min_relevance' => $this->minimalRelevance, 'filter' => $this->assembleFiltersString(), + 'filter_type' => $this->filterType, 'properties' => $this->responseProperties, ]; + if ($this->filterType === self::FILTER_TYPE_RGX) { + unset($parameters['filter_type']); + } + if ($this->modelName !== null) { $parameters['model_name'] = $this->modelName; } diff --git a/tests/unit/Model/Command/UserRecommendationTest.php b/tests/unit/Model/Command/UserRecommendationTest.php index dfe475f..e053183 100644 --- a/tests/unit/Model/Command/UserRecommendationTest.php +++ b/tests/unit/Model/Command/UserRecommendationTest.php @@ -23,7 +23,8 @@ public function shouldBeInstantiableViaNamedConstructorWithDefaultValues(): void 'rotation_time' => 3600, 'hard_rotation' => false, 'min_relevance' => UserRecommendation::MINIMAL_RELEVANCE_LOW, - 'filter' => 'valid_to >= NOW', + 'filter' => '', + 'filter_type' => UserRecommendation::FILTER_TYPE_MQL, 'properties' => [], // intentionally no model name ==> should be absent when not used ], @@ -48,6 +49,7 @@ public function shouldUseCustomParameters(): void $command->setMinimalRelevance(UserRecommendation::MINIMAL_RELEVANCE_HIGH) ->enableHardRotation() ->setFilters(['foo = bar', 'baz = ban']) + ->setFilterType(UserRecommendation::FILTER_TYPE_RGX) ->setModelName($modelName); $this->assertInstanceOf(UserRecommendation::class, $command); @@ -63,6 +65,7 @@ public function shouldUseCustomParameters(): void 'hard_rotation' => true, 'min_relevance' => UserRecommendation::MINIMAL_RELEVANCE_HIGH, 'filter' => 'foo = bar and baz = ban', + // when a filter type is RGX, the parameter is absent. 'properties' => [], 'model_name' => $modelName, ], @@ -72,19 +75,19 @@ public function shouldUseCustomParameters(): void } /** @test */ - public function shouldAssembleFilters(): void + public function shouldAssembleLegacyRgxFilters(): void { $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600); // Default filter - $this->assertSame('valid_to >= NOW', $command->jsonSerialize()['parameters']['filter']); + $this->assertSame('', $command->jsonSerialize()['parameters']['filter']); // Add custom filters to the default one $command->addFilter('foo = bar') ->addFilter('bar = baz'); $this->assertSame( - 'valid_to >= NOW and foo = bar and bar = baz', + 'foo = bar and bar = baz', $command->jsonSerialize()['parameters']['filter'] ); @@ -94,6 +97,38 @@ public function shouldAssembleFilters(): void $this->assertSame('my_filter = 1 and other_filter = foo', $command->jsonSerialize()['parameters']['filter']); } + /** @test */ + public function shouldAssembleMqlFilters(): void + { + $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600); + + // Default filter + $this->assertSame('', $command->jsonSerialize()['parameters']['filter']); + + // Add custom filters to the default one + $command->addFilter("first_string_property = 'bar'") + ->addFilter("second_string_property LIKE 'bar%'") + ->addFilter("third_string_property NOT LIKE '%bar'") + ->addFilter('bool_property = true') + ->addFilter('nullable_property IS NULL') + ->addFilter("'some_value' in set_property"); + + $this->assertSame( + "first_string_property = 'bar' and " . + "second_string_property LIKE 'bar%' and " . + "third_string_property NOT LIKE '%bar' and " . + 'bool_property = true and ' . + 'nullable_property IS NULL and ' . + "'some_value' in set_property", + $command->jsonSerialize()['parameters']['filter'] + ); + + // Overwrite all filters + $command->setFilters(['my_filter = 1', 'other_filter IS NULL']); + + $this->assertSame('my_filter = 1 and other_filter IS NULL', $command->jsonSerialize()['parameters']['filter']); + } + /** @test */ public function shouldAllowModificationOfResponseProperties(): void {