0

I have the following document schema in Mongo:

{ 
    "_id": "an id",
    "title": "my title",
    "muted": "true",
    "participants": [{ "userid":12345, "more": "data" }, ... ],
    "messages": [{ "message", "details " } ... { } ]
}

I'm trying to get an array of objects in this form:

[
    { 
        "_id": "an id", 
        "meta" 
        { 
            "title": "my title",
            "muted": "true",
            "participants": [12345, /* user ids only */ ],
        },
        "messages": [{ "message", "details " } ... { } ]
    }
]

I've got the aggregation working to generate messages and _id:

[
    { $match:
            {
                'participants.user_id': userId
            }
    },
    { $unwind: "$messages" },
    { $match: { 'messages.sent_at': { '$gte': new Date(date) }}},
    { $group: { _id: "$_id", messages: { $addToSet: "$messages" }}}
]

What is the magic I need to get the meta data?

2
  • In my solution, I change 'participants.user_id' to 'participants.userid' and removed $match because the sample document didn't have the sent_at filed Commented Jan 21, 2015 at 18:09
  • I updated the answer with 2 possibilities about participants Commented Jan 21, 2015 at 19:41

2 Answers 2

2

If you want just matched 'participants.userid': 12345 in output "participants"

db.collection.aggregate(
[    
    { $match:
            {
                'participants.userid': 12345
            }
    },
    { $unwind : "$participants"},    
    { $match:
            {
                'participants.userid': 12345
            }
    },
    { $unwind: "$messages" },    
    { $group: { _id: "$_id" , muted : { $first : '$muted'}, title : { $first : '$title'}, messages: { $addToSet: "$messages" }, participants: { $addToSet: "$participants.userid" }}},
    { $project : { "messages" : "$messages" ,'meta.muted': '$muted', 'meta.title': '$title', 'meta.participants': '$participants'} },
]
).result

If you want all user id in participants, no matter what is it.

db.collection.aggregate(
[
    { $match:
            {
                'participants.userid': 12345
            }
    },
    { $project : { "messages" : 1 ,"muted" : 1 , "title" : 1 , "messages" : 1 , "participants" : 1, "ids" : "$participants.userid"  } },
    { $unwind : "$participants"},    
    { $match:
            {
                'participants.userid': 12345
            }
    },
    { $unwind: "$messages" },    
    { $group: { _id: "$_id" , 
         muted : { $first : '$muted'}, title : { $first : '$title'}, 
         ids : { $first : '$ids'},
         messages: { $addToSet: "$messages" },
         participants: { $addToSet: "$participants.userid" }}},
    { $project : { "messages" : "$messages" ,'meta.muted': '$muted', 'meta.title': '$title', 'meta.participants': '$ids'} },
]
).result

output:

{
    "0" : {
        "_id" : "an id",
        "messages" : [ 
            {
                "message2" : "details2 "
            }, 
            {
                "message" : "details "
            }
        ],
        "meta" : {
            "muted" : "true",
            "title" : "my title",
            "participants" : [ 
                 12345
            ]
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

The only issue is participants is an array of an array. How can I get around this?
I updated the answer with adding { $unwind : "$participants"},
0

In the last $group statement, you'll need to tell Mongo what fields it should return. Currently, all that you ask it for are the _id and messages fields. Let's say that your aggregation returns three documents like this:

{ 
  "_id": 1111,
  "title": "my title",
  "muted": "true",
  "participants": [{ "userid":12345, "more": "data" }, ... ],
  "messages": { "message": "Foo" }
},
{ 
  "_id": 1111,
  "title": "my title",
  "muted": "true",
  "participants": [{ "userid":12345, "more": "data" }, ... ],
  "messages": { "message": "Bar" }
},
{ 
  "_id": 1111,
  "title": "my title",
  "muted": "true",
  "participants": [{ "userid":12345, "more": "data" }, ... ],
  "messages": { "message": "Baz" }
}

You're already asking Mongo for a list of all messages with the $push operator so we've got that covered. For the other fields, you can't do this:

{$group: {
  _id: "$_id", 
  title: 1,
  muted: "$muted"
}}

After all, how does Mongo know what to do with the three "my title" values? Or the three boolean values of muted? Make an array? Discard data? Concatenate them?

In this case, since all three values will always be the same (by definition), you could say "I don't care, just give me any one of them", "give me the second result", "give me the first" or "give me the last". The first two aren't possible, so we're left with either $first or $last:

{$group: {
  _id: "$_id", 
  title: {$first: "$title"},
  muted: {$first: "$muted"}
}}

Of course, it doesn't really matter which one you use in this case.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.