Sachin Kalsi
Sachin Kalsi

Reputation: 135

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

Here is the structure of the document.

{
    "_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
}

There are several objects inside the messages array of a perticular object. I want to get the objects inside the messages array only if the participants array of that object contains logged in user.

I have object Id of the document (5548b2b79b9567341c77d352) and I have the logged in user id (5534b2992a104ed914435c31). How to do the same in mongoose ?

Upvotes: 4

Views: 1043

Answers (3)

victorkt
victorkt

Reputation: 14552

Another solution apart from the aggregation framework is to use the $elemMatch projection operator.

For example:

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

This will output:

{ _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 ' } ] }

Upvotes: 1

Explosion Pills
Explosion Pills

Reputation: 191729

You can use the Aggregation Framework to $unwind the subdocument arrays (splits them into their own documents that you can match against. The query I would use:

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

This will return a separate message document in the result set for each message subdocument that matches that participant.

Using Mongoose constructs this would look something like:

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

The first $match is not strictly necessary but

Upvotes: 0

chridam
chridam

Reputation: 103305

You could use MongoDB's 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 operator then passes along the documents with only the specified fields to the result:

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

])

Result:

/* 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
}

In Mongoose you could use the same aggregation pipeline as follows:

// 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); 
});

Upvotes: 2

Related Questions