在多个文档上使用$ unwind

时间:2015-07-08 17:19:44

标签: mongodb aggregation-framework

这个问题 - Is it possible to get a slice of a slice in Mongo?介绍了如何在Mongo中获取切片。简而言之,使用聚合链到$skip$limit$unwind$skip$limit$group$unwind

我的问题是如何在多个文档的集合上执行此操作。我想修剪每个嵌套数组。但是,{I} $skip后,$limit$unwind只根据第一个数组的编号才有意义。

有没有办法在集合中的每个文档上运行这种管道,而不是整个集合?我想在聚合管道中做什么呢?显然可以使用Map-Reduce,但这样做要比单独运行n + 1个查询到{ title: "Text Title" vtitle: "Text Version Title" text: [[["Book1, Chapter 1, Line 1", "Book1, Chapter 1, Line 2"],["Book 1, Chapter 2, Line 1"]],[["Book 2, Chapter 1, Line 1]]] } 每个文档要慢。

修改

以下是一个示例记录。

vtitle

这里的记录是一本大书的文本,存储为深度为3的数组。对于相同的title,可以有许多不同的text,而[3,3]可能非常大。

我想从许多书籍集合中的每本书中选择一小部分由索引标识的包含文本 - 返回每个文档的一小部分。

例如,{ "text" : ["Book 4, Chapter 4, Line 1", "Book 4, Chapter 4, Line 2", ...] } 的输入参数将返回如下记录:

{{1}}

1 个答案:

答案 0 :(得分:1)

<强> TL; DR

我认为简短的回答是你不能真正做你想做的事。当前选项将等到v3.1或使用group聚合进行攻击(但我怀疑这对您的需求来说太慢了)。

<强>理由

虽然你想要得到的确切结果并不十分清楚,但目的很明显是你希望能够在你的集合中找到一组匹配的文档并转换(即映射)文档(通过切割嵌套的数组)产生一个扁平的字符串列表)。搜索无关紧要,因为您可以在映射之前或之后执行此操作,并且仍然满足您的约束。因此,我将谈谈映射。

这是MapReduce的一个自然用例,但您已明确将其从允许的答案中排除。所以,有3个选项,我将依次选择。

1)查询

由于您不允许多个查询,因此您唯一的选择是在请求中映射数据。这是通过the projection operators处理的。这些都行不通。

  1. 虽然这里有$slice运算符,但它不处理嵌套数组。
  2. $运算符只允许您获取数组中的第一个条目,这也不足以获取数组中的任意位置。
  3. $elemMatch只允许您从阵列中获取一个字段 - 这也不足以满足您的需求。
  4. 此外,您无法在查询上链接投影,因此您无法以多种巧妙的方式将多个投影放在一起以多次切片数据。

    简而言之,这不起作用。

    2)聚合管道

    不幸的是,在v3.1之前,聚合管道没有切片运算符。因此,您仅限于$project或者可能会对其他运算符进行一些巧妙的使用(根据您链接的文章)。

    首先考虑投影操作员。虽然你可以operate on array fields,但你现在只能获得大小。您可以尝试使用set logic,但这本质上是无序的,因此您无法在此处获取第N个条目。

    因此,直接操作显然不起作用。那么我们可以增强您链接的文章吗?此解决方法仅适用于一个文档,因为您不需要区分多个文档。因此,您可以放松数组以创建更大的文档列表,然后使用文档级范围操作来有效地进行切片。

    可悲的是,当你需要在最新的解包列表中找到下一个原始文档的开头时,这就会失败。没有operators的组合允许您枚举展开的数组,然后选择该枚举和原始文档。

    1. $unwind扩展了数组,但没有为您提供随后匹配的索引,$ skip无法跳转到具有匹配条件的下一个文档。
    2. $redact仍然让您处于原始文档的范围内,但后来遇到与$project相同的问题,即它无法在嵌套数组上运行。
    3. 简而言之,这也是一次破产。

      3)群组聚合

      此时我正要放弃,然后我注意到group aggregation。您可以为匹配的文档创建一个过滤器,但随后提供一个finalize的任意JavaScript函数,以便在返回之前转换该数据。这意味着您应该能够发出如下命令:

      db.runCommand(
         {
           group:
             {
               ns: 'books',
               key: { title: 1, text: 1 },
               cond: { },
               $reduce: function (curr, result) { },
               initial: { },
               finalize: function(result) {
                   // Insert your code here to slice the array - e.g.
                   result.text = result.text[0][0]
               }
            }
         })
      

      当然,正如文档here所述,如果您的数据库是分片的,您的结果大于16MB,或者您有超过20,000个文档(因为每个文档现在都是聚合的关键字),这不起作用。当数据集变大时,它也很慢。