Mongoose选择subdoc字段

时间:2014-09-21 15:15:42

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

我有这样的架构:

mongoose.model "Ticket", {
  title: String
  body: String
  likes: Number
  comments: [{
    body: String
    upvotes: Number
    downvotes: Number
  }]
}

我的查询代码

q = @model.findOne {"comments._id": oid}, {"comments.$": 1}

q.select "comments.upvotes"
q.exec (err, result) =>
   console.log(result.comment[0].downvotes) # 6

如您所见,select不适用于subdocs,它也不返回选定的字段。如何解决?

1 个答案:

答案 0 :(得分:2)

这就是MongoDB处理数组元素的基本投影的方法。虽然你可以做这样的事情:

Model.findOne({}, { "comments.upvotes": 1 },function(err,doc) {

})

这样就可以从comments数组的子文档中返回匹配条件和所有数组元素的子文档中的“upvotes”字段,当然,你不能将它与使用{{3的选定位置投影相结合运算符。这基本上源于“理论”通常你实际上想要返回整个数组。所以这就是它一直有效的方式,不太可能很快改变。

为了得到你想要的东西,你需要positional $提供的文档操作的扩展功能。这使您可以更好地控制文档的返回方式:

Model.aggregate(
    [
        // Match the document containing the array element
        { "$match": { "comments._id" : oid } },

        // Unwind to "de-normalize" the array content
        { "$unwind": "$comments" },

        // Match the specific array element
        { "$match": { "comments._id" : oid } },

        // Group back and just return the "upvotes" field
        { "$group": {
            "_id": "$_id",
            "comments": { "$push": { "upvotes": "$comments.upvotes" } }
        }}
    ],
    function(err,docs) {


    }
);

或者在2.6版本的MongoDB现代版本中你甚至可以这样做:

Model.aggregate(
    [
        { "$match": { "comments._id" : oid } },
        { "$project": {
            "comments": {
                "$setDifference": [
                    { "$map": {
                        "input": "$comments",
                        "as": "el",
                        "in": {
                            "$cond": [
                                { "$eq": [ "$$el._id", oid ] },
                                { "upvotes": "$$el.upvotes" },
                                false
                            ]
                        }
                    }},
                    [false]
                ]
            }}
        }}
    ],
    function(err,docs) {

    }
)

使用aggregation framework$map运算符对数组内容进行“内联”过滤,而无需先处理$setDifference阶段。

因此,如果您想要更多地控制文档的返回方式,那么在使用嵌入式文档时,聚合框架就是这样做的。