ArangoDB AQL中(n)个数组的交集

ArangoDB AQL中(n)个数组的交集,arangodb,aql,Arangodb,Aql,场景是这样的:我有一个包含项的ArangoDB集合,另一个包含标记的集合。我使用的是一个图形,我有一个名为“Contains”的边集合,用于连接项目和标记。一个项目有多个标记 现在我正在尝试搜索包含多个标记的项目。例如,包含标签“摄影”、“肖像”和“人脸”的项目 我的一般方法是从每个标记顶点开始图遍历,并找到与该标记相关的项。那部分很好用。我得到一张物品清单 但我的任务的最后一部分是对所有列表进行交集,以便找到包含所有指定标记的项。我不知道该怎么做 我想做的事情是这样的: let tagname

场景是这样的:我有一个包含项的ArangoDB集合,另一个包含标记的集合。我使用的是一个图形,我有一个名为“Contains”的边集合,用于连接项目和标记。一个项目有多个标记

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

我的一般方法是从每个标记顶点开始图遍历,并找到与该标记相关的项。那部分很好用。我得到一张物品清单

但我的任务的最后一部分是对所有列表进行交集,以便找到包含所有指定标记的项。我不知道该怎么做

我想做的事情是这样的:

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中的列表进行交叉。。返回块?

我会考虑将标签存储在您的项目上。 Arangodb2.8包含的内容完全针对您的场景。从他们的:


这将提高性能,并消除对上述AQL的需要,从而简化应用程序。

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

让我们在图形查询中演示这一点:

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

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" 
]

ArangoDB 3.0引入了特殊(
ANY
ALL
NONE
ALL-IN
可用于测试左侧阵列中的每个元素是否也在右侧阵列中:

[ "red", "green", "blue" ] ALL IN [ "purple", "red", "blue", "green" ]
// true
请注意,这些运算符还不能使用索引。给定将标记直接嵌入文档的数据模型,解决方法是使用索引查找包含其中一个标记的所有文档(例如,使用第一个元素,
[“红色”、“绿色”、“蓝色”][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)

    })

嗯,我在我的系统中广泛使用标签,用它们来标记许多不同的项目类型,它们存在于不同的集合中。因此,在我看来,标签是系统中的一种“主要”数据类型,我有一种直觉,认为将它们降级为属性可能是个坏主意:-(然而,这可能只是旧的RDBMS思维…?在我看来,关系的概念似乎是graph DB原则的核心。给定一个标记,我可以快速找到与该标记相关的项。对于这个用例,这很好,我只需要能够在两个或多个结果集上进行交集…如果你考虑重复性的话e遍历所做的工作(收集所有边,过滤它们,然后遍历它们),为了使遍历速度更快/保持速度,您希望创建最少数量的边。“中心”(高度顶点)基本上是图形世界的减速带。标签本质上是创建集线器的。但就像其他任何东西一样,有时你也需要集线器。这都是折衷,对吗?这里有一篇博客文章可能会有所帮助:你在找方法做@dothebart吗?不,我不想做连接-我只需要一个文档列表,它们不需要连接。如果如果您有一个列表数组,并且想要获得交集,您可能需要使用APPLY()将数组展开,并将每个列表作为单独的参数传递:
APPLY(“交集”,[listA,listB,listC])
。它与
交集(listA,listB,listC)相同
,但输入数组可以有可变长度。我认为你的评论就是我一直在寻找的答案。尽管其他评论非常有用,但你的答案直接回答了我的问题。但我不能将其标记为正确答案,因为它是一条评论。我发布了一个扩展答案:如果它解决了你的问题,请接受并投票,t汉克斯!这些操作现在可以使用索引了吗?
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)

    })