Skip to content

Commit

Permalink
Feature/boosting rules (#111)
Browse files Browse the repository at this point in the history
Implementation of boosting rules for Matej9 (fixes #109)
  • Loading branch information
JakubTesarek committed Apr 9, 2020
1 parent 9f5663f commit f9ff273
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,8 @@
<!-- There is always Unreleased section on the top. Subsections (Added, Changed, Fixed, Removed) should be added as needed. -->

## Unreleased
### Added
- Add support for recommendation boosting rules.

## 2.4.0 - 2020-01-21
- No changes in the library, new version released because of adjustments of PHP 5.6 compatible fork.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -194,9 +194,11 @@ 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(['for_recommendation = 1'])
->setMinimalRelevance(MinimalRelevance::HIGH())
->enableHardRotation();
->enableHardRotation()
->addBoost(Boost::create('valid_to >= NOW()', 2));

$response = $matej->request()
->recommendation($recommendation)
Expand Down
53 changes: 53 additions & 0 deletions src/Model/Command/Boost.php
@@ -0,0 +1,53 @@
<?php declare(strict_types=1);

namespace Lmc\Matej\Model\Command;

use Lmc\Matej\Model\Assertion;

/**
* Boosting items is a way how to modify results returend by Matej by specifying
* rules to increase items relevance.
*/
class Boost
{
/** @var string */
private $query;
/** @var float */
private $multiplier;

private function __construct(string $query, float $multiplier)
{
$this->setQuery($query);
$this->setMultiplier($multiplier);
}

/**
* Create boost rule to prioritize items
*
* @return static
*/
public static function create(string $query, float $multiplier): self
{
return new static($query, $multiplier);
}

public function setQuery(string $query): void
{
$this->query = $query;
}

public function setMultiplier(float $multiplier): void
{
Assertion::greaterThan($multiplier, 0);

$this->multiplier = $multiplier;
}

public function jsonSerialize(): array
{
return [
'query' => $this->query,
'multiplier' => $this->multiplier,
];
}
}
40 changes: 40 additions & 0 deletions src/Model/Command/UserRecommendation.php
Expand Up @@ -38,6 +38,8 @@ class UserRecommendation extends AbstractCommand implements UserAwareInterface
private $responseProperties = [];
/** @var bool */
private $allowSeen = false;
/** @var Boost[] */
private $boosts = [];

private function __construct(
string $userId,
Expand Down Expand Up @@ -189,6 +191,30 @@ public function setAllowSeen(bool $seen): self
return $this;
}

/**
* Add a boost rule to already added rules.
*
* @return $this
*/
public function addBoost(Boost $boost): self
{
$this->boosts[] = $boost;

return $this;
}

/**
* Set boosts. Removes all previously set rules.
*
* @return $this
*/
public function setBoosts(array $boosts): self
{
$this->boosts = $boosts;

return $this;
}

public function getUserId(): string
{
return $this->userId;
Expand Down Expand Up @@ -239,6 +265,16 @@ protected function getCommandType(): string
return 'user-based-recommendations';
}

protected function getSerializedBoosts(): array
{
return array_map(
function (Boost $boost) {
return $boost->jsonSerialize();
},
$this->boosts
);
}

protected function getCommandParameters(): array
{
$parameters = [
Expand All @@ -262,6 +298,10 @@ protected function getCommandParameters(): array
$parameters['allow_seen'] = $this->allowSeen;
}

if (!empty($this->boosts)) {
$parameters['boost_rules'] = $this->getSerializedBoosts();
}

return $parameters;
}
}
Expand Up @@ -3,6 +3,7 @@
namespace Lmc\Matej\IntegrationTests\RequestBuilder;

use Lmc\Matej\IntegrationTests\IntegrationTestCase;
use Lmc\Matej\Model\Command\Boost;
use Lmc\Matej\Model\Command\Interaction;
use Lmc\Matej\Model\Command\UserMerge;
use Lmc\Matej\Model\Command\UserRecommendation;
Expand All @@ -20,8 +21,9 @@ public function shouldExecuteRecommendationRequestOnly(): void
{
$response = static::createMatejInstance()
->request()
->recommendation($this->createRecommendationCommand('user-a'))
->send();
->recommendation($this->createRecommendationCommand('user-a')
->addBoost(Boost::create('test', 1.2))
)->send();

$this->assertInstanceOf(RecommendationsResponse::class, $response);
$this->assertResponseCommandStatuses($response, 'SKIPPED', 'SKIPPED', 'OK');
Expand Down
32 changes: 32 additions & 0 deletions tests/unit/Model/Command/BoostTest.php
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);

namespace Lmc\Matej\Model\Command;

use Lmc\Matej\Exception\DomainException;
use PHPUnit\Framework\TestCase;

class BoostTest extends TestCase
{
/**
* @test
*/
public function shouldBeJsonSerializable(): void
{
$boost = Boost::create('valid_to >= NOW()', 2.1);
$this->assertSame(
['query' => 'valid_to >= NOW()', 'multiplier' => 2.1],
$boost->jsonSerialize()
);
}

/**
* @test
*/
public function shouldNotAllowMultiplierLessThan0(): void
{
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Provided "-1" is not greater than "0".');

Boost::create('valid_to >= NOW()', -1);
}
}
37 changes: 36 additions & 1 deletion tests/unit/Model/Command/UserRecommendationTest.php
Expand Up @@ -52,7 +52,9 @@ public function shouldUseCustomParameters(): void
->enableHardRotation()
->setFilters(['foo = bar', 'baz = ban'])
->setModelName($modelName)
->setAllowSeen(true);
->setAllowSeen(true)
->addBoost(Boost::create('valid_to >= NOW()', 1.0))
->addBoost(Boost::create('custom = argument', 2.0));

$this->assertInstanceOf(UserRecommendation::class, $command);
$this->assertSame(
Expand All @@ -71,6 +73,10 @@ public function shouldUseCustomParameters(): void
'properties' => [],
'model_name' => $modelName,
'allow_seen' => true,
'boost_rules' => [
['query' => 'valid_to >= NOW()', 'multiplier' => 1.0],
['query' => 'custom = argument', 'multiplier' => 2.0],
],
],
],
$command->jsonSerialize()
Expand Down Expand Up @@ -124,4 +130,33 @@ public function shouldAllowModificationOfResponseProperties(): void
$command->setResponseProperties(['position_title']);
$this->assertSame(['position_title'], $command->jsonSerialize()['parameters']['properties']);
}

/** @test */
public function shouldResetBoostRules(): void
{
$command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600)
->addBoost(Boost::create('valid_to >= NOW()', 1.0));

$command->setBoosts([
Boost::create('foo = bar', 1.2),
Boost::create('baz = ban', 3.4),
]);

$this->assertSame(
[
['query' => 'foo = bar', 'multiplier' => 1.2],
['query' => 'baz = ban', 'multiplier' => 3.4],
],
$command->jsonSerialize()['parameters']['boost_rules']
);
}

/** @test */
public function shouldNotIncludeEmptyBoosts(): void
{
$command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600)
->setBoosts([]);

$this->assertArrayNotHasKey('boost_rules', $command->jsonSerialize()['parameters']);
}
}

0 comments on commit f9ff273

Please sign in to comment.