KingFish
KingFish

Reputation: 9153

Pass Through Values Using Aggregation Framework

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

Answers (2)

Disposer
Disposer

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

RickN
RickN

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

Related Questions