Skip to content

Commit

Permalink
Added support for new recommendation commands.
Browse files Browse the repository at this point in the history
  • Loading branch information
JakubTesarek committed Dec 3, 2020
1 parent 7860917 commit c31c4e2
Show file tree
Hide file tree
Showing 25 changed files with 1,176 additions and 425 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
<!-- There is always Unreleased section on the top. Subsections (Added, Changed, Fixed, Removed) should be added as needed. -->

## Unreleased
### Added
- Added support for new recommendation types - `UserUserRecommendation`, `ItemUserRecommendation`, `ItemItemRecommendation`.

### Changed
- **BC BREAK** | Removed deprecated constant `FILTER_TYPE_MQL` from `UserRecommendation`.
- **BC BREAK** | Class `UserRecommendation` renamed to `UserItemRecommendation`.
- **BC BREAK** | Class `MinimalRelavance` renamed to `ItemMinimalRelevances`.
- **BC BREAK** | Removed deprecated constant `FILTER_TYPE_MQL` from `UserRecommendation`.

## 3.1.0 - 2020-10-13
### Fixed
Expand Down
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,16 @@ If that happens, you should resend the entire request later, as no commands were

This has been implemented so that we don't lose any pushed data. Simple sleep of 100ms should be enough.

### Recommendations for single user
### Requesting recommendations

You can get recommendations for a single user using `recommendation()` builder.
You can request 4 types of recommendation from Matej. Each of them is represented by a specific recommendation command class:

- Items to user - UserItemRecommendation
- Items to item - ItemItemRecommendation
- Users to user - UserUserRecommendation
- Users to item - ItemUserRecommendation

For example, you can get recommendations for a single user using `recommendation()` builder.
You can attach most recent interaction and user merge event to the request so that they're taken into account
when providing recommendations.

Expand All @@ -204,7 +211,25 @@ $response = $matej->request()
$recommendations = $response->getRecommendation()->getData();
```

You can also set more granular options of the recommendation command and overwrite Matej default behavior on per-request basis:
You can also set more granular options of the recommendation command and overwrite Matej default behaviour on per-request basis.

Each type of recommendation command supports different customization options. See table bellow.


| Atribute | Methods | UserItemRecommendation | UserUserRecommendation | ItemItemRecommendation | ItemUserRecommendation |
|---------------|-------------------------------------------|------------------------|------------------------|------------------------|------------------------|
| scenario | `static::create` |||||
| count | `setCount` |||||
| rotation_rate | `setRotationRate` |||||
| rotation_time | `setRotationTime` |||||
| hard_rotation | `enableHardRotation` |||||
| allow_seen | `setAllowSeen` |||||
| min_relevance | `setMinimalRelevance` | `ItemMinimalRelevance` ||| `UserMinimalRelevance` |
| filter | `addFilter` `setFilters` |||||
| boost_rules | `addBoost` `setBoosts` |||||
| model_name | `setModelName` |||||
| properties | `addResponseProperty` `setResponseProperties` |||||


```php
$recommendation = UserItemRecommendation::create('user-id', 'test-scenario')
Expand Down Expand Up @@ -338,7 +363,7 @@ $response = $matej->request()
->addSortings([/* array of Sorting objects */])
// Request user-based recommendations
->addRecommendation(UserItemRecommendation::create('user-id', 'emailing'))
->addRecommendations([/* array of UserItemRecommendation objects */])
->addRecommendations([/* array of UserRecommendation objects */])
->send();
```

Expand Down
56 changes: 55 additions & 1 deletion UPGRADE-4.0.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@

# Upgrading from 3.x to 4.0

API client release 4.0 contains backward incompatible changes.

This guide will help you upgrade your codebase.


## New types of recommendations
API client now supports new types of recommendation commands:
- Recommend items to user (`UserItemRecommendation`, originally `UserRecommendation`)
- Recommend users to user (`UserUserRecommendation`)
- Recommend items to item (`ItemItemRecommendation`)
- Recommend users to item (`ItemUserRecommendation`)

Each recommendation type supports different parameter options. Awailable parameters are
described in table bellow:

| Atribute | Methods | UserItemRecommendation | UserUserRecommendation | ItemItemRecommendation | ItemUserRecommendation |
|---------------|-------------------------------------------|------------------------|------------------------|------------------------|------------------------|
| scenario | `static::create` |||||
| count | `setCount` |||||
| rotation_rate | `setRotationRate` |||||
| rotation_time | `setRotationTime` |||||
| hard_rotation | `enableHardRotation` |||||
| allow_seen | `setAllowSeen` |||||
| min_relevance | `setMinimalRelevance` | `ItemMinimalRelevance` ||| `UserMinimalRelevance`* |
| filter | `addFilter` `setFilters` |||||
| boost_rules | `addBoost` `setBoosts` |||||
| model_name | `setModelName` |||||
| properties | `addResponseProperty` `setResponseProperties` |||||

\* `UserMinimalRelevance` supports only `MEDIUM` and `HIGH` relevancies.

Each recommendation class provides a static constructor method `create` that accepts `userId` (recommendations for users) or `itemId` (recommendations for items).

## Recommending items to users
Class `UserRecommendation` was renamed to `UserItemRecommendation`. Class `MinimalRelevance` was
renamed to `ItemMinimalRelevance`. All other methods and attributes remained unchanged.

#### Before
```php
$recommendation = UserRecommendation::create('user-id', 'scenario')
->setMinimalRelevance(ItemMinimalRelevance::LOW())
->setMinimalRelevance(MinimalRelevance::LOW())
->setCount(5)
->setRotationRate(1.0)
->setRotationTime(3600);
Expand All @@ -25,3 +55,27 @@ $recommendation = UserItemRecommendation::create('user-id', 'scenario')
->setRotationRate(1.0)
->setRotationTime(3600);
```

## New recommendation types
New types of recommendations can be created is similar way to `UserItemRecommendation`. For example:

### Users that might be interested in an item
```php
$recommendation = ItemUserRecommendation::create('item-id', 'scenario')
->setCount(5)
->setAllowSeen(false);
```

### Users that are similar to a user
```php
$recommendation = UserUserRecommendation::create('user-id', 'scenario')
->setCount(5)
->setRotationRate(1.0)
->setRotationTime(3600);
```

### Items that are similar to an item
```php
$recommendation = ItemItemRecommendation::create('item-id', 'scenario')
->setCount(5);
```
41 changes: 41 additions & 0 deletions src/Model/Command/AbstractItemRecommendation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php declare(strict_types=1);

namespace Lmc\Matej\Model\Command;

use Lmc\Matej\Model\Assertion;

/**
* Deliver personalized recommendations for the given user.
*/
abstract class AbstractItemRecommendation extends AbstractRecommendation
{
/** @var string */
private $itemId;

protected function __construct(string $itemId, string $scenario)
{
parent::__construct($scenario);
$this->setItemId($itemId);
}

public function getItemId(): string
{
return $this->itemId;
}

protected function setItemId(string $itemId): void
{
Assertion::typeIdentifier($itemId);

$this->itemId = $itemId;
}

protected function getCommandParameters(): array
{
$parameters = parent::getCommandParameters();

$parameters['item_id'] = $this->itemId;

return $parameters;
}
}
81 changes: 81 additions & 0 deletions src/Model/Command/AbstractRecommendation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php declare(strict_types=1);

namespace Lmc\Matej\Model\Command;

use Lmc\Matej\Model\Assertion;

/**
* Deliver personalized recommendations for the given user.
*/
abstract class AbstractRecommendation extends AbstractCommand
{
/** @var int */
private $count;
/** @var string */
private $scenario;
/** @var string|null */
private $modelName;

protected function __construct(string $scenario)
{
$this->setScenario($scenario);
}

/**
* Set A/B model name
*
* @return $this
*/
public function setModelName(string $modelName): self
{
Assertion::typeIdentifier($modelName);

$this->modelName = $modelName;

return $this;
}

/**
* Set number of requested recommendations. The real number of recommended items could be lower or even zero when
* there are no items relevant for the user.
*
* @return $this
*/
public function setCount(int $count): self
{
Assertion::greaterThan($count, 0);

$this->count = $count;

return $this;
}

/**
* Scenario name.
*/
protected function setScenario(string $scenario): void
{
Assertion::typeIdentifier($scenario);

$this->scenario = $scenario;
}

abstract protected function getCommandType(): string;

protected function getCommandParameters(): array
{
$parameters = [
'scenario' => $this->scenario,
];

if ($this->count !== null) {
$parameters['count'] = $this->count;
}

if ($this->modelName !== null) {
$parameters['model_name'] = $this->modelName;
}

return $parameters;
}
}
126 changes: 126 additions & 0 deletions src/Model/Command/AbstractUserRecommendation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php declare(strict_types=1);

namespace Lmc\Matej\Model\Command;

use Lmc\Matej\Model\Assertion;

/**
* Deliver personalized recommendations for the given user.
*/
abstract class AbstractUserRecommendation extends AbstractRecommendation implements UserAwareInterface
{
/** @var string */
private $userId;
/** @var float */
private $rotationRate;
/** @var int */
private $rotationTime;
/** @var bool */
private $hardRotation;
/** @var bool */
private $allowSeen = false;

protected function __construct(string $userId, string $scenario)
{
parent::__construct($scenario);
$this->setUserId($userId);
}

/**
* Even with rotation rate 1.0 user could still obtain the same recommendations in some edge cases.
* To prevent this, enable hard rotation - recommended items are then excluded until rotation time is expired.
* By default hard rotation is not enabled.
* @return $this
*/
public function enableHardRotation(): self
{
$this->hardRotation = true;

return $this;
}

/**
* Allow items, that the user has already "seen"
*
* By default user won't see any items, that it has visited (and we have recorded DetailView interaction.)
* If you want to circumvent this, and get recommendations including the ones, that the user has already visited,
* you can set the "seen" allowance here.
*
* @return $this
*/
public function setAllowSeen(bool $seen): self
{
$this->allowSeen = $seen;

return $this;
}

/**
* Get id of user requesting recommendation.
*/
public function getUserId(): string
{
return $this->userId;
}

/**
* Set how much should the item be penalized for being recommended again in the near future.
*
* @return $this
*/
public function setRotationRate(float $rotationRate): self
{
Assertion::between($rotationRate, 0, 1);

$this->rotationRate = $rotationRate;

return $this;
}

/**
* Specify for how long will the item's rotationRate be taken in account and so the item is penalized for
* recommendations.
*
* @return $this
*/
public function setRotationTime(int $rotationTime): self
{
Assertion::greaterOrEqualThan($rotationTime, 0);

$this->rotationTime = $rotationTime;

return $this;
}

protected function setUserId(string $userId): void
{
Assertion::typeIdentifier($userId);

$this->userId = $userId;
}

protected function getCommandParameters(): array
{
$parameters = parent::getCommandParameters();

$parameters['user_id'] = $this->userId;

if ($this->rotationRate !== null) {
$parameters['rotation_rate'] = $this->rotationRate;
}

if ($this->rotationRate !== null) {
$parameters['rotation_time'] = $this->rotationTime;
}

if ($this->allowSeen !== false) {
$parameters['allow_seen'] = $this->allowSeen;
}

if ($this->hardRotation !== null) {
$parameters['hard_rotation'] = $this->hardRotation;
}

return $parameters;
}
}
Loading

0 comments on commit c31c4e2

Please sign in to comment.