Reputation: 9153
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?
Upvotes: 0
Views: 1576
Reputation: 6371
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
]
}
}
}
Upvotes: 2
Reputation: 13500
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.
Upvotes: 0