【问题标题】:How to improve this aggregate with many $projects如何使用许多 $projects 改进此聚合
【发布时间】:2016-06-21 22:54:08
【问题描述】:

我创建了一个聚合函数,我觉得它很长且非 DRY。我想知道有什么方法可以改进它。

我的Thread 模型有一个名为revisions 的子文档。该函数尝试获取具有statusAPPROVED 的最新修订版。

这是完整的模型。

{
  "_id": ObjectId("56dc750769faa2393a8eb656"),
  "slug": "my-thread",
  "title": "my-thread",
  "created": 1457249482555.0,
  "user": ObjectId("56d70a491128bb612c6c9220"),
  "revisions": [
    {
      "body": "This is the body!",
      "status": "APPROVED",
      "_id": ObjectId("56dc750769faa2393a8eb657"),
      "comments": [

      ],
      "title": "my-thread"
    }
  ]
}

这是我要改进的聚合函数。

Thread.aggregate([
  { $match: {
    slug: thread
  } },
  { $project: {
    user: '$user',
    created: '$created',
    slug: '$slug',
    revisions: {
      $filter: {
        input: '$revisions',
        as: 'revision',
        cond: { $eq: [ '$$revision.status', 'APPROVED' ] }
      }
    }
  } },
  { $sort: { 'revisions.created': -1 } },
  { $project: {
    user: '$user',
    created: '$created',
    slug: '$slug',
    revisions: { $slice: ["$revisions", 0, 1] }
  } },
  { $unwind: '$revisions'},
  { $project: {
    body: '$revisions.body',
    title: '$revisions.title',
    user: '$user',
    slug: '$slug',
    created: '$created'
  }}
])

【问题讨论】:

  • 请注意,您的示例文档中似乎缺少"revisions.created"。我还解释了为什么其中的 $sort 阶段没有做你认为它正在做的事情。

标签: javascript mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

嗯,你不能真的因为有$sort$unwind 故意介于两者之间的阶段。它也基本上是“错误的”,因为 $sort 不能重新排序数组,直到你先 $unwind 它。

那么最好使用$group$first 来代替,只从每个文档的排序中获取第一个元素:

Thread.aggregate([
  { "$match": {
    "slug": thread
  } },
  { "$project": {
    "user": 1,
    "created": 1,
    "slug": 1,
    "revisions": {
      "$filter": {
        "input": "$revisions",
        "as": "revision",
        "cond": { "$eq": [ "$$revision.status", "APPROVED" ] }
      }
    }
  } },
  // Cannot sort until you $unwind
  { "$unwind": "$revisions" },

  // Now that will sort the elements
  { "$sort": { "_id": 1, "revisions.created": -1 } },

  // And just grab the $first boundary for everything
  { "$group": {
    "_id": "$_id",
    "body": { "$first": "$revisions.body" },
    "title": { "$first": "$revisions.title" },
    "user": { "$first": "$user" },
    "slug": { "$first": "$slug" },
    "created": { "$first": "$created" }
  }}
])

您始终可以使用 $push 重新构造数组,然后应用 $arrayElemAt 而不是 $slice 以仅生成一个元素,但考虑到它需要在 $group 之后再添加一个 $project,这有点多余首先。

因此,即使有“一些”操作可以在不使用 $unwind 的情况下完成,但不幸的是,“排序”由 $filter 等函数生成的数组目前还无法完成,直到您 $unwind数组优先。


如果您不需要“需要”"revisions.created" 上的 $sort(在您的示例文档中明显缺失),那么您可以改为使用普通投影:

Thread.find(
    { "slug": slug, "revisions.status": "APPROVED" },
    { "revisions.$": 1 },
)

只有在对数组元素进行排序时才需要其他任何东西,因为$ 位置运算符无论如何都会返回第一个匹配的元素。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    • 2012-10-18
    • 1970-01-01
    • 1970-01-01
    • 2019-08-17
    相关资源
    最近更新 更多