Skip to content

Commit

Permalink
Merge 47887e0 into f9ff273
Browse files Browse the repository at this point in the history
  • Loading branch information
JakubTesarek committed Apr 12, 2020
2 parents f9ff273 + 47887e0 commit c3785ca
Show file tree
Hide file tree
Showing 18 changed files with 611 additions and 246 deletions.
60 changes: 39 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# PHP API Client for Matej recommendation engine

[![Latest Stable Version](https://img.shields.io/packagist/v/lmc/matej-client.svg?style=flat-square)](https://packagist.org/packages/lmc/matej-client)
Expand All @@ -8,7 +9,7 @@

This library requires PHP 7.1+. However, we provide also PHP 5.6-compatible version [`matej-client-php5`](https://github.com/lmc-eu/matej-client-php5).

Please note the PHP 5.6 version is just transpiled copy of this library - examples, pull requests, issues, changelog etc. are placed in this repository.
Please note that the PHP 5.6 version is just transpiled copy of this library - examples, pull requests, issues, changelog etc. are placed in this repository.

## Installation

Expand Down Expand Up @@ -41,8 +42,7 @@ $ composer require lmc/matej-client php-http/curl-client guzzlehttp/psr7 # use l

## Usage

To start using Matej you will need your account id (database name) and secret API key - both of them must be obtained
from LMC R&D team.
To start using Matej you will need your account id (database name) and secret API key - both of them must be obtained from LMC R&D team.

First create an instance of `Matej` object:
```php
Expand All @@ -61,7 +61,7 @@ Once finished with building the request, use `send()` method to execute it and r
```php
$response = $matej->request()
->events()
->addInteraction(\Lmc\Matej\Model\Command\Interaction::purchase('user-id', 'item-id'))
->addInteraction(\Lmc\Matej\Model\Command\Interaction::withItem('purchases', 'user-id', 'item-id'))
->addUserMerge(...)
...
->send();
Expand Down Expand Up @@ -155,7 +155,7 @@ $matej = new Matej('accountId', 'apikey');
$response = $matej->request()
->events()
// Add interaction between user and item
->addInteraction(Interaction::purchase('user-id', 'item-id'))
->addInteraction(Interaction::withItem('purchases', 'user-id', 'item-id'))
->addInteractions([/* array of Interaction objects */])
// Update item data
->addItemProperty(ItemProperty::create('item-id', ['valid_from' => time(), 'title' => 'Title']))
Expand All @@ -182,8 +182,8 @@ when providing recommendations.
$matej = new Matej('accountId', 'apikey');

$response = $matej->request()
->recommendation(UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600))
->setInteraction(Interaction::purchase('user-id', 'item-id')) // optional
->recommendation(UserRecommendation::create('user-id', 'test-scenario'))
->setInteraction(Interaction::withItem('purchases', 'user-id', 'item-id')) // optional
->setUserMerge(UserMerge::mergeInto('user-id', 'source-id')) // optional
->send();

Expand All @@ -193,9 +193,12 @@ $recommendations = $response->getRecommendation()->getData();
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'])
$recommendation = UserRecommendation::create('user-id', 'test-scenario')
->setCount(5)
->setRotationRate(1.0)
->setRotationTime(3600)
->setFilters(['for_recommendation = 1'])
->setMinimalRelevance(MinimalRelevance::HIGH())
->enableHardRotation()
->addBoost(Boost::create('valid_to >= NOW()', 2));
Expand Down Expand Up @@ -229,18 +232,26 @@ $recommendations = $response->getRecommendation()->getData();
// }
```

You can further modify which items will be recommended by providing boosting rules. Priority of items matching the
MQL `$criteria` will be multiplied by the value of `multiplier`:

```php
$reccomendation = UserRecommendation::create('user-id', 'test-scenario')
->addBoost(Boost::create('valid_to >= NOW()', 2))
->addBoost(Boost::create('for_recommendation = 1', 3.5));
```

#### Recommendation response properties

Every item in Matej has its id, and optionally other item properties. These properties can be set up in [item properties setup](#item-properties-setup-to-setup-you-matej-database),
and you can upload item data in the [events](#send-events-data-to-matej) request. This has major benefit because you can request
these properties to be returned as part of your Recommendation Request.

We call them response properties, and they can be specified either as the last parameter of `UserRecommendation::create` function,
by calling `->addResponseProperty()` method, or by calling `->setResponseProperties()` method. Following will request an `item_id`,
We call them response properties. They can be specified by calling `->addResponseProperty()` method or by calling `->setResponseProperties()` method. Following will request an `item_id`,
`item_url` and `item_title`:

```php
$recommendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600, ['item_url'])
$recommendation = UserRecommendation::create('user-id', 'test-scenario')
->addResponseProperty('item_title');

$response = $matej->request()
Expand All @@ -266,7 +277,7 @@ $recommendedItems = $response->getRecommendation()->getData();
```

If you don't specify any response properties, Matej will return an array of `stdClass` instances, which contain only `item_id` property.
If you do request at least one response property, you don't need to metion `item_id`, as Matej will always return it regardless of the
If you do request at least one response property, you don't need to mention `item_id`, as Matej will always return it regardless of the
properties requested.

If you request an unknown property, Matej will return a `BAD REQUEST` with HTTP status code `400`.
Expand All @@ -285,7 +296,7 @@ $matej = new Matej('accountId', 'apikey');

$response = $matej->request()
->sorting(Sorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3']))
->setInteraction(Interaction::purchase('user-id', 'item-id')) // optional
->setInteraction(Interaction::withItem('purchases', 'user-id', 'item-id')) // optional
->setUserMerge(UserMerge::mergeInto('user-id', 'source-id')) // optional
->send();

Expand Down Expand Up @@ -320,20 +331,27 @@ $response = $matej->request()
->addSorting(Sorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3']))
->addSortings([/* array of Sorting objects */])
// Request user-based recommendations
->addRecommendation(UserRecommendation::create('user-id', 10, 'emailing', 1.0, 3600))
->addRecommendations([/* array of UserRecommendation objects */])
->addRecommendation(
UserRecommendation::create('user-id', 'emailing')
->setCount(10)
->setRotationRate(1.0)
->setRotationTime(3600)
)->addRecommendations([/* array of UserRecommendation objects */])
->send();
```

### A/B Testing support
`Recommendation` and `Sorting` commands support optional A/B testing of various models. This has to be set up in Matej first,
but once available, you can specify which model you want to use when requesting recommendations or sortings.
but once available, you can specify which model you want to use when requesting recommendations or sorting.

This is available for `recommendation`, `sorting` and `campaign` requests:

```php
$recommendationCommand = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600);
$recommendationCommand->setModelName('alpha');
$recommendationCommand = UserRecommendation::create('user-id', 'test-scenario');
->setCount(5)
->setRotationRate(1.0)
->setRotationTime(3600)
->setModelName('alpha');

$sortingCommand = Sorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3']);
$sortingCommand->setModelName('beta')
Expand All @@ -355,11 +373,11 @@ $response = $matej->request()

If you don't provide any model name, the request will be sent without it, and Matej will use default model for your instance.

Typically, you'd select a random sample of users, to which you'd present recommendations and sortings from second model. This way, implementation
Typically, you'd select a random sample of users, to which you'd present recommendations and sorting from second model. This way, implementation
in your code should look similar to this:

```php
$recommendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600);
$recommendation = UserRecommendation::create('user-id', 'test-scenario')

if ($session->isUserInBucketB()) {
$recommendation->setModelName('alpha');
Expand Down
110 changes: 110 additions & 0 deletions UPGRADE-3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@

# Upgrading from 2.x to 3.0

API client release 3.0 contains few backward incompatible changes.

This guide will help you upgrade your codebase.

## `UserRecommendation::create()` now accepts only `$userId` and `$scenario`
`UserReccomentation::create()` accepts only two arguments: `$user_id` and `$scenario`.

Both arguments are required.

All other arguments can be now set using new setters and are optional:
- `setCount(int $count)`
- `setRotationRate(float $rotationRate)`
- `setRotationTime(int $rotationTime)`

#### Before
```php
$recommendation = UserRecommendation::create('user-id', 5, 'scenario', 1.0, 3600);
```

#### After
```php
$recommendation = UserRecommendation::create('user-id', 'scenario')
->setCount(5)
->setRotationRate(1.0)
->setRotationTime(3600);
```

which is equivalent to

```php
$recommendation = UserRecommendation::create('user-id', 'scenario');
$recommendation->setCount(5);
$recommendation->setRotationRate(1.0);
$recommendation->setRotationTime(3600);
```

## `Interaction` now accepts interaction type as parameter
Matej now allows configuration of custom interaction types and interaction attributes.

At the same time, it allows specifying interaction item using item alias instead
of item ID. For that reason we removed static constructor methods for creating specific interaction types - `Interaction::detailView`, `Interaction::purchase`, `Interaction::bookmark` and `Interaction::rating`.

We replaced them with constructors for creating Interaction based on `$itemId` or `$itemIdAlias`:

```php
Interaction::withItem(
string $interactionType,
string $userId,
string $itemId,
int $timestamp = null
```

```php
Interaction::withAliasedItem(
string $interactionType,
string $userId,
array $itemIdAlias,
int $timestamp = null):
```

The first argument is always a string representing interaction type. Consult the table bellow to find out the correct value to fill in:

| Before: constructor method | After: argument $interactionType |
|------------------------------|----------------------------------|
| `Interaction::detailView` | `"detailviews"` |
| `Interaction::purchase` | `"purchases"` |
| `Interaction::bookmark` | `"bookmarks"` |
| `Interaction::rating` | `"ratings"` |

> To request new interaction types, please contact Matej support.
#### Before
```php
$interaction = Interaction::bookmark('user-id', 'item_id', time());
```

#### After
```php
$interaction = Interaction::withItem('bookmarks', 'user-id', 'item_id', time());
```
> Argument `$timestamp` remains optional.
## `Interaction` command supports custom attributes
Interactions now support custom attributes. These can be added using fluent API
methods `setAttribute()` and `setAttributes()`.

Argument `value` was removed from constructor methods and has to be set using new attribute methods. Its real name might have change as well. For example, for interaction type `bookmarks`, it was renamed to `stars`.

**Attribute `context` is no longer supported and was removed.**

#### Before
```php
$interaction = Interaction::rating('user-id', 'item_id', 0.5);
```

#### After
```php
$interaction = Interaction::create('ratings', 'user-id', 'item_id')
->setAttribute('stars', 0.5)
```

which is equivalent to

```php
$interaction = Interaction::create('ratings', 'user-id', 'item_id')
$interaction->setAttribute('stars', 0.5)
```
2 changes: 0 additions & 2 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ parameters:
path: tests/
- message: '#Parameter .+ of class Lmc\\Matej\\Matej constructor expects string#'
path: tests/integration/IntegrationTestCase.php
- message: '#Parameter .+ of function forward_static_call_array#'
path: tests/unit/Model/Command/InteractionTest.php
- message: '#Parameter \#4 \$body of method Http\\Message\\RequestFactory::createRequest\(\) expects#'
path: src/Http/RequestManager.php
- '#Unsafe usage of new static\(\)#'
Expand Down
19 changes: 0 additions & 19 deletions src/Model/Command/Constants/InteractionType.php

This file was deleted.

Loading

0 comments on commit c3785ca

Please sign in to comment.