如何获取 mongoose/mongodb 文档中对象数组中的对象?

How to get the objects inside an array of objects which is inside an document in mongoose/mongodb?

这是文档的结构。

{
    "_id" : ObjectId("5548b2b79b9567341c77d352"),
    "messages" : [
        {
            "subject" : "fresh subject ",
            "from" : ObjectId("5534b2992a104ed914435c31"),
            "_id" : ObjectId("5548b5dab9279faf1c1b8688"),
            "created" : ISODate("2015-05-05T12:21:46.261Z"),
            "read" : true,
            "message" : "fresh message ",
            "participants" : [
                ObjectId("5534b2992a104ed914435c31"), //logged in user
                ObjectId("5530af38576214dd3553331c")
            ]
        },
        {
            "subject" : " subjet",
            "from" : ObjectId("5530af38576214dd3553331c"),
            "_id" : ObjectId("5548b608b9279faf1c1b8689"),
            "created" : ISODate("2015-05-05T12:22:32.809Z"),
            "read" : true,
            "message" : "is fresh?",
            "participants" : [
                ObjectId("5530af38576214dd3553331c")
            ]
        }
    ],
    "participants" : [
        ObjectId("5534b2992a104ed914435c31"),
        ObjectId("5530af38576214dd3553331c")
    ],
    "__v" : 2
}

特定对象的消息数组中有多个对象。仅当该对象的参与者数组包含登录用户时,我才想获取消息数组中的对象。

我有文档的对象 ID (5548b2b79b9567341c77d352) 和登录用户 ID (5534b2992a104ed914435c31)。 如何在猫鼬中做同样的事情?

您可以使用 MongoDB 的 aggregation framework to get the desired result. The aggregation pipeline would consist of an initial step which has a $match operator that filters the documents based on the criteria above. The next pipeline stage would be the $unwind operator that deconstructs the messages array from the input documents to output a document for each element. A further $match filter then returns only the documents with the participant id. The final step with the $project 运算符,然后将仅包含指定字段的文档传递给结果:

db.collection.aggregate([
    {
        "$match": {
            "_id" : ObjectId("5548b2b79b9567341c77d352"),
            "messages.participants": ObjectId("5534b2992a104ed914435c31")
        }
    },
    {
        "$unwind": "$messages"
    },
    {
        "$match": {            
            "messages.participants": ObjectId("5534b2992a104ed914435c31")
        }
    },
    {
        "$project": {
            "_id": 0,
            "messages": 1
        }
    }

])

结果:

/* 0 */
{
    "result" : [ 
        {
            "messages" : {
                "subject" : "fresh subject ",
                "from" : ObjectId("5534b2992a104ed914435c31"),
                "_id" : ObjectId("5548b5dab9279faf1c1b8688"),
                "created" : ISODate("2015-05-05T12:21:46.261Z"),
                "read" : true,
                "message" : "fresh message ",
                "participants" : [ 
                    ObjectId("5534b2992a104ed914435c31"), 
                    ObjectId("5530af38576214dd3553331c")
                ]
            }
        }
    ],
    "ok" : 1
}

在 Mongoose 中,您可以使用相同的 aggregation 管道,如下所示:

// Using the pipeline builder
Model.aggregate({
        "$match: {
            ""_id" : ObjectId("5548b2b79b9567341c77d352"),
            "messages.participants": ObjectId("5534b2992a104ed914435c31")
        }
    })
    .unwind("messages")
    .match({ "messages.participants": ObjectId("5534b2992a104ed914435c31") })
    .project({
        "_id": 0,
        "messages": 1
    })
    .exec(function (err, res) {
        if (err) return handleError(err);
        console.log(res); 
    });

// Or the simple aggregate method
var pipeline = [
    {
        "$match": {
            "_id" : ObjectId("5548b2b79b9567341c77d352"),
            "messages.participants": ObjectId("5534b2992a104ed914435c31")
        }
    },
    {
        "$unwind": "$messages"
    },
    {
        "$match": {            
            "messages.participants": ObjectId("5534b2992a104ed914435c31")
        }
    },
    {
        "$project": {
            "_id": 0,
            "messages": 1
        }
    }

]

Model.aggregate(pipeline, function (err, res) {
    if (err) return handleError(err);
    console.log(res); 
});

您可以使用 Aggregation Framework to $unwind 子文档数组(将它们拆分为您可以匹配的自己的文档。我将使用的查询:

.aggregate([
    {$unwind: "$messages"},
    {$match:
        {"messages.participants": ObjectId("5534b2992a104ed914435c31")}
    }
])

这将 return 在结果集中为与该参与者匹配的每个 message 子文档创建一个单独的 message 文档。

使用 Mongoose 构造这看起来像:

Conversation
    .aggregate({$unwind: "messages"})
    .match({
        "messages.participants": mongoose.Types.ObjectId("5534b2992a104ed914435c31")
    })
    .exec(cb);

第一个 $match 不是绝对必要的,但

除了聚合框架之外的另一种解决方案是使用 $elemMatch 投影运算符。

例如:

var id = '5548b2b79b9567341c77d352';
var loggedInUserId = '5534b2992a104ed914435c31';
var projection = {
    participants: 1,
    messages: {
        $elemMatch: { participants: new ObjectId(loggedInUserId) }
    }
};

Model.findById(id, projection, function(err, result) {
    console.log(result);
});

这将输出:

{ _id: 5548b2b79b9567341c77d352,
  participants: [ 5534b2992a104ed914435c31, 5530af38576214dd3553331c ],
  messages: 
   [ { participants: [Object],
       message: 'fresh message ',
       read: true,
       created: Tue May 05 2015 09:21:46 GMT-0300 (BRT),
       _id: 5548b5dab9279faf1c1b8688,
       from: 5534b2992a104ed914435c31,
       subject: 'fresh subject ' } ] }