【问题标题】:Order results by number of coincidences in edge properties按边缘属性中的重合数对结果进行排序
【发布时间】:2020-04-02 05:05:54
【问题描述】:

我正在开发一个推荐其他用户的推荐系统。第一个结果应该是与“搜索者”用户最“相似”的用户。用户对问题的回答,以相同方式回答的问题的数量就是相似度。

问题是我不知道怎么写查询

所以用技术术语来说,我需要按具有特定属性值的边的数量对用户进行排序,我尝试了这个查询,我认为它应该可以工作,但它不起作用:

   let query = g.V().hasLabel('user');

   let search = __;
   for (const question of searcher.questions) {
      search = search.outE('response')
            .has('questionId', question.questionId)
            .has('answerId', question.answerId)
            .aggregate('x')
            .cap('x')     
   }

   query = query.order().by(search.unfold().count(), order.asc);

抛出这个 gremlin 内部错误:

org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet 不能转换为 org.apache.tinkerpop.gremlin.structure.Vertex

我也为每个问题尝试了多个.by(),但结果没有按巧合程度排序。

我该如何编写这个查询?

【问题讨论】:

    标签: gremlin tinkerpop tinkerpop3 gremlin-server azure-cosmosdb-gremlinapi


    【解决方案1】:

    当您 cap()aggregate() 时,它会返回一个 BulkSet,这是一个 Set,它计算每个对象在该 Set 中存在的次数。当您通过展开每个对象相关的计数大小来迭代它时,它的行为就像List。所以你得到你的错误,因为cap('x') 的输出是BulkSet,但是因为你在循环中构建search,你基本上只是在BulkSet 上调用outE('response'),这不是has() 的有效语法需要一个图形Element,例如错误指示的Vertex

    我想你会更喜欢类似的东西:

    let query = g.V().hasLabel('user').
                  outE('response');
    
    let search = [];
    for (const question of searcher.questions) {
      search.push(has('questionId', question.questionId).
                  has('answerId', question.answerId));
    }
    
    query = query.or(...search).
                  groupCount().
                    by(outV())
                  order(local).by(values, asc)
    

    我可能没有完全正确的 javascript 语法(我在 or() 中使用 spread syntax 只是为了快速传达需要发生的事情的想法)但基本上这里的想法是过滤符合您的问题标准的边缘然后使用groupCount() 计算这些边。

    如果您需要计算没有连接的用户,那么也许您可以切换到project() - 也许像:

    let query = g.V().hasLabel('user').
                  project('user','count').
                    by();
    
    let search = [];
    for (const question of searcher.questions) {
      search.push(has('questionId', question.questionId).
                  has('answerId', question.answerId));
    }
    
    query = query.by(outE('response').or(...search).count()).
                  order().by('count', asc);
    

    fwiw,我认为您可能会考虑为您的数据使用不同的架构,这可能会使此推荐算法更像图。一个想法可能是使问题/答案成为一个顶点(也许是一个“qa”标签)并让边从用户顶点到“qa”顶点。然后用户直接链接到他们给出的问题/答案。您可以通过边缘、直接关系轻松查看哪些用户给出了相同的问题/答案组合。这种变化使查询在询问“哪些用户回答问题的方式与用户‘A’相同?”时更加自然地流动。

    g.V().has('person','name','A').
      out('responds').
      in('responds').
      groupCount().
      order(local).by(values)
    

    通过该更改,您可以看到我们可以摆脱所有那些has() 过滤器,因为它们隐含在“响应”边中,这些边将它们编码到图形数据本身中。

    【讨论】:

    • 我必须在 .by(outV()) 之后添加 .unfold() 否则订单不正确,就像 order() 步骤不起作用,在更改后它工作得很好,所以谢谢非常!我会像你提到的那样改进架构。您认为通过改进架构可以成倍提高搜索速度吗?还是两者的速度差不多?
    • 还有一件事:您的查询从结果中删除了所有答案匹配为 0 的用户,我不希望将它们删除,只是希望它们位于结果的底部,是否有干净的方法来解决这个问题?我在 for 循环之后用这个丑陋的 hack 修复了它:search.push(__.has('questionId', searcher.questions[0].questionId)) 搜索所有用户都将拥有的响应边缘,因为没有过滤响应,所以 or() 步骤返回每个用户至少一个边缘,所以没有用户被删除
    • 还有一件事:由于我需要结果中的所有用户以及 0 个匹配用户,因此更难以遵循您的架构改进建议,因为在该建议中这些用户也将被删除。有解决办法吗?
    • 关于unfold(),你可以这样做或者只是order(local) - 我已经更新了我的答案。
    • 至于通过更改架构来提高速度,这很难说,但我知道您从对所有“人”顶点及其边缘的全局扫描到单个的索引查找“人”遍历他们回答的所有问题,然后再一步到回答问题的“人”顶点。通常,这会明显“更快”,但显然很大程度上取决于数据的形状。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-08
    • 2011-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多