如何在MongoDB中使用数组位置$和投影聚合?

3

revisions是一个对象(键为entryId)时,我的聚合调用是有效的。

await Project.aggregate([
  { $limit: 1 },
  { $match: { _id: ObjectId(projectId) } },
  {
    $project: {
      limits: 1,
      revisions: { $slice: [`$revisions.${entryId}.${languageId}`, startIndex, PageSize] },
      totalRevisions: {
        $size: { $ifNull: [`$revisions.${entryId}.${languageId}`, []] },
      },
    },
  },
]);

现在我已经将带有entryId的revisions转换为数组,但我不确定如何获得相同的结果。我尝试过:

await Project.aggregate([
  { $limit: 1 },
  {
    $match: {
      _id: ObjectId(projectId),
      'revisions.entryId': ObjectId(entryId),
    },
  },
  {
    $project: {
      limits: 1,
      revisions: { $slice: [`$revisions.$.${languageId}`, startIndex, PageSize] },
      totalRevisions: {
        $size: { $ifNull: [`$revisions.$.${languageId}`, []] },
      },
    },
  },
]);

但是我收到了这个错误消息:
MongoError: 字段名不能以 $ 开头。
怎样才能在数组中使用 $slice 和 $ifNull?谢谢。
样例文档:
{
  "revisions" : [ 
    {
      "entryId" : ObjectId("5bbf8813c272e05171463bc4"),
      "5bbe76c6d3fb1a4f143f8304" : [ 
        {
          "authorId" : ObjectId("5b1c5384d75d9f3b0eb65c2a"),
          "revisionId" : ObjectId("5bbf8813c272e05171463bc7"),
          "updated" : "2018-10-11T17:27:47.842Z",
          "value" : "County"
        }
      ]
    }
  ]
}

首先,如果先应用 $limit ,为什么要使用 $match 无法理解!!!请同时发布示例文档和输出。 - Ashh
@AnthonyWinzlet我在想如果没有它,匹配将会在第一个匹配之后继续搜索所有文档吗?谢谢,已添加示例。 - Dominic
1个回答

1

在这里,管道阶段的顺序确实很重要。当您在$match之前使用$limit时,它会从$limit阶段找到的单个文档中过滤数据。

如果您在$limit之前使用$match,则它将从数据库中的所有集合中过滤文档,并在$limit阶段中返回单个文档。

之后,您可以尝试以下聚合操作

db.collection.aggregate([
  { "$match": {
    "_id": ObjectId(projectId),
    "revisions.entryId": ObjectId(entryId)
  }},
  {
    "$project": {
      "revisions": {
        "$map": {
          "input": "$revisions",
          "in": {
            "$arrayToObject": {
              "$map": {
                "input": {
                  "$filter": {
                    "input": { "$objectToArray": "$$this" },
                    "as": "ee",
                    "cond": { "$eq": ["$$ee.k", "5bbe76c6d3fb1a4f143f8304"] }
                  }
                },
                "as": "dd",
                "in": {
                  "k": "$$dd.k",
                  "v": { "$slice": [startIndex, 1 ] }
                }
              }
            }
          }
        }
      },
      "totalRevisions": {
        "$arrayElemAt": [
          {
            "$map": {
              "input": "$revisions",
              "in": {
                "$size": {
                  "$map": {
                    "input": {
                      "$filter": {
                        "input": { "$objectToArray": "$$this" },
                        "as": "ee",
                        "cond": { "$eq": ["$$ee.k", "5bbe76c6d3fb1a4f143f8304"] }
                      }
                    },
                    "as": "dd",
                    "in": {
                      "k": "$$dd.k",
                      "v": { "$slice": [startIndex, 1] }
                    }
                  }
                }
              }
            }
          },
          0
        ]
      }
    }
  }
])

但是,如果您刚开始进行项目,则我强烈建议您不要使用此结构,因为您的嵌套数组键是动态的,与动态键打交道总是像玩裸露的电线一样危险。


非常感谢您,哇,这看起来是一个复杂的聚合!这是一个新项目,所以您建议将语言放在数组中吗?尽可能地想要减少复杂性。在我的脑海中,我认为对象查找在计算上更快,但似乎Mongo更喜欢数组(尽管在这种情况下似乎不是!)。 - Dominic
1
Mongo对数组很舒适,但是在数组和嵌套数组上应用操作特别困难,尤其是当数组的键不是静态的时候。因此,在我看来,您应该为entryId创建另一个集合,然后可以继续进行操作。在开始项目之前,请参考$lookup聚合。 - Ashh

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