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

时间:2016-03-13 20:37:10

标签: javascript node.js mongodb mongodb-query aggregation-framework

我有一个架构,Comment,如下所示。这是一个“评论”和“回复”系统,但每个评论和回复都有多个版本。当用户想要查看评论时,我想仅返回status APPROVED的最新版本。

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

我已经让父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()

使用replies子文档获得相同结果的任何帮助都将不胜感激。我想以这样的形式返回每个回复的最新APPROVED版本:

comment: {
  body: "The comment's body.",
  user: ObjectId(...),
  replies: [
    {
      body: "The reply's body."
      user: ObjectId(...)
    }
  ]
}

1 个答案:

答案 0 :(得分: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"
        }
    }
}}

所有这些都取决于你想要的字段,来自Reply或来自Version

无论哪个领域,因为你&#34;没有伤口&#34;两个数组,你$group返回&#34;两次&#34;。

  • Reply

  • 排序后获取$first
  • 使用$push重新构建"replies"数组

那也是一切。

如果你还在寻找方法&#34;排序&#34;数组&#34;就地&#34;没有使用$unwind,MongoDB还没有这样做。

对您的设计提出一些建议

作为一个注释,我看到你要去哪里,这是你想要的使用类型的错误模型。

存储&#34;修订历史记录&#34;在嵌入式结构内。您很少会在常规更新和查询操作中使用它,并且正如此所示,大多数情况下您只需要最新的&#34;。

所以,只需这样做,并存储一个&#34;标志&#34;指示&#34;修订&#34;如果真的有必要。然后,这些数据可以存储在主结构的外部,并且您不必跳过这些环节只是为了获得最新的版本&#34;在每一个请求上。