基本的なグラフトラバーサルがわかったところで、もう少し難しいことに挑戦してみましょう。
アプリケーションで、ユーザーがフォローすべき他のユーザーに関するユーザー固有のレコメンデーションを生成したいと考えています。これらのレコメンデーションを生成する一般的な方法は、ユーザーがフォローしているのと同じような人々をフォローしている他のユーザーを探すことです。これらの類似したユーザーが共通してフォローしている他のユーザーがいる場合、ユーザーがそれらのユーザーもフォローしたいと考える可能性があることを示唆しています。
たとえば、次の図を見てください。
この図は、楕円で表される Users と、楕円から楕円への矢印で示される Follow の関係を示しています。この例では、左端の User である MyUser が上部の User である PopularPolly をフォローしています。また、SimilarSam と MirrorMax という 2 人の別のユーザーが PopularPolly をフォローしていることがわかります。これら 2 人の User はどちらも、InterestingIngrid という名前の別の User をフォローしています。SimilarSam と MirrorMax がフォローしている何人かを MyUser もフォローしているため、MyUser が InterestingIngrid をフォローすることに興味を持つ可能性は高いと言えます。
scripts/ フォルダには、findFriendsOfFriends.js というファイルがあります。そのファイルの内容は次のとおりです。
const gremlin = require('gremlin');
const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;
const Graph = gremlin.structure.Graph;
const neq = gremlin.process.P.neq
const without = gremlin.process.P.without
const order = gremlin.process.order
const local = gremlin.process.scope.local
const values = gremlin.process.column.values
const desc = gremlin.process.order.desc
const connection = new DriverRemoteConnection(`wss://${process.env.NEPTUNE_ENDPOINT}:8182/gremlin`,{});
const graph = new Graph();
const g = graph.traversal().withRemote(connection);
const findFriendsOfFriends = async (username) => {
return g.V()
.has('User', 'username', username).as('user')
.out('Follows').aggregate('friends')
.in_('Follows')
.out('Follows').where(without('friends'))
.where(neq('user'))
.values('username')
.groupCount()
.order(local)
.by(values, desc)
.limit(local, 10)
.next()
}
findFriendsOfFriends('davidmiller').then((resp) => {
console.log(resp.value)
connection.close()
})
他と同様に、始めるためには、大量のインポートと初期化の作業をこなす必要があります。興味深いのは、ファイルで定義されている findFriendsOfFriends 関数です。これは、役立つレコメンデーションを生成するために「友人の友人」を探すアプリケーションの関数に似ています。
この複雑なグラフクエリについて、順を追っておさらいしてみましょう。
まず、g.V() .has('User', 'username', username).as('user') 部分を使用して、グラフ内で関連する User を見つけます。この部分は、適切な User の頂点を見つけるために特定のユーザー名を使用します。as 関数を使用してその頂点を「ユーザー」として保存し、後ほどクエリでその頂点を参照できるようにします。
次に、このユーザーが現在フォローしている全員を検索します。これを行うには、 .out('Follows').aggregate('friends') を使用します。これにより、User の頂点からの Follow のエッジをトラバースして、フォローされているユーザーを見つけることができます。次に、これらを friends と呼ばれる変数に集約します。これは、後ほどクエリで参照できます。
次に、これらの同じユーザーをフォローしている他のユーザーを検索します。.in_('Follows') を使用すると、これらのユーザーに Follows のエッジポイントを持つすべての頂点を検索できます。
これで、要求されたユーザーに似たユーザーを検索することができました。次のステップでは、これらのユーザーがフォローしている他のユーザーを検索します。元のユーザーがこれらのユーザーに興味を抱く可能性が高いためです。これらを検索するには、.out('Follows').where(without('friends')) を使用します。これは、ユーザーからの Follows のラベルが付いたエッジを追います。where 句に留意してください。これは、元のユーザーの保存された friends 変数を使用して、元のユーザーがすでにフォローしているユーザーを除外します。既存の友達をレコメンデーションに含めたくないですよね!
次に、ユーザーが自身をフォローすることをレコメンドしないように、.where(neq('user')) 句を使用して元のユーザーを除外します。次に、.values('username') 句を使用して、検出された各ノードのユーザー名を取得します。
この時点で、グラフには、類似したユーザーから別のユーザーへの「Follows」のエッジごとに 1 つのエントリが含まれています。これは、結果に重複があることを意味します。すなわち、2 人の類似したユーザーが同じユーザーをフォローした場合、フォローされたユーザーは 2 回表示されます。あるユーザーが類似したユーザーによってフォローされた回数に基づき、そのフォローされたユーザーをグループ化できるので、これは非常に有用です。類似性がより高いユーザーによってフォローされているユーザーは、元のユーザーに関連している可能性がより高いと言えます。
ターミナルで次のコマンドを実行して、スクリプトを実行できます。
Map {
'paullaurie' => 23,
'thardy' => 20,
'ocarrillo' => 18,
'toddjones' => 18,
'michaelunderwood' => 17,
'ihensley' => 17,
'paulacruz' => 17,
'annette32' => 17,
'morenojason' => 16,
'bergjames' => 16 }
素晴らしい! これらは、特定のユーザーに対してレコメンドされるユーザーの上位 10 名です。これらのユーザーをフォローしていた類似のユーザーの数が示されています。