【问题标题】:INTERSECTION of (n) arrays in ArangoDB AQLArangoDB AQL 中 (n) 个数组的交集
【发布时间】:2016-02-26 20:35:30
【问题描述】:

场景是这样的:我有一个包含项目的 ArangoDB 集合,以及另一个包含标签的集合。我正在使用图表,并且我有一个名为“包含”的边缘集合连接项目和标签。一个项目有多个标签。

现在我正在尝试搜索包含多个标签的项目。例如。包含标签“photography”、“portrait”和“faces”的项目。

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

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

我想做的是这样的:

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)

但是,它不起作用:INTERSECTION 函数不适用于单个列表,而是适用于多个项目,例如:INTERSECTION(listA, listB, listC...)。

如何使 FOR .. RETURN 块中的列表相交?

【问题讨论】:

  • 如果您有一个列表数组并且想要获取交集,您可能需要使用 APPLY() 来展开数组并将每个列表作为单独的参数传递:APPLY("INTERSECTION", [listA, listB, listC])。它与INTERSECTION(listA, listB, listC) 相同,但输入数组可以具有可变长度。
  • 我认为您的评论是我一直在寻找的答案。尽管其他 cmets 非常有用,但您的回答直接回答了我的问题。但是当它是评论时,我无法将其标记为正确答案...
  • 我发布了一个扩展答案:stackoverflow.com/a/38484463/2044940 如果它解决了您的问题,请接受并投票,谢谢!

标签: arangodb aql


【解决方案1】:

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)

    })

【讨论】:

  • 这些操作现在可以使用索引了吗?
【解决方案2】:

我会考虑将您的标签作为属性存储在您的商品上。 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 的需求,从而简化您的应用程序。

【讨论】:

  • 嗯。我在我的系统中广泛使用标签,使用它们来标记许多不同的项目类型,这些项目类型存在于不同的集合中。因此,由于标签在我看来是我系统中的“主要”数据类型,我有一种直觉,将它们降级为属性可能是个坏主意:-(然而,这可能只是旧的 RDBMS 思维......?在我看来,关系的概念是图数据库原则的核心。给定一个标签,我可以快速找到与该标签相关的项目。对于这个用例,这很好,我只需要能够在两个或多个结果集上进行交集...
  • 如果您考虑遍历所做的重复工作(收集所有边,过滤它们,然后遍历它们),为了使遍历保持/保持快速,您需要创建最少数量的边。 “集线器”(高度顶点)基本上是图世界的减速带。
  • 标签本质上是创建集线器。但就像其他一切一样,有时你想要那样。这都是取舍对吗?这里有一篇博文可能会有所帮助:mikewilliamson.wordpress.com/2015/07/16/…
  • 你在寻找join?的方法
  • @dothebart:不,我不打算加入 - 我只需要一个文档列表,它们不需要加入。
【解决方案3】:

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

让我们在图形查询using the knows graph example 中证明这一点:

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

我们看到您的情况,Bob 被退回两次。现在让我们添加 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" 
]

【讨论】:

    猜你喜欢
    • 2020-04-30
    • 2021-11-26
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多