【问题标题】:Find people who bought the same games as someone else查找与其他人购买相同游戏的人
【发布时间】:2021-05-23 08:40:18
【问题描述】:

我正在使用 Amazon Neptune 创建和查询一个简单的图形数据库。我目前正在 AWS Jupyter Notebook 中运行我的代码,但最终会将代码移至 Python (gremlin_python)。正如您可能猜到的那样,我对 Gremlin 和图形数据库很陌生。

我有以下数据

g.addV('person').property(id, 'john')
 .addV('person').property(id, 'jim')
 .addV('person').property(id, 'pam')
 .addV('game').property(id, 'G1')
 .addV('game').property(id, 'G2')
 .addV('game').property(id, 'G3').iterate() 

g.V('john').as('p').V('G1').addE('bought').from('p').iterate()
g.V('john').as('p').V('G2').addE('bought').from('p').iterate()
g.V('john').as('p').V('G3').addE('bought').from('p').iterate()

g.V('jim').as('p').V('G1').addE('bought').from('p').iterate()
g.V('jim').as('p').V('G2').addE('bought').from('p').iterate()

g.V('pam').as('p').V('G1').addE('bought').from('p').iterate()

数据库中有 3 个人和 3 个游戏。我的目标是,给定一个人,告诉我哪些人购买了与他们相同的游戏,以及哪些游戏是那些

查看示例代码(主要来自https://tinkerpop.apache.org/docs/current/recipes/#recommendation)后,我有以下代码尝试查找购买的游戏

g.V('john').as('target')   Target person we are interested in comparing against
.out('bought').aggregate('target_games') // Games bought by target
.in('bought').where(P.neq('target')).dedup() // Persons who bought same games as target (excluding target and without duplicates)
.group().by().by(out("bought").where(P.within("target_games")).count()) // Find persons, group by number of co owned games
.unfold().order().by(values, desc).toList() // Unfold to create list, order by greatest number of common games

这给了我结果:

  • {v[jim]: 2}
  • {v[pam]: 1}

这告诉我 jim 有 2 个与 john 相同的游戏,而 pam 只有 1 个。但我希望我的查询返回他们共同拥有的实际游戏(仍按最常见游戏的顺序排列):

  • {v[jim]: ['G1', 'G2']}
  • {v[pam]: ['G1]}

感谢您的帮助。

【问题讨论】:

  • 我在下面的答案中提供了几种不同的方法来做到这一点。您可能希望将问题标题更改为“查找拥有我拥有的游戏的人”之类的内容。当前标题不会帮助其他人搜索此类问题。感谢您提供示例图。这在回答问题时真的很有帮助。
  • @KelvinLawrence 在下面回答了所有问题,除了如何按常见游戏数量对最终结果进行排序。
  • 我添加了一个按共同游戏数量排序的查询版本

标签: python-3.x gremlin amazon-neptune


【解决方案1】:

这个查询有几种不同的写法。这是一种使用中间遍历 V 步骤的方法,该步骤已找到 John 的游戏来查找所有其他非 John 的人,查看他们的游戏并查看他们是否与 John 拥有的游戏相交。

gremlin> g.V('john').as('j').
......1>   out().
......2>   aggregate('owns').
......3>   V().
......4>   hasLabel('person').
......5>   where(neq('j')).
......6>   group().
......7>     by(id).
......8>     by(out('bought').where(within('owns')).dedup().fold())

==>[pam:[v[G1]],jim:[v[G1],v[G2]]]       

但是,中间遍历 V 方法并不是真正需要的,因为您可以只查看 Jown 拥有的游戏的传入顶点

gremlin> g.V('john').as('j').
......1>   out().
......2>   aggregate('owns').
......3>   in('bought').
......4>   where(neq('j')).
......5>   group().
......6>     by(id).
......7>     by(out('bought').where(within('owns')).dedup().fold())

==>[pam:[v[G1]],jim:[v[G1],v[G2]]]         
                   

最后,这是第三种方式,更快地应用dedup 步骤。这可能是三者中效率最高的。

gremlin> g.V('john').as('j').
......1>   out().
......2>   aggregate('owns').
......3>   in('bought').
......4>   where(neq('j')).
......5>   dedup().
......6>   group().
......7>     by(id).
......8>     by(out('bought').where(within('owns')).fold())

==>[pam:[v[G1]],jim:[v[G1],v[G2]]]    

根据 cmets 讨论更新。我不确定这是一个更简单的查询,但您可以从如下投影中提取 group

gremlin> g.V('john').as('j').
......1>   out().as('johnGames').
......2>   in('bought').
......3>   where(neq('j')).as('personPurchasedJohnGames').
......4>   project('johnGames','personPurchasedJohnGames').
......5>     by(select('johnGames')).
......6>     by(select('personPurchasedJohnGames')).
......7>   group().
......8>     by(select('personPurchasedJohnGames')).
......9>     by(select('johnGames').fold())

==>[v[pam]:[v[G1]],v[jim]:[v[G1],v[G2]]]      

但实际上你可以进一步减少到

gremlin> g.V('john').as('j').
......1>   out().as('johnGames').
......2>   in('bought').
......3>   where(neq('j')).as('personPurchasedJohnGames').
......4>   group().
......5>     by(select('personPurchasedJohnGames')).
......6>     by(select('johnGames').fold())

==>[v[pam]:[v[G1]],v[jim]:[v[G1],v[G2]]]        

所以现在我们有很多选择!测量这些并查看是否有任何比其他更快会很有趣。一般来说,我倾向于避免使用as 步骤,因为这会导致路径跟踪被打开(耗尽内存),但因为我们在其他查询中已经有了as('j'),这并不是什么大问题。

再次编辑以添加结果排序

g.V('john').as('j').
   out().as('johnGames').
   in('bought').
   where(neq('j')).as('personPurchasedJohnGames').
   group().
     by(select('personPurchasedJohnGames')).
     by(select('johnGames').fold()).
   unfold().
   order().
    by(select(values).count(local),desc)

{v[jim]: [v[G1], v[G2]]}
{v[pam]: [v[G1]]}

【讨论】:

  • 太棒了!这真的很有帮助。我自己一直在尝试这个,无法让它工作,主要是因为我以不同的方式接近:(。所有这三种解决方案都是选择购买约翰游戏的人的游戏,并从列表中排除约翰游戏。是吗有可能在不遍历每个人的所有游戏的情况下做到这一点吗?我们从 John 开始,去他的游戏(收集它们),让所有其他购买这些游戏的人停在那里,因为我们拥有我们需要的一切,然后简单地分组游戏和人?
  • 可以说g.V('john').as('j').out().as('johnGames').in().where(neq('j')).as('personPurchasedJohnGames').project('johnGames','personPurchasedJohnGames').by(select('johnGames')).by(select('personPurchasedJohnGames')),但不能按人分组
  • 我添加了一个示例,说明如何对项目结果进行分组以及如何将其删除。
  • 太棒了!!最后一个是我一直试图达到的。我没有在.by(select('personPurchasedJohnGames')).by(select('johnGames').fold()) 中使用 .fold() 并且只玩了 1 场比赛。仍然只学习了一个月的 Graph 数据库。通过多个示例欣赏答案。如果可以的话,我会加倍支持:)
  • 很高兴它有帮助。请接受答案,以便其他人可以看到答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-03
  • 1970-01-01
  • 2013-03-31
  • 2019-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多