lmc / matej-client
Matej推荐引擎的API客户端
Requires
- php: ^7.1 || ^8.0
- ext-hash: *
- ext-json: *
- beberlei/assert: ^2.8 || ^3.0
- fig/http-message-util: ^1.1
- myclabs/php-enum: ^1.6
- php-http/client-common: ^1.6 || ^2.0
- php-http/client-implementation: ^1.0
- php-http/discovery: ^1.0
- php-http/httplug: ^1.1 || ^2.0
- php-http/message: ^1.6
- php-http/message-factory: ^1.0
- php-http/promise: ^1.0
- psr/http-message: ^1.0
- ramsey/uuid: ^3.7 || ^4.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.4
- lmc/coding-standard: ^1.3 || ^2.0
- php-coveralls/php-coveralls: ^2.4
- php-http/guzzle6-adapter: ^1.1.1 || ^2.0
- php-http/mock-client: ^1.0
- php-mock/php-mock-phpunit: ^2.1.2
- php-parallel-lint/php-parallel-lint: ^1.1
- phpstan/extension-installer: ^1.0.5
- phpstan/phpstan: ^0.12.48
- phpstan/phpstan-phpunit: ^0.12.16
- phpunit/phpunit: ^7.5.20 || ^8.0 || ^9.0
- symfony/var-dumper: ^3.3 || ^4.0 || ^5.0
README
安装
Matej API客户端库使用Composer安装。请务必注意,您必须首先在项目中正确设置自动加载。
我们使用HTTPlug抽象,这样库就不会与任何特定的HTTP库硬耦合,允许您使用适合您需求的HTTP库(或者可能是您已经使用的HTTP库)。
这意味着,除了lmc/matej-client
库本身之外,您还必须安装一个提供php-http/client-implementation
的包 - 请参阅支持的客户端和适配器列表。
例如,如果您想使用Guzzle 6作为底层HTTP库,可以这样安装包
$ composer require lmc/matej-client php-http/guzzle6-adapter
或者如果您想使用Guzzle 5(请注意,与guzzle6-adapter
不同,它不包含guzzlehttp/psr7
,因此您也必须安装它)
$ composer require lmc/matej-client php-http/guzzle5-adapter guzzlehttp/psr7
或者如果您想使用cURL客户端
$ composer require lmc/matej-client php-http/curl-client guzzlehttp/psr7
使用
要开始使用Matej,您需要您的账户ID(数据库名称)和秘密API密钥 - 这两个都应从LMC研发团队获取。
首先创建一个Matej
对象的实例
use Lmc\Matej\Matej; // in all following examples namespaces and use statements are ommited from the code samples $matej = new Matej('accountId', 'apikey');
现在您可以使用request()
方法使用构建器,这些构建器对每个Matej端点都是可用的,并将帮助您组装请求。每个请求构建器通过其方法接受各种Command(s)对象实例(请参阅Lmc\Matej\Model\Command
命名空间)。有关更多信息,请参阅Matej文档、IDE中的代码补全或下面的示例。
完成构建请求后,使用send()
方法执行它并检索响应
$response = $matej->request() ->events() ->addInteraction(\Lmc\Matej\Model\Command\Interaction::withItem('purchases', 'user-id', 'item-id')) ->addUserMerge(...) ... ->send();
下面提供了为每个端点构建请求的示例。
处理响应
一旦$response
填充了Matej中的数据(如上例所示),您现在可以像这样处理响应
echo 'Number of commands: ' . $response->getNumberOfCommands() . "\n"; echo 'Number of successful commands: ' . $response->getNumberOfSuccessfulCommands() . "\n"; echo 'Number of failed commands: ' . $response->getNumberOfFailedCommands()() . "\n"; // Use $response->isSuccessful() to check whether all of the commands send in request were successful or not: if (!$response->isSuccessful()) { echo 'At least one command response was not succesful!'; } // Iterate over getCommandResponses() to get response for each command passed to the builder. // Commands in the response are present in the same order as they were added to the requets builder. foreach ($response->getCommandResponses() as $commandResponse) { if ($commandResponse->isSuccessful()) { // Methods $commandResponse->getData(), ->getMessage() and ->getStatus() are available } else { // Log error etc. } }
推荐、排序和商品属性端点有语法糖快捷方式,这使得处理响应更加容易。下面提供详细示例。
设置商品属性(用于设置您的Matej数据库)
$matej = new Matej('accountId', 'apikey'); // Create new item property in database: $response = $matej->request() ->setupItemProperties() ->addProperty(ItemPropertySetup::timestamp('valid_to')) ->addProperty(ItemPropertySetup::string('title')) ->send(); // Get list of item properties that are defined in Matej $response = $matej->request() ->getItemProperties() ->send(); $properties = $response->getData(); // this is shortcut for $response->getCommandResponse(0)->getData() // Delete item property from database: $response = $matej->request() ->deleteItemProperties() ->addProperty(ItemPropertySetup::timestamp('valid_from')) ->addProperty(ItemPropertySetup::string('title')) ->send();
重置数据库
测试账户的数据库(以-test
结尾的)可以通过API重置。使用此方法可以删除所有数据,包括数据库设置(商品属性)。
$matej = new Matej('accountId', 'apikey'); $response = $matej->request() ->resetDatabase() ->send(); var_dump($response->isSuccessful()); // true on success
重置数据
测试账户的数据(以-test
结尾的)可以通过API重置。使用此方法可以删除所有数据,同时保留数据库设置(商品属性、场景)。
$matej = new Matej('accountId', 'apikey'); $response = $matej->request() ->resetData() ->send(); var_dump($response->isSuccessful()); // true on success
将事件数据发送到Matej
您可以使用events()
构建器发送一系列命令到Matej
交互
通过addInteraction()
- 发送用户与商品之间的交互信息商品属性
通过addItemProperty()
- 更新Matej数据库中存储的商品数据UserMerge
通过addUserMerge()
- 用于合并两个用户的交互并删除源用户
您可以在同一请求中混合不同的命令类型。您可以在单个请求中发送最多 1 000 个命令。
$matej = new Matej('accountId', 'apikey'); $response = $matej->request() ->events() // Add interaction between user and item ->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'])) ->addItemProperties([/* array of ItemProperty objects */]) // Merge user ->addUserMerge(UserMerge::mergeInto('target-user-id', 'source-user-id', 1629361884)) ->addUserMerges([/* array of UserMerge objects */]) ->send();
此端点已实施速率限制。 我们持续监控后端系统的负载,当队列中的事件数量超过一定阈值时,Matej API 将开始返回 429
错误。如果发生这种情况,您应该稍后重新发送整个请求,因为没有处理任何命令。
这样做是为了确保我们不会丢失任何推送的数据。简单的 100ms 睡眠应该足够。
合并用户
您可以使用 UserMerge
命令来合并用户。第一个参数是目标用户 ID,第二个参数是源用户 ID。当您合并两个用户时,Matej 将将源用户的全部交互和历史记录移动到目标用户,并将它们分配给目标用户。然后删除源用户。
可选地,您还可以发送一个第三个参数,其中包含合并发生的时间戳。
将来将需要时间戳。我们建议您发送它,这将使 Matej 客户端的未来升级对您来说更容易。
请求推荐
您可以从 Matej 请求 4 种类型的推荐。每种类型都由一个特定的推荐命令类表示
- 项目到用户 - UserItemRecommendation
- 项目到项目 - ItemItemRecommendation
- 用户到用户 - UserUserRecommendation
- 用户到项目 - ItemUserRecommendation
例如,您可以使用 recommendation()
构建器为单个用户获取推荐。您可以将最近的交互和用户合并事件附加到请求中,以便在提供推荐时考虑这些交互。
$matej = new Matej('accountId', 'apikey'); $response = $matej->request() ->recommendation(UserItemRecommendation::create('user-id', 'test-scenario')) ->setInteraction(Interaction::withItem('purchases', 'user-id', 'item-id')) // optional ->setUserMerge(UserMerge::mergeInto('user-id', 'source-id')) // optional ->send(); $recommendations = $response->getRecommendation()->getData();
您还可以设置推荐命令的更细粒度选项,并覆盖 Matej 的默认行为。
每种推荐命令类型支持不同的定制选项。请参见下表。
可用的推荐属性
$recommendation = UserItemRecommendation::create('user-id', 'test-scenario') ->setCount(5) ->setRotationRate(1.0) ->setRotationTime(3600) ->setFilters(['for_recommendation = 1']) ->setMinimalRelevance(ItemMinimalRelevance::HIGH()) ->enableHardRotation() // You can further modify which items will be recommended by providing boosting rules. // Priority of items matching the query will be multiplied by the value of multiplier: ->addBoost(Boost::create('valid_to >= NOW()', 2)); $response = $matej->request() ->recommendation($recommendation) ->send();
从 $response
,您还可以访问其余的数据
$response = $matej->request() ->recommendation($recommendation) ->send(); echo $response->getInteraction()->getStatus(); // SKIPPED echo $response->getUserMerge()->getStatus(); // SKIPPED echo $response->getRecommendation()->getStatus(); // OK $recommendations = $response->getRecommendation()->getData(); // var_dump($recommendations): // array(2) { // [0] => object(stdClass)#1 (2) { // ["item_id"] => string(9) "item_id_1" // } // [1] => object(stdClass)#2 (2) { // ["item_id"] => string(9) "item_id_2" // } // }
推荐响应属性
每个 Matej 中的项目都有自己的 id,以及可选的其他项目属性。这些属性可以在 项目属性设置 中设置,您可以在 事件 请求中上传项目数据。这有一个主要的好处,因为您可以将这些属性请求作为推荐请求的一部分返回。
我们称它们为响应属性。可以通过调用 ->addResponseProperty()
方法或调用 ->setResponseProperties()
方法来指定。以下将请求 item_id
、item_url
、item_title
$recommendation = UserItemRecommendation::create('user-id', 'test-scenario') ->addResponseProperty('item_title') ->addResponseProperty('item_url'); $response = $matej->request() ->recommendation($recommendation) ->send(); $recommendedItems = $response->getRecommendation()->getData(); // $recommendedItems is an array of stdClass instances: // // array(2) { // [0] => object(stdClass)#1 (2) { // ["item_id"] => string(9) "item_id_1" // ["item_url"] => string(5) "url_1" // ["item_title"] => string(5) "title_1" // } // [1] => object(stdClass)#2 (2) { // ["item_id"] => string(9) "item_id_2" // ["item_url"] => string(10) "url_2" // ["item_title"] => string(10) "title_2" // } // }
如果您没有指定任何响应属性,Matej 将返回一个包含仅 item_id
属性的 stdClass
实例数组。如果您至少请求了一个响应属性,则不需要提及 item_id
,因为 Matej 将始终返回它,无论请求了哪些属性。
如果您请求了未知属性,Matej 将返回一个包含 HTTP 状态码 400
的 BAD REQUEST
。
这样,当您从 Matej 接收推荐时,您不需要循环 item_id
并从本地数据库检索更多信息。然而,这意味着您必须通过 事件 请求在 Matej 中保留所有项目的最新状态。
请求单个用户的商品排序
请求单个用户的物品排序。您可以将此排序命令与最近的交互和用户合并事件合并在一个请求中,以便在执行物品排序时考虑它们。
$matej = new Matej('accountId', 'apikey'); $response = $matej->request() ->sorting(ItemSorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3'])) ->setInteraction(Interaction::withItem('purchases', 'user-id', 'item-id')) // optional ->setUserMerge(UserMerge::mergeInto('user-id', 'source-id')) // optional ->send(); $sortedItems = $response->getSorting()->getData();
从 $response
,您还可以访问其余的数据
$response = $matej->request() ->sorting($sorting) ->send(); echo $response->getInteraction()->getStatus(); // SKIPPED echo $response->getUserMerge()->getStatus(); // SKIPPED echo $response->getSorting()->getStatus(); // OK $sortedData = $response->getSorting()->getData();
请求推荐/物品排序的批次
使用campaign()
构建器请求多个用户的推荐和/或物品排序批次。这种用例的典型用途是生成电子邮件营销活动。
$matej = new Matej('accountId', 'apikey'); $response = $matej->request() ->campaign() // Request item sortings ->addSorting(ItemSorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3'])) ->addSortings([/* array of Sorting objects */]) // Request user-based recommendations ->addRecommendation(UserItemRecommendation::create('user-id', 'emailing')) ->addRecommendations([/* array of UserRecommendation objects */]) ->send();
A/B 测试支持
Recommendation
和ItemSorting
命令支持各种模型的可选A/B测试。这需要在Matej中首先设置,但一旦可用,您可以在请求推荐或排序时指定要使用哪个模型。
此功能适用于Recommendation
、ItemSorting
和Campaign
请求
$recommendationCommand = UserItemRecommendation::create('user-id', 'test-scenario') ->setModelName('alpha'); $sortingCommand = ItemSorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3']); $sortingCommand->setModelName('beta'); $response = $matej->request() ->recommendation($recommendationCommand) ->send(); $response = $matej->request() ->sorting($sortingCommand) ->send(); $response = $matej->request() ->campaign() ->addRecommendation($recommendationCommand->setModelName('gamma')) ->addSorting($sortingCommand->setModelName('delta')) ->send();
如果您不提供任何模型名称,请求将不带模型名称发送,Matej将使用您的实例的默认模型。
通常,您会选择一组随机用户样本,向他们展示第二个模型的推荐和排序。因此,在您的代码中的实现应类似于以下内容
$recommendation = UserItemRecommendation::create('user-id', 'test-scenario'); if ($session->isUserInBucketB()) { $recommendation->setModelName('alpha'); } $response = $matej->request()->recommendation($recommendation)->send();
模型名称将由LMC提供。
忘记用户数据(GDPR)
Matej可以通过匿名化或删除来“忘记”用户数据。删除权(“被遗忘权”)是欧盟《通用数据保护条例》(GDPR)的一部分,并且可以使用forget()
构建器在您的端上实现。
有两种删除用户数据的方式,但两者都是不可逆的,您将无法再识别该用户。
- 首选方式是
匿名化
用户,这将为所有个人数据随机生成唯一的标识符,并更改所有数据库和日志文件中的该标识符。这样,用户的行为将保留在Matej数据库中,因此将继续为推荐模型做出贡献,但您将无法识别用户。因此,其配置文件将实际上被冻结(因为不会有新的交互进入。)新用户ID在服务器端生成,因此在发出请求后无法撤销。 - 另一种方式是
删除
用户,这将根据数据保护法从所有数据库中擦除他们的数据。这可能会影响推荐的品质,因为用户的行为将完全从所有数据库中删除,因此他们的配置文件将不再为推荐模型做出贡献。
通常,用户将确定他们是否希望其数据被匿名化或删除,您必须遵守他们的请求。
要调用端点,请使用forget()
构建器并附加用户
$matej = new Matej('accountId', 'apikey'); $matej->request() ->forget() ->addUser(UserForget::anonymize('anonymize-this-user-id')) ->addUser(UserForget::anonymize('delete-this-user-id')) ->addUsers([ UserForget::anonymize('anonymize-this-user-id-as-well'), UserForget::delete('delete-this-user-id-as-well'), ]) ->send() ;
异常和错误处理
只有在请求整个Matej失败(在发送、解码、认证等时)或库被错误使用时才会抛出异常。如果请求成功送达Matej,即使任何(或可能全部)提交的命令(这些命令是请求的一部分)被Matej拒绝,也不会抛出异常。
因此,为了检查整个响应(即所有包含的命令响应)是否成功,您必须不依赖于异常(因为它们不会抛出,如上所述),而应使用Response::isSuccessful()
方法——请参阅上面的使用示例。
如果您想检查哪个单个命令响应是成功的,您可以使用CommandResponse::isSuccessful()
方法检查其状态。
在Matej API客户端中发生的异常实现Lmc\Matej\Exception\MatejExceptionInterface
。异常树如下
请注意,如果您通过 $matej->setHttpClient()
注入自定义的HTTP客户端,它可能在HTTP请求失败时抛出自定义异常。因此,请确保禁用此行为(例如,Guzzle 6中的http_errors
选项)。
故障排除 RequestException
当向Matej发起请求时抛出RequestException
,您可能需要从服务器读取响应体,以查看Matej产生的完整错误,以便您能够排除异常的原因。
您可以通过以下方式实现:
$matej = new Matej('accountId', 'apikey'); try { $response = $matej ->request() ->sorting(/* ... */) // ... ->send(); } catch (\Lmc\Matej\Exception\RequestException $exception) { echo $e->getMessage(); // this will output just HTTP reason phrase, like "Bad Request" $serverResponseContents = $exception->getResponse() ->getBody() ->getContents(); echo $serverResponseContents; // this will output the full response body which Matej server gave }
变更日志
有关最新更改,请参阅CHANGELOG.md文件。我们遵循语义化版本控制。
发布新版本
有关如何分步发布新客户端版本的详细信息,请参阅RELEASE.md。
运行测试
对于每个pull-request,必须通过单元测试、静态分析和代码风格检查。
要运行所有这些检查,请执行以下操作:
$ composer all
在代码风格违规的情况下,您可以运行此命令,该命令将尝试自动修复代码风格:
$ composer fix