给MongoDB的聚合命令/管道添加行号

12

这个想法是为了在mongodb的聚合命令/管道中返回一种行号,类似于关系型数据库中所使用的。它应该是一个唯一的数字,如果它与行数完全匹配则不重要。

对于如下查询:

[ { $match: { "author" : { $ne: 1 } } }, { $limit: 1000000 } ]

我想要退货:

{ "rownum" : 0, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }
{ "rownum" : 1, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }
{ "rownum" : 2, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
{ "rownum" : 3, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }
{ "rownum" : 4, "title" : "Iliad", "author" : "Homer", "copies" : 10 }

在 MongoDB 中是否有可能生成这个 rownum


1
不,这是不可能的。你最好解释一下“为什么你认为你需要这个”。它通常用于SQL实现中的窗口分页结果,例如当项目被排序时。如果您愿意解释您的用例以解决问题,可能会有其他选项。 - Blakes Seven
这是一个改进 - 当数据量超过100百万,在从MongoDB数据源获取数据的BI工具中使用字符串作为ID确实是一个糟糕的想法。除非有真正的解决方法... - ic3
在 MongoDB 中给行添加数字(无论如何也不能这样做)意味着需要通过所有结果/数据(可能是在选择“页面”之前)并逐个进行分配。因此,根据架构处理事物的方式,它不可能是一种改进方式。我给你提供了两个选项:1. 接受“不能做到”。2. 解释您的用例,并可能获得比您迄今为止想到的更好的替代方法。在我看来,一个是死路一条,而另一个可能会有所作为。 - Blakes Seven
3个回答

11

对于大查询的性能不确定,但这至少是一个选项。

您可以通过分组/推送将结果添加到数组中,然后使用includeArrayIndex进行展开,就像这样:

[
  {$match: {author: {$ne: 1}}},
  {$limit: 10000},
  {$group: {
    _id: 1,
    book: {$push: {title: '$title', author: '$author', copies: '$copies'}}
  }},
  {$unwind: {path: '$book', includeArrayIndex: 'rownum'}},
  {$project: {
    author: '$book.author',
    title: '$book.title',
    copies: '$book.copies',
    rownum: 1
  }}
]

如果你的数据库包含大量记录,并且你想要分页,你可以使用$skip阶段,然后使用$limit 10或20(或任何你想要每页显示的数量),只需将$skip阶段的数字添加到你的行号中即可获得真实位置,而不必将所有结果推送以枚举它们。


3
看起来非常低效,但这正是我所需要的并且回答了我的问题。 - villasv
1
请注意,https://docs.mongodb.com/manual/reference/operator/aggregation/group/#pipe._S_group - $group 不会对其输出文档进行排序。 - barrypicker

5

从Mongo 5开始,这是新的$setWindowFields聚合操作符和其$documentNumber操作的完美使用案例:

// { x: "a" }
// { x: "b" }
// { x: "c" }
// { x: "d" }
db.collection.aggregate([
  { $setWindowFields: {
    sortBy: { _id: 1 },
    output: { rowNumber: { $documentNumber: {} } }
  }}
])
// { x: "a", rowNumber: 1 }
// { x: "b", rowNumber: 2 }
// { x: "c", rowNumber: 3 }
// { x: "d", rowNumber: 4 }

$setWindowFields 允许我们在处理每个文档时考虑前面或后面的文档。这里我们只需要知道文档在整个集合(或聚合中间结果)中的位置信息,这由$documentNumber提供。

请注意,我们按_id排序是因为sortBy参数是必需的,但实际上,由于您不关心行的顺序,它可以是任何值。


3
另一种方法是使用"$function"跟踪row_number。
[{ $match: { "author" : { $ne: 1 } }}  , { $limit: 1000000 },
{
    $set: {
      "rownum": {
        "$function": {
          "body": "function() {try {row_number+= 1;} catch (e) {row_number= 0;}return row_number;}",
          "args": [],
          "lang": "js"
        }
      }
    }
  }]

我不确定这会不会捣乱什么!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接