在多个文档上使用 $unwind

Using $unwind on multiple documents

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

我的问题是如何对多个文档的集合执行此操作。我想 trim 每个数组中的嵌套数组。但是,一旦我 $unwind$skip$limit 仅根据第一个数组的编号才有意义。

有没有办法 运行 在集合中的每个文档而不是整个集合上使用这种管道?我希望在聚合管道中做的事情是可能的吗?使用 Map-Reduce 显然是可能的,但是这样做比 运行 n+1 查询分别 $unwind 每个文档要慢。


编辑

下面是一个示例记录。

{
  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]]]
}

这里记录的是一本大书的正文,存储为一个深度为3的数组。同一个title可以有很多不同的vtitletext可以很大。

我想 select 一小部分包含的文本,由索引标识,来自许多书籍的集合中的每本书 - 每个文档的一部分 return编辑。

例如,[3,3] 的输入参数将 return 记录为:

{ "text" : ["Book 4, Chapter 4, Line 1", "Book 4, Chapter 4, Line 2", ...] }

TL; DR

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

理由

虽然不太清楚您想要获得的确切结果,但意图显然是您希望能够在您的集合中找到一组匹配的文档并转换(即映射)这些文档(通过切片您的嵌套数组以生成一个扁平的字符串列表)。搜索是无关紧要的,因为您可以在映射之前或之后进行搜索并且仍然满足您的约束。因此,我将只谈论映射。

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

1) 查询

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

  1. 虽然这里有一个 $slice 运算符,但它不处理嵌套数组。
  2. $ 运算符只允许您获取数组中的第一个条目,这也不足以获取数组中的任意位置。
  3. $elemMatch只允许您从数组中获取一个字段 - 这也不足以满足您的需求。

此外,您不能在查询上链接投影,因此您不能以某种狡猾的方式将多个投影放在一起以对数据进行多次切片。

简而言之,这行不通。

2) 聚合管道

不幸的是,在 v3.1. You are therefore limited to $project 之前聚合管道没有切片运算符,或者可能巧妙地使用了其他运算符(根据您链接的文章)。

先说投影算子。虽然你可以 operate on array fields, you are limited to just getting the size for now. You could try to use set logic 但这本质上是无序的,所以你不能在这里获得第 N 个条目。

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

遗憾的是,当您需要在最新的展开列表中找到下一个原始文档的开头时,这会失败。没有 operators 的组合允许您枚举展开的数组,然后在该枚举和原始文档上 select。

  1. $unwind 扩展数组,但不会为您提供随后匹配的索引,并且 $skip 无法跳到具有匹配条件的下一个文档。
  2. $redact 仍然使您处于原始文档的范围内,但随后遇到与 $project 相同的问题,即它无法对嵌套数组进行操作。

总之,这也是个半身像

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 个文档(因为每个文档现在都是聚合的键) .当数据集变大时,它也很慢。