アイテムレコメンドAPIを KARTE Datahub と Craft Functions で実装する
- solution
- 2024年9月17日
こんにちは、Customer Engineerのgamiです!最近、第二子が生まれて1ヶ月の育休を取りました。強くてニューゲーム。
さて、以前の記事で紹介した通り、KARTEを駆使するとKARTEで計測したWebページ上のユーザー行動データをもとにしたアイテム軸のレコメンドを比較的簡単に実装することができます。
KARTEでレコメンドを実装してみた | PLAID Solution Blog
一方で、この記事にある方法は、レコメンドを表示するチャネルとして「KARTEの機能を使ってWebサイトやネイティブアプリ上でレコメンドアイテムを表示する」ということしか想定されていません。
実際には、たとえばネイティブアプリのネイティブコンポーネント上でレコメンドを直接利用したいニーズがあったりします。その場合はKARTE単独での実現は難しいです。
そこで今回は、KARTEで実現したアイテムレコメンドロジックを外部サーバーなどから柔軟に利用するためのAPIをKARTE Craftで実装する方法について紹介します。
アウトプットイメージ
最終的なアウトプットイメージは次の通りです。
Craft Functionsのエンドポイントに対して ?item_id=002 のようなクエリパラメータを付けてGETリクエストを送ると、そのアイテムに関連するアイテムの情報が配列で返されます。レコメンド結果は、日次など定期的に更新されます。
構成図は次の通り。
アイテム間の関連性を計算するDatahubクエリを定期実行し、Datahubジョブフロー経由で「KVS更新用」のCraft Functionsに結果を連携し、それをCraft KVSに格納しておきます。「API用」のCraft Functionsは、外部からのリクエストをエンドポイント経由で受け付けて、関連するアイテムのデータを返却します。
注意点
前提として、次の点に注意をしてください。
- KARTE Datahub 、 Craft Functions 、 Craft KVS を利用できるKARTEプロジェクトが必要です
- Craft Functions の「ファンクション月間実行数」上限に抵触しないように注意してご利用ください
- 特に「KVS更新用」のファンクションは、1回の更新ジョブにつき、レコメンドデータに含まれるアイテム数の数だけ実行されます
- たとえば10万件のアイテムに対するレコメンドデータを日次更新する場合、それだけで約300万回/月のファンクション実行数を消費します
- 特に「KVS更新用」のファンクションは、1回の更新ジョブにつき、レコメンドデータに含まれるアイテム数の数だけ実行されます
- 1アイテムに対するレコメンドデータのサイズが Craft KVS の 1 value あたりのサイズ上限に抵触しないように注意してください
- どうしても上限を超えてしまう場合は、サイズ上限を緩和するか、後述する回避方法の利用をご検討ください
設定手順
設定の手順は次の通りです。
- 対象データを抽出するDatahubクエリを作成する
- Craft KVSにDatahubクエリ結果を格納する
- Craft Functionsのファンクションを作成する
順番に見てみましょう。
1. 対象データを抽出するDatahubクエリを作成する
Craft KVSの更新に利用するレコメンドデータ抽出用Datahubクエリを作成します。
この記事で紹介したファンクションテンプレートは、 次のような配列をBase64形式に変換したデータ を受け取る想定で作られています。
[
{
"target": "002",
"rank": 1,
"related": "005",
"name": "チェスターコート",
"price": 15600,
"image": "https://placehold.jp/703e3e/ffffff/350x430.png?text=005",
"url": "https://example.com/items/005/"
},
{
"target": "002",
"rank": 2,
"related": "001",
"name": "デニムジャケット",
"price": 9800,
"image": "https://placehold.jp/703e3e/ffffff/350x430.png?text=001",
"url": "https://example.com/items/001/"
},
...
]
このデータは、Datahubクエリを使って対象アイテムID毎に生成され、次のようにCraft KVSの1レコード内にまとめて格納されます。
| key | value |
|---|---|
| xxxx-item_recommend-001 | {"kvs_key":"item_recommend-001","data_base64":"aaaa..."} |
| yyyy-item_recommend-002 | {"kvs_key":"item_recommend-002","data_base64":"bbbb..."} |
(※ xxxx,yyyyはkvsのhotspotを避けるためにkeyに付与するhash値です。詳細は後述します。)
ちなみに、Craft KVSのkeyにある xxxx-item_recommend-001 の 001 の部分が、関連アイテムを取得する元となる対象アイテムID (target) に該当します。最終的には、この対象アイテムIDをリクエストに含めてファンクション実行し、Craft KVSのvalue内にある関連アイテム情報 (related) を返したいわけです。
この形式でダミーデータを抽出するためのサンプルクエリを用意しました。
WITH items AS (
SELECT '001' AS item_id, 'デニムジャケット' AS name, 'https://example.com/items/001/' AS url, 'https://placehold.jp/703e3e/ffffff/350x430.png?text=001' AS image, 9800 AS price UNION ALL
SELECT '002', 'フラワープリントワンピース', 'https://example.com/items/002/', 'https://placehold.jp/703e3e/ffffff/350x430.png?text=002', 12600 UNION ALL
SELECT '003', 'クルーネックニット', 'https://example.com/items/003/', 'https://placehold.jp/703e3e/ffffff/350x430.png?text=003', 5800 UNION ALL
SELECT '004', 'セットアップ', 'https://example.com/items/004/', 'https://placehold.jp/703e3e/ffffff/350x430.png?text=004', 9800 UNION ALL
SELECT '005', 'チェスターコート', 'https://example.com/items/005/', 'https://placehold.jp/703e3e/ffffff/350x430.png?text=005', 15600
)
, relations AS (
SELECT '001' AS target, 1 AS rank, '002' AS related UNION ALL
SELECT '001' AS target, 2 AS rank, '003' AS related UNION ALL
SELECT '001' AS target, 3 AS rank, '004' AS related UNION ALL
SELECT '002' AS target, 1 AS rank, '005' AS related UNION ALL
SELECT '002' AS target, 2 AS rank, '001' AS related UNION ALL
SELECT '002' AS target, 3 AS rank, '003' AS related
)
, recommend_raw AS (
SELECT
r.target
, r.rank
, r.related
, i.name
, i.price
, i.image
, i.url
FROM relations AS r
INNER JOIN items AS i
ON r.related = i.item_id
)
SELECT
CONCAT('item_recommend_api-', target) AS kvs_key
, TO_BASE64(CAST(TO_JSON_STRING(ARRAY_AGG(r ORDER BY rank)) AS BYTES)) AS data_base64
FROM recommend_raw AS r
GROUP BY target
このクエリでは、ダミーのアイテムマスタとダミーの関連性データを使って、アイテムID毎の関連アイテムデータを出力しています。最終的には、それをARRAY_AGGで1レコードのオブジェクト配列に集約した上で、JSON文字列に変換し、さらにBase64エンコードします。
ダミーデータで検証する場合は、上記内容でDatahubクエリを作成してください。
なお、実運用で使う場合はKARTEのイベントなどから実際のレコメンドデータを抽出した上で、同様のデータ変換を行ってください。Datahubのクエリコレクションにある「商品軸での商品レコメンド」というクエリをベースにカスタマイズするのがおすすめです。
2. Craft KVSにDatahubクエリ結果を格納する
作成したDatahubクエリの結果をCraft KVSに連携します。
詳しい手順は、次の記事に記載されています。
Datahubクエリの結果をCraft kvsに格納してみた
すでにクエリは作成済みなので、次の2つの手順を実施してください。
2. Craft Functionsのファンクションを作成する- 今回はCraft KVSのkeyにhash prefixを付けたいので、ファンクションの変数にある
APPEND_HASH_PREFIXはtrueに設定します
- 今回はCraft KVSのkeyにhash prefixを付けたいので、ファンクションの変数にある
3. Datahubジョブフローを作成する
Datahubジョブフローを作成できたら、Craft KVSにデータを連携するため、そのジョブフローを実行してください。
ジョブフローの実行が完了し、Craft KVSに問題なくレコード追加できたら次の手順に進みましょう。
3. Craft Functionsのファンクションを作成する
Craft Functionsのファンクションを作成します。作成手順は次の通りです。
-
[Craft > ファンクション > 新規作成 > テンプレートから作成] を選択します
-
「 外部から利用可能なアイテムレコメンドAPI 」というテンプレートを検索し[取得]ボタンをクリックします
-
[反映] ボタンをクリックします
-
[設定 > ファンクションのタイプ] で「HTTPタイプ」を選択します
-
[変数] タブで変数の値を設定してください
- ひとまずデフォルト値のままでも問題ありません
-
適当なファンクション名をつけて [デプロイ] します
なお、テンプレートのソースコードはGitHubで公開しています。
craft-codes/craft-functions/item-recommend-api at main · plaidev/craft-codes
実際に動かしてみる
実際に動作を確認してみましょう。
-
作成したファンクションの編集画面から [設定] タブを開き、エンドポイントURLをコピーします
-
エンドポイントURLの末尾に
?item_id=001を付けてWebブラウザで開き、関連アイテムの情報を取得できることを確認します -
item_idを
?item_id=002に変えてみて、結果が変わることを確認します
もちろん、Webブラウザ以外の方法で同様のGETリクエストを送っても、同じレコメンドデータを取得することができます。
挙動に問題がある場合は、次の点を確認してみてください。
- Datahubジョブフローは実行済みか?実行時のエラーが発生していないか?
- ファンクションのログにエラーが出ていないか?
- 「KVS更新用」ファンクション
- 「API用」ファンクション
補足
サーバー間認証の実装について
もしレコメンドAPIの機能を特定の外部サーバーからしか利用させたくない場合は、追加の認証を「API用」ファンクション側に実装してください。
たとえば次のような実装方法が考えられます。
- 外部サーバー側とファンクション側で共通の認証情報を持たせておく
- レコメンドAPIへのリクエストにその認証情報を含める
- 「API用」ファンクションは、リクエストに含まれているその認証情報が正しいことが検証できたらレコメンドデータを返す
こうした実装により、認められたサーバー以外からのリクエストにはレコメンド結果を返却しないようにできます。ただし、エンドポイントにリクエストが送られた時点でファンクションの実行数は消費してしまいます。
Craft KVSのレコードサイズ上限を回避する方法
今回の記事は、レコメンドしたいアイテムの名前や画像URLなどもCraft KVS内に保存する想定で書かれています。それによりCraft KVSのレコードサイズ上限に抵触しやすくなっています。
たとえば自社システム側でアイテムIDからそのアイテム情報を取得できるようなAPIを利用可能なとき、レコメンドしたいアイテムのID群だけ取得できれば、そのアイテムの詳細情報は当該API経由で取得できます。
その場合は、次のようにID以外の関連アイテム情報をCraft KVSに保存しないようにすることでレコードサイズを節約できます。
before
[
{
"target": "002",
"rank": 1,
"related": "005",
"name": "チェスターコート",
"price": 15600,
"image": "https://placehold.jp/703e3e/ffffff/350x430.png?text=005",
"url": "https://example.com/items/005/"
},
{
"target": "002",
"rank": 2,
"related": "001",
"name": "デニムジャケット",
"price": 9800,
"image": "https://placehold.jp/703e3e/ffffff/350x430.png?text=001",
"url": "https://example.com/items/001/"
},
...
]
after(例1)
[
{
"target": "002",
"rank": 1,
"related": "005",
},
{
"target": "002",
"rank": 2,
"related": "001",
},
...
]
after(例2)
["005","001",...]
おわりに
今回はアイテムレコメンドAPIを KARTE Datahub と Craft Functions で実装する方法について紹介しました。
Webサイト上でのレコメンド表示であればKARTEの接客サービスやKARTE Blocks経由で実現できます。しかし、たとえばネイティブアプリのネイティブコンポーネント上でアイテムレコメンドを直接利用したい場合などはKARTE単独での実現は難しいです。
この記事で紹介したレコメンドAPIを実装すれば、こうした制約に縛られずに、KARTE上のデータを元にしたアイテムレコメンドを様々なチャネルで利用できるようになります。
ぜひ皆さんも試してみてください!