Skip to content

Commit

Permalink
Implement support for Matej Query Language in recommendation requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
foglcz committed Aug 30, 2018
1 parent c3e9134 commit 7edd62a
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
31 changes: 28 additions & 3 deletions src/Model/Command/UserRecommendation.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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[] */
Expand Down Expand Up @@ -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
*/
Expand All @@ -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
*/
Expand All @@ -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.
*/
Expand Down Expand Up @@ -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;
}
Expand Down
43 changes: 39 additions & 4 deletions tests/unit/Model/Command/UserRecommendationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
],
Expand All @@ -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);
Expand All @@ -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,
],
Expand All @@ -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']
);

Expand All @@ -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
{
Expand Down

0 comments on commit 7edd62a

Please sign in to comment.