Merge mongodb aggregation results of unread messages count per conversation member into document - mongodb

I want to aggregate unread messages count per member in a conversation group and seems my aggregation pipeline is working correctly, but, I don't know how to achieve the following results.
Please check the example.
Thread documents
/* 1 */
{
"_id" : ObjectId("60d4efa6a95f446051f31492"),
"latestMessage" : "Voluptatem eos officiis optio dolor est et.",
"type" : "FEED",
"users" : [
{
"_id" : ObjectId("60d4efa6a95f446051f31491"),
"displayName" : "Monique Connelly II"
},
{
"_id" : ObjectId("60d4efa6a95f446051f31490"),
"displayName" : "Ivory Jacobson DDS"
},
{
"_id" : ObjectId("60d4efa6a95f446051f3148f"),
"displayName" : "Ron Weimann"
}
],
"createdBy" : "60d4efa6a95f446051f3148f",
"createdAt" : ISODate("2021-06-24T20:48:38.537Z"),
"modifiedAt" : ISODate("2021-06-24T20:48:38.620Z"),
}
/* 2 */
{
"_id" : ObjectId("60d4efa6a95f446051f31493"),
"type" : "CONVERSATION",
"users" : [
{
"_id" : ObjectId("60d4efa6a95f446051f31491"),
"displayName" : "Monique Connelly II"
},
{
"_id" : ObjectId("60d4efa6a95f446051f31490"),
"displayName" : "Ivory Jacobson DDS"
},
{
"_id" : ObjectId("60d4efa6a95f446051f3148f"),
"displayName" : "Ron Weimann"
}
],
"createdBy" : "60d4efa6a95f446051f3148f",
"createdAt" : ISODate("2021-06-24T20:48:38.569Z"),
"modifiedAt" : ISODate("2021-06-24T20:48:38.569Z"),
}
Message documents
/* 1 */
{
"_id" : ObjectId("60d4efa6a95f446051f31494"),
"content" : "Itaque rerum facere neque fuga aspernatur dolorum.",
"deleted" : false,
"threadId" : ObjectId("60d4efa6a95f446051f31492"),
"type" : "TEXT",
"thread" : {
"$ref" : "threads",
"$id" : ObjectId("60d4efa6a95f446051f31492")
},
"readBy" : [
ObjectId("60d4efa6a95f446051f3148f")
],
"attributes" : [],
"createdBy" : "60d4efa6a95f446051f3148f",
"createdAt" : ISODate("2021-06-24T20:48:38.598Z"),
"modifiedAt" : ISODate("2021-06-24T20:48:38.598Z"),
}
/* 2 */
{
"_id" : ObjectId("60d4efa6a95f446051f31495"),
"content" : "Nisi suscipit iste magni voluptatem.",
"deleted" : false,
"threadId" : ObjectId("60d4efa6a95f446051f31492"),
"type" : "TEXT",
"thread" : {
"$ref" : "threads",
"$id" : ObjectId("60d4efa6a95f446051f31492")
},
"readBy" : [
ObjectId("60d4efa6a95f446051f3148f")
],
"attributes" : [],
"createdBy" : "60d4efa6a95f446051f3148f",
"createdAt" : ISODate("2021-06-24T20:48:38.610Z"),
"modifiedAt" : ISODate("2021-06-24T20:48:38.610Z"),
}
and more...
My aggregation pipeline
db.threads.aggregate([
{ "$lookup": { "from": "messages", "localField": "_id", "foreignField": "threadId", "as": "message"}},
{ "$unwind": "$message" },
{ "$unwind": "$users" },
{ "$unwind": "$message.readBy" },
{ "$group": {
"_id": {
"user_id": "$users._id",
"thread_id": "$_id",
},
"unread_messages": {
"$sum": {
"$cond": [
{ "$ne": [ "$users._id", "$message.readBy" ] },
1,
0
]
}
}
}},
// { "$replaceRoot": { "newRoot": { "$mergeObjects": ["$_id", { "count": "$unread_messages" }]} } }
])
Aggregation results
{
"_id" : {
"user_id" : ObjectId("60d4efa6a95f446051f31491"),
"thread_id" : ObjectId("60d4efa6a95f446051f31492")
},
"unread_messages" : 4.0
},
{
"_id" : {
"user_id" : ObjectId("60d4efa6a95f446051f3148f"),
"thread_id" : ObjectId("60d4efa6a95f446051f31492")
},
"unread_messages" : 4.0
},
{
"_id" : {
"user_id" : ObjectId("60d4efa6a95f446051f31490"),
"thread_id" : ObjectId("60d4efa6a95f446051f31492")
},
"unread_messages" : 4.0
}
Results are partially OK, but, I want to keep my original structure of a document and copy aggregation results accordingly to the thread id back to the original document
I would be very grateful if you help me with the aggregation pipeline
Expected results
{
"_id": ObjectId("60d4efa6a95f446051f31492"),
"latestMessage": "Voluptatem eos officiis optio dolor est et.",
"type": "LISTING",
"users": [{
"_id": ObjectId("60d4efa6a95f446051f31491"),
"displayName": "Monique Connelly II"
},
{
"_id": ObjectId("60d4efa6a95f446051f31490"),
"displayName": "Ivory Jacobson DDS"
},
{
"_id": ObjectId("60d4efa6a95f446051f3148f"),
"displayName": "Ron Weimann"
}
],
"createdBy": "60d4efa6a95f446051f3148f",
"createdAt": ISODate("2021-06-24T20:48:38.537Z"),
"modifiedAt": ISODate("2021-06-24T20:48:38.620Z"),
"message": [...],
"stats": [{
"_id": {
"user_id": ObjectId("60d4efa6a95f446051f31491"),
"thread_id": ObjectId("60d4efa6a95f446051f31492")
},
"unread_messages": 4.0
},
{
"_id": {
"user_id": ObjectId("60d4efa6a95f446051f3148f"),
"thread_id": ObjectId("60d4efa6a95f446051f31492")
},
"unread_messages": 4.0
},
{
"_id": {
"user_id": ObjectId("60d4efa6a95f446051f31490"),
"thread_id": ObjectId("60d4efa6a95f446051f31492")
},
"unread_messages": 4.0
}
]
}

I post a solution that works perfectly for my case with $first + $replateRoot operations.
Final results have different ids as not in the first post that is because I re-created documents
db.threads.aggregate([
{ "$lookup": { "from": "messages", "localField": "_id", "foreignField": "threadId", "as": "message"}},
{ "$unwind": "$message" },
{ "$unwind": "$users" },
{ "$unwind": "$message.readBy" },
{ "$group": {
"_id": {
"user_id": "$users._id",
"thread_id": "$_id",
},
"thread": {
"$first": "$$ROOT"
},
"unread_messages": {
"$sum": {
"$cond": [
{ "$ne": [ "$users._id", "$message.readBy" ] },
1,
0
]
}
}
}},
{ "$group": {
"_id": "$_id.thread_id",
"thread": { "$first": "$thread" },
"stats": {
"$push": {
"userId": "$_id.user_id",
"unreadMessages": "$unread_messages"
}
}
}},
{ "$replaceRoot": { "newRoot": { "$mergeObjects": ["$thread", { stats: "$stats" }]} } }
])
Final results
/* 1 */
{
"_id" : ObjectId("60d574e242e59a48b886c586"),
"latestMessage" : "Molestias quo quod occaecati exercitationem veniam eaque.",
"type" : "LISTING",
"users" : {
"_id" : ObjectId("60d574e242e59a48b886c584"),
"displayName" : "Meggan Vandervort"
},
"unreadMessages" : NumberLong(0),
"createdBy" : "60d574e242e59a48b886c582",
"createdAt" : ISODate("2021-06-25T06:17:06.547Z"),
"modifiedAt" : ISODate("2021-06-25T06:17:06.617Z")
"message" : {
"_id" : ObjectId("60d574e242e59a48b886c58d"),
"content" : "Velit dolores vel.",
"deleted" : false,
"threadId" : ObjectId("60d574e242e59a48b886c586"),
"type" : "TEXT",
"thread" : {
"$ref" : "threads",
"$id" : ObjectId("60d574e242e59a48b886c586")
},
"readBy" : ObjectId("60d574e242e59a48b886c582"),
"attributes" : [],
"createdBy" : "60d574e242e59a48b886c582",
"createdAt" : ISODate("2021-06-25T06:17:06.595Z"),
"modifiedAt" : ISODate("2021-06-25T06:17:06.595Z")
},
"stats" : [
{
"userId" : ObjectId("60d574e242e59a48b886c584"),
"unreadessages" : 6.0
},
{
"userId" : ObjectId("60d574e242e59a48b886c583"),
"unreadessages" : 6.0
},
{
"userId" : ObjectId("60d574e242e59a48b886c582"),
"unreadessages" : 6.0
}
]
}
and more...

Related

MongoDB Query that will $project document values in horizontal format

Im trying to create a query that will project or show the field in HORIZONTAL format. Below is my sample collection and the expected output. BTW, Im also using jaspersoft studio to create the report but i think my output should be done in mongodb query. Hope someone can help. Thanks!
"_id" : ObjectId("60ddc6f44c893c117141e9b9"),
"observationvalues" : [
{
"_id" : ObjectId("60ddc6f44c893c117141e9c3"),
"name" : "Systolic BP",
"resultvalue" : "88.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9c2"),
"name" : "Diastolic BP",
"resultvalue" : "66.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9c1"),
"name" : "Weight",
"resultvalue" : "90.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9c5"),
"name" : "Height",
"resultvalue" : null
}
],
"createdat" : ISODate("2021-07-01T13:45:24.679Z"),
"modifiedat" : ISODate("2021-07-01T13:45:24.679Z"),
"statusflag" : "A"
"_id" : ObjectId("60ddc6f44c893c117141e8b8"),
"observationvalues" : [
{
"_id" : ObjectId("60ddc6f44c893c117141e9b3"),
"name" : "Systolic BP",
"resultvalue" : "84.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9b2"),
"name" : "Diastolic BP",
"resultvalue" : "63.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9b1"),
"name" : "Weight",
"resultvalue" : "99.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9b5"),
"name" : "Height",
"resultvalue" : 172.00
}
],
"createdat" : ISODate("2021-07-02T13:45:24.679Z"),
"modifiedat" : ISODate("2021-07-02T13:45:24.679Z"),
"statusflag" : "A"
"_id" : ObjectId("60ddc6f44c893c117141e7b7"),
"observationvalues" : [
{
"_id" : ObjectId("60ddc6f44c893c117141e9a3"),
"name" : "Systolic BP",
"resultvalue" : "81.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9a2"),
"name" : "Diastolic BP",
"resultvalue" : "65.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9a1"),
"name" : "Weight",
"resultvalue" : "96.00"
},
{
"_id" : ObjectId("60ddc6f44c893c117141e9a5"),
"name" : "Height",
"resultvalue" : 165.00
}
],
"createdat" : ISODate("2021-07-03T13:45:24.679Z"),
"modifiedat" : ISODate("2021-07-03T13:45:24.679Z"),
"statusflag" : "A",
The expected Output will be:
{"createdat" : "2021-07-01T13:45:24.679Z", "Systolic BP" : 88.00 ,"Diastolic BP" : 66.00 ,"Weight": 90.00 ,"Height":null }
{"createdat" : "2021-07-01T13:45:24.679Z", "Systolic BP" : 84.00 ,"Diastolic BP" : 63.00 ,"Weight": 99.00 ,"Height":172.00 }
{"createdat" : "2021-07-03T13:45:24.679Z", "Systolic BP" : 81.00 ,"Diastolic BP" : 65.00 ,"Weight": 96.00 ,"Height":165.00 }
I have created my query but it is not having the output in one line grouped for each createdat field
{
$project : {
_id: {
"systolic": {"$cond": [ { "$eq": ["$name","Systolic BP" ] }, "$resultvalue","" ] },
"diastolic": {"$cond": [ { "$eq": ["$name","Diastolic BP" ] }, "$resultvalue","" ] },
"weight": {"$cond": [ { "$eq": ["$name","Weight" ] }, "$resultvalue","" ] },
"height": {"$cond": [ { "$eq": ["$name","Height" ] }, "$resultvalue","" ] },
"create": "$createdat"
}
}
},
Your expected data output could be done like this: playground
[
{
"$project": {
"createdat": 1,
"rows": {
"$arrayToObject": {
"$map": {
"input": "$observationvalues",
"in": {
"k": "$$this.name",
"v": {
"$convert": {
"input": "$$this.resultvalue",
"to": "double",
"onError": null,
"onNull": null
}
}
}
}
}
}
}
},
{
"$addFields": {
"rows.create": "$createdat"
}
},
{
"$replaceRoot": {
"newRoot": "$rows"
}
},
{
"$match": {
"Systolic BP": 84.00
}
}
]
To get them in one line you should look for some JSON formater.

Mongodb aggregate with cond and query value

I'm new to mongodb. I need to know how it is possible to query item for set to the value with aggregate
Data
[
{
"_id" : "11111",
"parent_id" : "99",
"name" : "AAAA"
},
{
"_id" : "11112",
"parent_id" : "99",
"name" : "BBBB"
},
{
"_id" : "11113",
"parent_id" : "100",
"name" : "CCCC"
},
{
"_id" : "11114",
"parent_id" : "99",
"name" : "DDDD"
}
]
mongoshell
Assume $check is false
db.getCollection('test').aggregate(
[
{
"$group": {
"_id": "$id",
//...,
"item": {
"$last": {
"$cond": [
{"$eq": ["$check", true]},
"YES",
* * ANSWER **,
}
]
}
},
}
]
)
So i need the result for item is all the name contain with same parent_id as string of array
Expect result
[
{
"_id" : "11111",
"parent_id" : "99",
"name" : "AAAA",
"item" : ["AAAA","BBBB","DDDD"]
},
{
"_id" : "11112",
"parent_id" : "99",
"name" : "BBBB",
"item" : ["AAAA","BBBB","DDDD"]
},
{
"_id" : "11113",
"parent_id" : "100",
"name" : "CCCC",
"item" : ["CCCC"]
},
{
"_id" : "11114",
"parent_id" : "99",
"name" : "DDDD",
"item" : ["AAAA","BBBB","DDDD"]
}
]
Try this..
Sample live demo
db.collection.aggregate([
{
"$group": {
"_id": "$parent_id",
"item": {
"$push": "$name"
},
"data": {
"$push": {
"_id": "$_id",
"name": "$name"
}
}
}
},
{
"$unwind": "$data"
},
{
"$project": {
"_id": "$data._id",
"parent_id": "$_id",
"name": "$data.name",
"item": 1
}
}
])

Lookup on array of ids mongodb

I've made several searches before posting, but I'm currently facing an issue.
I am working with Mongo 4 with Symfony4 and Mongo-odm. I need to correct my shell query, I will adapt it to doctrine-odm then.
I've two collections like that :
circuit :
{
"_id" : "5c6e6f7be05bd900a1682582",
"name" : "Awesome name",
"startDate" : ISODate("2019-02-13T05:48:24.000Z"),
"endDate" : ISODate("2019-02-13T12:30:50.000Z"),
"state" : "closed",
"type" : "I",
"agency" : "5c6cfe7ae05bd9007477f7ee",
"drivers" : [
"5c6cfe99e05bd90074783c99",
"5c6cfe99e05bd90074784d85",
],
"exportedAt" : ISODate("2019-02-13T12:32:02.000Z"),
"createdAt" : ISODate("2019-02-21T09:29:31.783Z"),
"updatedAt" : ISODate("2019-02-21T09:29:31.783Z"),
"messages" : [
"5c6e7125e05bd900a1683595",
"5c6e7125e05bd900a1683596",
"5c6e7125e05bd900a1683597",
"5c6e7126e05bd900a1683598"
],
"orders" : [
"5c6d0269e05bd900747a559c",
"5c6d0269e05bd900747a559c",
"5c6d0269e05bd900747a559c"
]
}
order :
{
"_id" : "5c6d04a9e05bd900747a7c40",
"startDate" : ISODate("2019-02-19T08:35:04.000Z"),
"endDate" : ISODate("2019-02-19T12:53:50.000Z"),
"state" : "closed",
"company" : 1982,
"weight" : 960.0,
"type" : "E",
"circuit" : "5c6d0307e05bd900747a6c39",
"agency" : "5c6cfe79e05bd9007477f580",
"client" : "5c6d0074e05bd9007479a7c1",
"createdAt" : ISODate("2019-02-20T19:45:54.392Z"),
"updatedAt" : ISODate("2019-02-20T19:45:54.392Z"),
}
From circuit, I want to retrieve orders documents instead of their id.
I tried request like that :
db.circuit.aggregate([
{ "$match":
{ "agency":
{ "$in":
[
"5c6cfe79e05bd9007477f559", "5c6cfe79e05bd9007477f55b", "5c6cfe79e05bd9007477f562", "5c6cfe79e05bd9007477f563", "5c6cfe79e05bd9007477f565",
"5c6cfe79e05bd9007477f566", "5c6cfe79e05bd9007477f57e", "5c6cfe79e05bd9007477f57f", "5c6cfe79e05bd9007477f580", "5c6cfe79e05bd9007477f582",
"5c6cfe79e05bd9007477f587", "5c6cfe79e05bd9007477f588", "5c6cfe79e05bd9007477f589", "5c6cfe79e05bd9007477f58a", "5c6cfe79e05bd9007477f590",
"5c6cfe79e05bd9007477f591", "5c6cfe79e05bd9007477f592", "5c6cfe79e05bd9007477f593", "5c6cfe79e05bd9007477f594", "5c6cfe79e05bd9007477f595",
"5c6cfe79e05bd9007477f596", "5c6cfe79e05bd9007477f59b", "5c6cfe79e05bd9007477f59c", "5c6cfe79e05bd9007477f5af", "5c6cfe79e05bd9007477f5b2",
"5c6cfe79e05bd9007477f5e5", "5c6cfe79e05bd9007477f66d", "5c6cfe79e05bd9007477f679"
]
}
}
},
{
"$lookup":
{
"from": "agency",
"localField": "agency",
"foreignField": "_id",
"as": "agency"
}
},
{"$unwind": "$agency"},
{
"$lookup":
{
"from": "order",
"localField": "orders",
"foreignField": "_id",
"as": "sub_orders"
}
},
{
$unwind: {
path: "$sub_orders",
preserveNullAndEmptyArrays: true
}
},
{
"$project":
{
"name": "$$ROOT.nom",
"startDate": "$$ROOT.startDate",
"endDate": "$$ROOT.endDate",
"state": "$$ROOT.state",
"type": "$$ROOT.type",
"agencyName": "$agency.name",
"sub_orders": "$sub_orders"
}
}
]);
Unfortunately, I just retrieve the first document of the array id of orders like that :
{
"_id" : "5c6e6f9ee05bd900a168268f",
"name" : "Circuit",
"startDate" : ISODate("2019-02-13T06:18:28.000Z"),
"endDate" : ISODate("2019-02-13T11:40:12.000Z"),
"state" : "closed",
"type" : "I",
"agencyName" : "Amazing Agency",
"sub_orders" : {
"_id" : "5c6d04a9e05bd900747a7c40",
"startDate" : ISODate("2019-02-19T08:35:04.000Z"),
"endDate" : ISODate("2019-02-19T12:53:50.000Z"),
"state" : "closed",
"company" : 1982,
"weight" : 960.0,
"type" : "E",
"circuit" : "5c6d0307e05bd900747a6c39",
"agency" : "5c6cfe79e05bd9007477f580",
"client" : "5c6d0074e05bd9007479a7c1",
"createdAt" : ISODate("2019-02-20T19:45:54.392Z"),
"updatedAt" : ISODate("2019-02-20T19:45:54.392Z"),
}
}
However I want to get as many order document as id in array ids like :
{
"_id" : "5c6e6f9ee05bd900a168268f",
"name" : "Circuit",
"startDate" : ISODate("2019-02-13T06:18:28.000Z"),
"endDate" : ISODate("2019-02-13T11:40:12.000Z"),
"state" : "closed",
"type" : "I",
"agencyName" : "Amazing Agency",
"sub_orders" : {
"_id" : "5c6d04a9e05bd900747a7c40",
"startDate" : ISODate("2019-02-19T08:35:04.000Z"),
"endDate" : ISODate("2019-02-19T12:53:50.000Z"),
"state" : "closed",
"company" : 1982,
"weight" : 960.0,
"type" : "E",
"createdAt" : ISODate("2019-02-20T19:45:54.392Z"),
"updatedAt" : ISODate("2019-02-20T19:45:54.392Z"),
},{
"_id" : ".....",
"startDate" : ISODate("2019-02-19T08:35:04.000Z"),
"endDate" : ISODate("2019-02-19T12:53:50.000Z"),
"state" : "closed",
"company" : 1982,
"weight" : 960.0,
"type" : "E",
"createdAt" : ISODate("2019-02-20T19:45:54.392Z"),
"updatedAt" : ISODate("2019-02-20T19:45:54.392Z"),
},{
"_id" : ".....",
"startDate" : ISODate("2019-02-19T08:35:04.000Z"),
"endDate" : ISODate("2019-02-19T12:53:50.000Z"),
"state" : "closed",
"company" : 1982,
"weight" : 960.0,
"type" : "E",
"createdAt" : ISODate("2019-02-20T19:45:54.392Z"),
"updatedAt" : ISODate("2019-02-20T19:45:54.392Z"),
}
}
Thanks for saving me ! :)
I managed to do what i wanted to with the following query, it takes me few hours... :)
db.circuit.aggregate([
{ "$match":
{ "agency":
{ "$in":
[
"5c6cfe79e05bd9007477f559", "5c6cfe79e05bd9007477f55b", "5c6cfe79e05bd9007477f562", "5c6cfe79e05bd9007477f563", "5c6cfe79e05bd9007477f565",
"5c6cfe79e05bd9007477f566", "5c6cfe79e05bd9007477f57e", "5c6cfe79e05bd9007477f57f", "5c6cfe79e05bd9007477f580", "5c6cfe79e05bd9007477f582",
"5c6cfe79e05bd9007477f587", "5c6cfe79e05bd9007477f588", "5c6cfe79e05bd9007477f589", "5c6cfe79e05bd9007477f58a", "5c6cfe79e05bd9007477f590",
"5c6cfe79e05bd9007477f591", "5c6cfe79e05bd9007477f592", "5c6cfe79e05bd9007477f593", "5c6cfe79e05bd9007477f594", "5c6cfe79e05bd9007477f595",
"5c6cfe79e05bd9007477f596", "5c6cfe79e05bd9007477f59b", "5c6cfe79e05bd9007477f59c", "5c6cfe79e05bd9007477f5af", "5c6cfe79e05bd9007477f5b2",
"5c6cfe79e05bd9007477f5e5", "5c6cfe79e05bd9007477f66d", "5c6cfe79e05bd9007477f679"
]
}
},
},
{
"$match":
{ "date": { "$lte": ISODate("2019-02-13T12:32:02.000Z") } }
},
{"$match":
{ "date": { "$gte": ISODate("2019-02-11T12:32:02.000Z") } }
},
{
"$lookup":
{
"from": "agency",
"localField": "agency",
"foreignField": "_id",
"as": "agency"
}
},
{"$unwind": "$agency"},
{
"$lookup":
{
"from": "order",
"localField": "orders",
"foreignField": "_id",
"as": "orders"
}
},
{ "$unwind": "$orders" },
{
"$lookup":
{
"from": "driver",
"localField": "drivers",
"foreignField": "_id",
"as": "drivers"
}
},
{ "$unwind": "$drivers" },
{ "$unwind": "$camions" },
{
"$group": {
"_id": "$_id",
"orders": { "$addToSet": "$orders" },
"drivers": { "$last": "$drivers" },
"agency": { "$addToSet": "$agency" },
"type": { "$addToSet": "$type" },
"state": { "$addToSet": "$state" },
"date": { "$addToSet": "$date" }
}
},
{ "$unwind": "$agency" },
{ "$unwind": "$date" },
{ "$unwind": "$type" },
{ "$unwind": "$state" }
{
"$project":
{
"unloaded_orders": {
$filter: {
input: '$orders',
as: 'o',
cond: { $eq: ['$$o.state', 'unloaded'] }
}
},
"unclosed_orders": {
$filter: {
input: '$orders',
as: 'o',
cond: { $in: ['$$o.state', ['added', 'edited', 'started']] }
}
},
"closed_orders": {
$filter: {
input: '$orders',
as: 'o',
cond: { $eq: ['$$o.state', 'closed'] }
}
},
"agency": "$agency",
"date": "$date",
"state": "$state",
"type": "$type",
"drivers": "$drivers"
}
}
]);
Hope it could be help someone :)

Mongodb $lookup joins all collection instead of matching object

so I am trying to do a $lookup with Mongodb but I have a strange output.
I have two collections, "sites" and "consumptions".
sites :
{
"_id" : ObjectId("5b26db6e7f59e825909da106"),
"siteId" : 49,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 497092,
"latitude" : 41.2161756,
"longitude" : -78.14809154,
"timezone" : "America/New_York",
"timezone_offset" : "-04:00",
"__v" : 0
}
consumptions :
{
"_id" : ObjectId("5b26db907f59e825909f3d2a"),
"timestamp" : 1325382000,
"dttm_utc" : ISODate("2012-01-01T00:40:00Z"),
"value" : 2.8956,
"estimated" : 0,
"anomaly" : "",
"site" : [
{
"_id" : ObjectId("5b26db727f59e825909da16a")
}
],
"__v" : 0
}
This is the $lookup I am trying to do :
db.consumptions.aggregate([
{
$lookup:
{
from: "sites",
localField: "site.id",
foreignField: "id",
as: "site"
}
}
])
The expected output would be to have the detail of the site in each consumption :
{
"_id" : ObjectId("5b26db907f59e825909f3d2a"),
"timestamp" : 1325382000,
"dttm_utc" : ISODate("2012-01-01T00:40:00Z"),
"value" : 2.8956,
"estimated" : 0,
"anomaly" : "",
"site" : [
{
"_id" : ObjectId("5b26db6e7f59e825909da106"),
"siteId" : 49,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 497092,
"latitude" : 41.2161756,
"longitude" : -78.14809154,
"timezone" : "America/New_York",
"timezone_offset" : "-04:00",
"__v" : 0
}
],
"__v" : 0
}
This is the output I am getting with the $lookup :
{
"_id" : ObjectId("5b26db907f59e825909f3d2a"),
"timestamp" : 1325382000,
"dttm_utc" : ISODate("2012-01-01T00:40:00Z"),
"value" : 2.8956,
"estimated" : 0,
"anomaly" : "",
"site" : [
{
"_id" : ObjectId("5b26db6e7f59e825909da0f3"),
"siteId" : 6,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 161532,
"latitude" : 34.78300117,
"longitude" : -106.8952497,
"timezone" : "America/Denver",
"timezone_offset" : "-06:00",
"__v" : 0
},
{
"_id" : ObjectId("5b26db6e7f59e825909da0f4"),
"siteId" : 8,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 823966,
"latitude" : 40.32024733,
"longitude" : -76.40494239,
"timezone" : "America/New_York",
"timezone_offset" : "-04:00",
"__v" : 0
}, ... (all the sites details are listed)
],
"__v" : 0
}
Thank you in advance for your help !
You need to first $unwind the site array to match site._id to the foreign field _id and then $group to rolling back into the arrays again.
db.collection.aggregate([
{ "$unwind": "$site" },
{ "$lookup": {
"from": Site.collection.name,
"localField": "site._id",
"foreignField": "_id",
"as": "site"
}},
{ "$unwind": "$site" },
{ "$group": {
"_id": "$_id",
"value": { "$first": "$value" },
"estimated": { "$first": "$estimated" },
"anomaly": { "$first": "$anomaly" },
"timestamp": { "$first": "$timestamp" },
"dttm_utc": { "$first": "$dttm_utc" },
"site": { "$push": "$site" }
}}
])
And if you have mongodb 3.6 then you can try this
db.collection.aggregate([
{ "$unwind": "$site" },
{ "$lookup": {
"from": Site.collection.name,
"let": { "siteId": "$site._id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$siteId" ] } } }
],
"as": "site"
}},
{ "$unwind": "$site" },
{ "$group": {
"_id": "$_id",
"value": { "$first": "$value" },
"estimated": { "$first": "$estimated" },
"anomaly": { "$first": "$anomaly" },
"timestamp": { "$first": "$timestamp" },
"dttm_utc": { "$first": "$dttm_utc" },
"site": { "$push": "$site" }
}}
])
Make sure you should put Site.collection.name correctly
I think that The $lookup doesn't work directly with an array.
try using $unwind first.

MongoDb aggregation query with $group and $push into subdocument

I have a question regarding the $group argument of MongoDb aggregations. My data structure looks as follows:
My "Event" collection contains this single document:
{
"_id": ObjectId("mongodbobjectid..."),
"name": "Some Event",
"attendeeContainer": {
"min": 0,
"max": 10,
"attendees": [
{
"type": 1,
"status": 2,
"contact": ObjectId("mongodbobjectidHEX1")
},
{
"type": 7,
"status": 4,
"contact": ObjectId("mongodbobjectidHEX2")
}
]
}
}
My "Contact" collection contains these documents:
{
"_id": ObjectId("mongodbobjectidHEX1"),
"name": "John Doe",
"age": 35
},
{
"_id": ObjectId("mongodbobjectidHEX2"),
"name": "Peter Pan",
"age": 60
}
What I want to do is perform an aggregate query on the "Event" collection and get the following result with full "contact" data:
{
"_id": ObjectId("mongodbobjectid..."),
"name": "Some Event",
"attendeeContainer": {
"min": 0,
"max": 10,
"attendees": [
{
"type": 1,
"status": 2,
"contact": {
"_id": ObjectId("mongodbobjectidHEX1"),
"name": "John Doe",
"age": 35
}
},
{
"type": 7,
"status": 4,
"contact": {
"_id": ObjectId("mongodbobjectidHEX2"),
"name": "Peter Pan",
"age": 60
}
}
]
}
}
The arguments I am using right now look as follows (shortened version):
"$unwind" : "$attendeeContainer.attendees",
"$lookup" : { "from" : "contactinfo", "localField" : "attendeeContainer.attendees.contact","foreignField" : "_id", "as" : "contactInfo" },
"$unwind" : "$contactInfo",
"$group" : { "_id": "$_id",
"name": { "$first" : "$name" },
...
"contact": { "$push": { "contact": "$contactInfo"} }
}
However, this leads to the "contact" array being on "Event" level (because of the grouping) instead of one document of the array being at each "attendeeContainer.attendees". How can I push the "contact" array to be at "attendeeContainer.attendees"? (as shown in the desired output above)
I tried things like:
"attendeeContainer.attendees.contact": { "$push": { "contact": "$contactInfo"} }
But mongodb apparently does not allow "." at $group stage.
Try running the following aggregation pipeline, the key is using a final $project pipeline to create the attendeeContainer subdocument:
db.event.aggregate([
{ "$unwind": "$attendeeContainer.attendees" },
{
"$lookup" : {
"from" : "contactinfo",
"localField" : "attendeeContainer.attendees.contact",
"foreignField" : "_id",
"as" : "attendeeContainer.attendees.contactInfo"
}
},
{ "$unwind": "$attendeeContainer.attendees.contactInfo" },
{
"$group": {
"_id" : "$_id",
"name": { "$first": "$name" },
"min" : { "$first": "$attendeeContainer.min" },
"max" : { "$first": "$attendeeContainer.max" },
"attendees": { "$push": "$attendeeContainer.attendees" }
}
},
{
"$project": {
"name": 1,
"attendeeContainer.min": "$min",
"attendeeContainer.max": "$min",
"attendeeContainer.attendees": "$attendees"
}
}
])
Debugging Tips
Debugging the pipeline at the 4th stage, you would get the result
db.event.aggregate([
{ "$unwind": "$attendeeContainer.attendees" },
{
"$lookup" : {
"from" : "contactinfo",
"localField" : "attendeeContainer.attendees.contact",
"foreignField" : "_id",
"as" : "attendeeContainer.attendees.contactInfo"
}
},
{ "$unwind": "$attendeeContainer.attendees.contactInfo" },
{
"$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"min" : { "$first": "$attendeeContainer.min" },
"max" : { "$first": "$attendeeContainer.max" },
"attendees": { "$push": "$attendeeContainer.attendees" }
}
}/*,
{
"$project": {
"name": 1,
"attendeeContainer.min": "$min",
"attendeeContainer.max": "$min",
"attendeeContainer.attendees": "$attendees"
}
}*/
])
Pipeline result
{
"_id" : ObjectId("582c789282a9183adc0b53f5"),
"name" : "Some Event",
"min" : 0,
"max" : 10,
"attendees" : [
{
"type" : 1,
"status" : 2,
"contact" : ObjectId("582c787682a9183adc0b53f3"),
"contactInfo" : {
"_id" : ObjectId("582c787682a9183adc0b53f3"),
"name" : "John Doe",
"age" : 35
}
},
{
"type" : 7,
"status" : 4,
"contact" : ObjectId("582c787682a9183adc0b53f4"),
"contactInfo" : {
"_id" : ObjectId("582c787682a9183adc0b53f4"),
"name" : "Peter Pan",
"age" : 60
}
}
]
}
and the final $project pipeline will give you the desired result:
db.event.aggregate([
{ "$unwind": "$attendeeContainer.attendees" },
{
"$lookup" : {
"from" : "contactinfo",
"localField" : "attendeeContainer.attendees.contact",
"foreignField" : "_id",
"as" : "attendeeContainer.attendees.contactInfo"
}
},
{ "$unwind": "$attendeeContainer.attendees.contactInfo" },
{
"$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"min" : { "$first": "$attendeeContainer.min" },
"max" : { "$first": "$attendeeContainer.max" },
"attendees": { "$push": "$attendeeContainer.attendees" }
}
},
{
"$project": {
"name": 1,
"attendeeContainer.min": "$min",
"attendeeContainer.max": "$min",
"attendeeContainer.attendees": "$attendees"
}
}/**/
])
Desired/Actual Output
{
"_id" : ObjectId("582c789282a9183adc0b53f5"),
"name" : "Some Event",
"attendeeContainer" : {
"min" : 0,
"max" : 10,
"attendees" : [
{
"type" : 1,
"status" : 2,
"contact" : ObjectId("582c787682a9183adc0b53f3"),
"contactInfo" : {
"_id" : ObjectId("582c787682a9183adc0b53f3"),
"name" : "John Doe",
"age" : 35
}
},
{
"type" : 7,
"status" : 4,
"contact" : ObjectId("582c787682a9183adc0b53f4"),
"contactInfo" : {
"_id" : ObjectId("582c787682a9183adc0b53f4"),
"name" : "Peter Pan",
"age" : 60
}
}
]
}
}