在Mongoose中对嵌套的子文档进行排序和分组

Noa*_*oah 6 javascript mongodb node.js mongodb-query aggregation-framework

我有一个架构,Comment如下所示.这是一个"评论"和"回复"系统,但每个评论和回复都有多个版本.当用户想要查看评论时,我想只返回最新版本statusAPPROVED.

const Version = new mongoose.Schema({
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  body: String,
  created: Date,
  title: String,
  status: {
    type: String,
    enum: [ 'APPROVED', 'OPEN', 'CLOSED' ]
  }
})

const Reply = new mongoose.Schema({
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  created: Date,
  versions: [ Version ]
})

const Comment = new mongoose.Schema({
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  created: Date,
  versions: [ Version ],
  replies: [ Reply ]
})
Run Code Online (Sandbox Code Playgroud)

我已让父母Comment用下面的代码显示我想要的内容.但是,我在将该文件应用于子文档时遇到了麻烦Reply.

const requestedComment = yield Comment.aggregate([
  { $match: {
    query
  } },
  { $project: {
    user: 1,
    replies: 1,
    versions: {
      $filter: {
        input: '$versions',
        as: 'version',
        cond: { $eq: [ '$$version.status', 'APPROVED' ] }
      }
    },
  }},
  { "$unwind": "$versions" },
  { $sort: { 'versions.created': -1 } },
  { $group: {
    _id: '$_id',
    body: { $first: '$versions.body' },
    title: { $first: '$versions.title' },
    replies: { $first: '$replies' }
  }}
])
.exec()
Run Code Online (Sandbox Code Playgroud)

任何帮助实现与replies子文档相同的结果将不胜感激.我想以这样的形式返回APPROVED每个回复的最新版本:

comment: {
  body: "The comment's body.",
  user: ObjectId(...),
  replies: [
    {
      body: "The reply's body."
      user: ObjectId(...)
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

Bla*_*ven 3

基本上,您只需要从现有管道继续执行相同的过程即可。但这一次要$unwind列出每个“回复”条目的“版本”以及$sort它们所在的位置。

因此,这些是您的管道的“附加”阶段。

// Unwind replies
{ "$unwind": "$replies" },
// Unwind inner versions
{ "$unwind": "$replies.versions" },

// Filter for only approved
{ "$match": { "replies.versions.status": "APPROVED" } },

// Sort on all "keys" and then the "version" date
{ "$sort": { 
    "_id": 1,
    "replies._id": 1,
    "replies.versions.created": -1
}},

// Group replies to get the latest version of each
{ "$group": {
    "_id": {
        "_id": "$_id",
        "body": "$body",
        "title": "$title",
        "replyId": "$replies._id",
        "replyUser": "$replies.user",
        "replyCreated": "$replies.created"
    },
    "version": { "$first": "$replies.version" }
}},

// Push replies back into an array in the main document
{ "$group": {
    "_id": "$_id._id",
    "body": { "$first": "$_id.body" },
    "title": { "$first": "$_id.title" },
    "replies": {
        "$push": {
            "_id": "$_id.replyId",
            "user": "$_id.replyUser" },
            "created": "$_id.replyCreated",       // <-- Value from Reply
            "body": "$version.body",              // <-- Value from specific Version
            "title": "$version.title"
        }
    }
}}
Run Code Online (Sandbox Code Playgroud)

当然,这一切都取决于您想要哪些字段,是来自那里Reply还是来自Version.

无论哪个字段,由于您“取消缠绕”两个数组,所以您都会$group返回“两次”。

  • 一次获得$first排序后的物品Reply

  • "replies"再次使用以下命令重建数组$push

这就是全部了。

如果您仍在寻找不使用 就地“排序”数组的方法$unwind,那么 MongoDB 还没有这样做。


关于你的设计的一些建议

作为注释,我知道您要使用此方法,并且对于您想要的使用类型来说,这是错误的模型。

在嵌入式结构中存储“修订历史记录”没有什么意义。您很少会在一般更新和查询操作中使用它,正如本文所演示的,大多数时候您只需要“最新”。

因此,只需这样做,并在确实必要时存储一个指示“修订”的“标志”。然后,该数据可以存储在主结构的外部,并且您不必跳过这些环节只是为了在每个请求上获取“最新接受的版本”。