How to get objects inside an array of objects that are inside a 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 in the message array of the perticular element. I only want to get objects inside the post array if the member array of that object contains the logged in user.

I have a Document Object ID (5548b2b79b9567341c77d352) and I have a registered User ID (5534b2992a104ed914435c31). How do you do the same in a mongoose?

+3


source to share


3 answers


You can use MongoDB aggregation framework to get the desired result. The aggregation pipeline will consist of an initial step that has a statement that filters documents based on the criteria above. The next step in the pipeline would be an operator that deconstructs the message array from the input documents to output the document for each item. The following filter only returns documents with member ID. The last step with the operator then passes over documents with only the specified fields: $match

$unwind

$match

$project

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 can use the same aggregation as below:

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

      

+2


source


Another solution besides the aggregation structure is to use the projection operator $ elemMatch .

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

      

+1


source


You can use Aggregate structure for $unwind

arrays of subdocuments (splits them into your own documents that you can match against). I would use the following query:

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

      

This will return a separate document message

in the result set for each subdocument message

that matches this member.

Using Mongoose constructs it would look something like this:

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

      

The first is $match

not strictly necessary, but

0


source







All Articles