2016-02-26 43 views
3

这种情况是这样的:我有一个包含项目的ArangoDB集合和另一个包含标签的集合。我正在使用图形,并且我有一个名为“Contains”的边缘集合,用于连接项目和标签。一个项目有多个标签。ArangoDB中(n)阵列的交互AQL

现在我正在尝试搜索包含多个标签的项目。例如。包含标签“摄影”,“肖像”和“面孔”的项目。

我的一般方法是从每个标签顶点开始图遍历并找到与该标签相关的项。这部分工作正常。我得到一个项目列表。

但我的任务的最后一部分是为了找到包含所有指定标签的项目的所有列表的交集。我无法解决如何做到这一点。

我想做的事是这样的:

let tagnames = SPLIT(@tagnames,',') 
let tagcollections = (
    FOR tagname IN tagnames 
    LET atag = (FOR t IN tags FILTER LOWER(t.text)==LOWER(tagname) RETURN t) 
    let collections = (FOR v IN 1..1 INBOUND atag[0] Contains RETURN v) 

    RETURN { tag: atag, collections: collections } 
) 

RETURN INTERSECTION(tagcollections) 

但是,这是行不通的:交集功能没有一个单独的列表上工作,但多个项目,像这样的:交会( listA,listB,listC ...)。

如何在FOR .. RETURN块中找到列表的交集?

+1

,并希望得到的路口,你可能希望使用APPLY()来传播数组,并将每个列表作为单独的参数传递:'APPLY(“INTERSECTION”,[listA,listB,listC])''。它与'INTERSECTION(listA,listB,listC)'相同,但输入数组的长度可变。 – CoDEmanX

+0

我认为你的评论是我正在寻找的答案。尽管其他评论非常有用,但您的回答直接回答了我的问题。但我不能将它标记为正确的答案,当它是一个评论... –

+0

我张贴了一个扩展的答案:http://stackoverflow.com/a/38484463/2044940如果它解决您的问题,请接受并投票表决,谢谢! – CoDEmanX

回答

4

ArangoDB 3.0引入了特殊array comparison operatorsANYALLNONE)。 ALL IN可以用来测试是否左侧阵列中的每个元素也是右侧数组中:

[ "red", "green", "blue" ] ALL IN [ "purple", "red", "blue", "green" ] 
// true 

注意,这些运营商不能使用索引呢。给定一个将标记直接嵌入到文档中的数据模型,解决方法是使用索引来查找包含其中一个标记的所有文档(例如,采用第一个元素["red","green","blue"][0])以减少结果集而不进行完整的集合扫描,然后过滤后用ALL IN如果其他标签也都在名单:

LET tagsToSearchFor = [ "red", "green", "blue" ] 
FOR doc IN coll 
    FILTER tagsToSearchFor[0] IN doc.tags[*] // array index 
    FILTER tagsToSeachFor ALL IN doc.tags 
    RETURN doc 

ALL IN也可以用来为您的数据模型标签为一个单独的集合,但你将不能够使用的像上面的索引。例如:

FOR doc IN documents 
    LET tags = (
     FOR v IN INBOUND doc contains 
      RETURN v._key 
    ) 
    FILTER ["red", "green", "blue"] ALL IN tags 
    RETURN MERGE(doc, {tags}) 

或者,如果你想开始与标签的遍历和使用基于交叉路口的方法:如果你有名单的数组

LET startTags = ["red", "green", "blue"] // must exist 
LET ids = (
    FOR startTag IN DOCUMENT("tags", startTags) 
     RETURN (
      FOR v IN OUTBOUND startTag contains 
       RETURN v._id 
     ) 
) 
LET docs = APPLY("INTERSECTION", ids) 

FOR doc IN DOCUMENT(docs) 
    RETURN MERGE(doc, { 
     tags: (FOR tag IN INBOUND doc contains RETURN tag._key) 

    }) 
3

我会考虑将您的标签作为属性存储在您的项目上。 ArangoDB 2.8包含array indexes,它们完全针对您的情况。从他们blog post

{ 
    text: "Here's what I want to retrieve...", 
    tags: [ "graphdb", "ArangoDB", "multi-model" ] 
} 

FOR doc IN documents 
    FILTER "graphdb" IN doc.tags[*] 
    RETURN doc 

这应该是既具有更好的性能,消除了上述的AQL的需要,简化了您的应用程序。

+0

嗯。我在我的系统中广泛使用标签,使用它们标记很多不同的项目类型,这些项目类型存在于不同的集合中。因此,由于标签在我的系统中似乎是一种“主要”数据类型,我有一种直觉认为将它们降为属性可能是一个糟糕的主意:-(然而,这可能只是旧的RDBMS-思考......?关系的概念在我看来是图DB原理的核心,给定一个标签,我可以快速找到与该标签相关的项目。对于这个用例,这个工作正常,我只需要能够在两个或多个结果集上交叉... –

+0

如果您考虑遍历所做的重复性工作(收集所有边,过滤它们然后遍历它们),为了遍历/保持快速,您希望创建最小值“Hubs”(高度顶点)基本上就是图形世界的减速带, – mikewilliamson

+0

标签本质上是创造集线器,但是和其他所有的东西一样,有时候你需要它,这都是权衡的吗?帮助这里:https://mikewilliamson.wordpress.com/2015/07/16/d ata-modeling-with-arangodb/ – mikewilliamson

0

您可以确保在使用DISTINCT keyword的AQL查询结果中不会获得两次文档。

让我们在图形查询using the knows graph example demonstate这样的:

var examples = require("org/arangodb/graph-examples/example-graph.js"); 

var g = examples.loadGraph("knows_graph"); 
db._query("FOR oneGuy IN persons " + 
    "FOR v IN 1..1 OUTBOUND oneGuy GRAPH 'knows_graph' RETURN v.name").toArray() 
[ 
    "Charlie", 
    "Dave", 
    "Alice", 
    "Bob", 
    "Bob" 
] 

我们看到你的情况,鲍勃返回两次。现在,让我们添加不同的关键字:

db._query("FOR oneGuy IN persons " + 
    "FOR v IN 1..1 OUTBOUND oneGuy GRAPH 'knows_graph' RETURN DISTINCT v.name" 
).toArray() 
[ 
    "Bob", 
    "Alice", 
    "Dave", 
    "Charlie" 
]