Combining array of objects according to field in MongoDb - mongodb

I am trying to build an aggregation that will return the documents with the biggest number of objects in one of the fields, after combining the field.
Let's say that have this following documents that two of them contains the same id inside the movie field
{
"_id" : ObjectId("5b79bb0c15d2d0697885467c"),
"movie" : [
ObjectId("5b79b8387d467d5ab860544f")
],
"takenSeats" : [{"id" : 1},{"id" : 2},{"id" : 4},
],
"creteDate" : ISODate("2018-08-14T18:46:36.090Z"),
}
{
"_id" : ObjectId("5b79bb0c15d2d069788ef48d"),
"movie" : [
ObjectId("5b79b8387d467d5ab860544f")
],
"takenSeats" : [{"id" : 2},{"id" : 7},{"id" : 4},
],
"creteDate" : ISODate("2018-08-14T18:46:36.090Z"),
}
{
"_id" : ObjectId("5b79bb0c15d2d069788fg54hq"),
"movie" : [
ObjectId("5b79b8387d467d5ab8df54h43")
],
"takenSeats" : [{"id" : 6},{"id" : 2},{"id" : 5},
],
"creteDate" : ISODate("2018-08-14T18:46:36.090Z"),
}
As you can see, two of the documents contains the same id in the field movie.
What I trying to do is: to take those document that contains the same id in movie field and combine the takenSeats field
The wanted result should looks like
{
"_id" : ObjectId("5b79b8387d467d5ab860544f"),
"takenSeats" : [{"id" : 2},{"id" : 7},{"id" : 4},{"id" : 1},{"id" : 2},
{"id" : 4}
],
"creteDate" : ISODate("2018-08-14T18:46:36.090Z"),
}
{
"_id" : ObjectId("5b79b8387d467d5ab8df54h43"),
"takenSeats" : [{"id" : 6},{"id" : 2},{"id" : 5},
],
"creteDate" : ISODate("2018-08-14T18:46:36.090Z"),
}
In the last hours I tried to achieve it with different operators like $push and $addToSet. This is the query that I did that was the closest to the result that I want, but the issue that the documents that I was receiving in the result are with duplicated ids
db.orders.aggregate([
{$match:{ "created":{$gt: new Date(ISODate().getTime() - 1000*60*60*24*15)}}},
{ $lookup: { from: "shows", localField: "showId", foreignField: "_id", as: "acociatedShow" } },
{ "$project": { "acociatedShow": 1 } },
{ $unwind : "$acociatedShow" },
{ "$group": {"_id": { "movie": "$acociatedShow.movie"},
"takenSeats": { "$addToSet": "$acociatedShow.takenSeats"}}},
{ $unwind : "$takenSeats" },
{ $group : { _id : "$takenSeats", movieId : { $first: '$_id.movie' },len : { $sum : 1 } } },
{ $limit : 3 },
{ $lookup: { from: "movies", localField: "movieId", foreignField: "_id", as: "topMovie" } },
{ $unwind: "$topMovie" }, { $replaceRoot: { newRoot: "$topMovie" } }
])

Related

Not able to receive expected result in mongodb between collections

I'm trying to get a product list with logged user wishlist in the product list itself. I'm trying $lookup but not getting the expected result.
Product Document:
[
{
"_id" : ObjectId("6044351794bee8b6e0fce48f"),
"sku" : "P003474",
"name" : "Kitchen Wash"
},
{
"_id" : ObjectId("6085584c42ad3c58c5dbc6b7"),
"sku" : "TS0012",
"name" : "T-shirt"
},
{
"_id" : ObjectId("608d20a90e629fcc0d3a93e1"),
"sku" : "Apple1101",
"name" : "Green Apple"
}
]
Wishlist Document:
wishlist:
[
{
"_id" : ObjectId("608d8af12b7556c445f5cd87"),
"product" : ObjectId("6044351794bee8b6e0fce48f"),
"user" : ObjectId("601fb31a60e0a024143e6ea3"),
"isLiked" : false
},
{
"_id" : ObjectId("608d8bad2b7556c445f5cd88"),
"product" : ObjectId("6085584c42ad3c58c5dbc6b7"),
"user" : ObjectId("601fb31a60e0a024143e6ea3"),
"isLiked" : true
}
]
Expected Result:
Expected output:
[
{
"_id" : ObjectId("6044351794bee8b6e0fce48f"),
"sku" : "P003474",
"name" : "Kitchen Wash",
"isLiked": false
},
{
"_id" : ObjectId("6085584c42ad3c58c5dbc6b7"),
"sku" : "TS0012",
"name" : "T-shirt",
"isLiked": true
},
{
"_id" : ObjectId("608d20a90e629fcc0d3a93e1"),
"sku" : "Apple1101",
"name" : "Green Apple"
}
]
The query I'm trying:
db.getCollection('products').aggregate([
{
$lookup: {
from: "wishlists",
localField: "_id",
foreignField: "product",
as: "product"
}
},
{
$unwind:"$product"
}
])
Not able to get the above-declared result
$unwind causing the issues, first problem is it will remove document when product lookup result empty/[] if you don't specify preserveNullAndEmptyArrays : true, see working behaviour in playground
use $lookup with pipeline and match product id and user id condition
i have changed as name to isLiked in $lookup
$addFields to add isLiked parameter it its present, $arrayElemAt to get isLiked element from first element
db.getCollection('products').aggregate([
{
$lookup: {
from: "wishlists",
let: { product: "$_id" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $eq: ["$$product", "$product"] } },
{ user: ObjectId("601fb31a60e0a024143e6ea3") }
]
}
}
],
as: "isLiked"
}
},
{
$addFields: {
isLiked: { $arrayElemAt: ["$isLiked.isLiked", 0] }
}
}
])
Playground

Unable to aggregate two collections using lookup in MongoDB Atlas

I have an orders collection that looks like this:
{
"_id" : "wJNEiSYwBd5ozGtLX",
"orderId" : 52713,
"createdAt" : ISODate("2020-01-31T04:34:13.790Z"),
"status" : "closed",
"orders" : [
{
"_id" : "ziPzwLuZrz9MNkaRT",
"productId" : 10290,
"quantity" : 2
}
]
}
I have an products collection that looks like this
{
"_id" : "238cwwLkZa6gKNN86",
"productId" : 10290,
"title" : "Product Title",
"price" : 9.9
}
I am trying to merge the price information into the orders information.
Something like:
{
"_id" : "wJNEiSYwBd5ozGtLX",
"orderId" : 52713,
"createdAt" : ISODate("2020-01-31T04:34:13.790Z"),
"status" : "closed",
"orders" : [
{
"_id" : "ziPzwLuZrz9MNkaRT",
"productId" : 10290,
"quantity" : 2,
"price": 9.9
}
]
}
If I try a $lookup command on MongoDB Atlas Dashboard like this:
{
from: 'products',
localField: 'orders.productId',
foreignField: 'productId',
as: 'priceInfo'
}
The aggregated output is (not what I wanted):
{
"_id" : "wJNEiSYwBd5ozGtLX",
"orderId" : 52713,
"createdAt" : ISODate("2020-01-31T04:34:13.790Z"),
"status" : "closed",
"orders" : [
{
"_id" : "ziPzwLuZrz9MNkaRT",
"productId" : 10290,
}
],
"priceInfo": [
{
"_id" : "238cwwLkZa6gKNN86",
"productId" : 10290,
"title" : "Product Title",
"price" : 9.9
}
]
}
I do not need a separate priceInfo array. It will be best if I have the product details information merged into the "orders" array. What should be the aggregation lookup syntax to achieve the desired output?
Demo - https://mongoplayground.net/p/bLqcN7tauWU
Read - $lookup $unwind $first $set $push $group
db.orders.aggregate([
{ $unwind: "$orders" }, // break array of orders into individual documents
{
$lookup: { // join
"from": "products",
"localField": "orders.productId",
"foreignField": "productId",
"as": "products"
}
},
{
$set: {
"orders.price": { "$arrayElemAt": [ "$products.price", 0 ] } // set the price
}
},
{
$group: { // group records back
_id: "$_id",
createdAt: { $first: "$createdAt" },
status: { $first: "$status" },
orderId: { $first: "$orderId" },
orders: { $push: "$orders" }
}
}
])

Aggregation unwind multiple arrays $lookup as multiple fields for projection

This is my current query:
Follow.aggregate([
{
$match: {
"user": Types.ObjectId(user_id)
}
},
{
$unwind: "$following"
},
{
$lookup: {
"from": "users",
"localField": "following",
"foreignField": "createdById",
"as": "followingUsers"
}
}, {
$project: {
"user": 1,
"followingUsers": 1
}
}
])
This query is run on a document like this:
{
"_id" : ObjectId("5a271a93a19d690b25a3e181"),
"user" : ObjectId("5a271a4f50261a3c1695a391"),
"following" : [
ObjectId("5a257c87086eb00712fd02ec"),
ObjectId("5a257a79086eb00712fd02eb")
],
"followers" : [
ObjectId("5a257a79086eb00712fd02eb")
]
}
And gives a result like this:
{
"_id" : ObjectId("5a271a93a19d690b25a3e181"),
"user" : ObjectId("5a271a4f50261a3c1695a391"),
"followingUsers" : [
{
"_id" : ObjectId("5a257a79086eb00712fd02eb"),
"email" : "email#gmail.com",
"username" : "username",
"email_verified" : true,
"created" : ISODate("2010-12-04T16:40:25.670Z"),
"__v" : 0,
"last_login" : ISODate("2010-12-06T21:14:25.538Z"),
"is_active" : false
}
]
}
The above unwinds only one of the arrays and follows up the lookup.
I want to, in one scoop (assuming that is possible)
unwind both the $following and $followers array fields
$lookup both of the array fields independently
then have both come out as, say, followingUsers and followersUsers
So end up with something like this:
{
"_id" : ObjectId("5a271a93a19d690b25a3e181"),
"user" : ObjectId("5a271a4f50261a3c1695a391"),
"followingUsers" : [
{
"_id" : ObjectId("5a257a79086eb00712fd02eb"),
....
}
],
"followersUsers": [
{
"_id" : ObjectId("5a257a79086eb00712fd02eb"),
....
}
]
}
Am I even sane to imagine something like this in MongoDB?

count of $lookup result mongodb

Using mongodb with NodeJS driver
I have 2 collections. One for department and other for students.
Sample data for Deparmtent.
{
"_id" : ObjectId("5a24d20590d3d12155f3094e"), "name" : "CSE", "hod" :
"abc",
"students" : [
ObjectId("5a2129172c3e542acb78c1f5"),
ObjectId("5a2129172c3e542acb78c1f7"),
ObjectId("5a2129172c3e542acb78c1f9"),
ObjectId("5a2129172c3e542acb78c1fb")
]
}
{
"_id" : ObjectId("5a24d20590d3d12155f3094f"),
"name" : "IT",
"hod" : "xyz", ,
"students" : [
ObjectId("5a2129172c3e542acb78c1f6"),
ObjectId("5a2129172c3e542acb78c1f8"),
ObjectId("5a2129172c3e542acb78c1fa"),
ObjectId("5a2129172c3e542acb78c1fc")
]
}
and sample data for Students
{
"_id" : ObjectId("5a2129172c3e542acb78c1f4"),
"rollNumber" : 11,
"name" : "Thor",
"branch" : ObjectId("5a24d20590d3d12155f3094e"),
}
Here is the query:
aggregate([
{$lookup:
{ from: "students",
localField: "_id",
foreignField: "branch",
as: "studentData"
}
}
])
Output of the query
[
{ _id: 5a24d20590d3d12155f3094e,
name: 'CSE',
hod: 'abc',
studentData: [ [Object], [Object], [Object], [Object], [Object] ]
},
{ _id: 5a24d20590d3d12155f3094f,
name: 'IT',
hod: 'xyz',
studentData: [ [Object], [Object], [Object], [Object] ] }
]
I have two questions
How to print complete student data.
How to get the count of studentData for each Department
To get complete student data you can do something like
db.student.aggregate(
[
{
$lookup:
{
from:"department",
localField:"branch",
foreignField:"_id",
as:"branch"
}
}
]
)
This will give you something like this:
{
"_id" : 1.0,
"rollNumber" : 110.0,
"name" : "Thor",
"branch" : [
{
"_id" : 1.0,
"name" : "CSE",
"hod" : "abc"
}
]
}
To get the count of studentData for each Department
db.getCollection('student').aggregate(
[
{
$lookup:
{
from:"department",
localField:"branch",
foreignField:"_id",
as:"branch"
}
},
{
$group:
{
_id:"$branch",
"numOfStudent":{$sum:1},
"listOfStudents":{$push:"$name"}
}
}
]
)
This will give you something like this:
{
"_id" : [
{
"_id" : 2.0,
"name" : "IT",
"hod" : "xyz"
}
],
"numOfStudent" : 1.0,
"listOfStudents" : [
"Ironman2"
]
}
{
"_id" : [
{
"_id" : 1.0,
"name" : "CSE",
"hod" : "abc"
}
],
"numOfStudent" : 3.0,
"listOfStudents" : [
"Thor",
"Ironman",
"Ironman3"
]
}
You can change $push:$name to $push:$_id If you want to store the Ids of students and not their names.
EDIT
Get similar result using "Departments" collection:
db.department.aggregate([
{
$lookup:
{
from:"student",
localField:"students",
foreignField:"_id",
as:"studentsDetails"
}
},
{
$project:{
_id:0,
name:"$name",
hod:"$hod",
numOfStudents:{$size:"$studentsDetails"},
students:"$studentsDetails"
}
}
])
This will give you something like this:
{
"name" : "CSE",
"hod" : "abc",
"numOfStudents" : 2,
"students" : [
{
"_id" : 1.0,
"rollNumber" : 110.0,
"name" : "Thor",
"branch" : 1.0
},
{
"_id" : 3.0,
"rollNumber" : 111.0,
"name" : "Ironman2",
"branch" : 2.0
}
]
}
Your $lookup is completely fine. To add a count of students to your output data, you can use $addFields.
Add this to your aggregation:
{ $addFields: {studentCount: {$size: "$studentData"}}}
}
So, your query should be something like this:
aggregate([
{$lookup:
{ from: "students",
localField: "_id",
foreignField: "branch", as: "studentData"
}
}, { $addFields: {studentCount: {$size: "$studentData"}}
}
])

Triple relation lookup in MongoDB

I have tried to solve this one but its WAY over my Mongo skill level.
I hope there are some hardcore Mongo wizards who have an idea :-)
I would like to make a result where
db.getCollection('invoice').find({
dueDate: {
$gte:148000000,
$lt: 149000000
}
})
This is the "invoice" table....
invoice
{
"_id" : "KLKIU",
"invoiceNumber" : 1,
"bookingId" : "0J0DR",
"dueDate" : "148100000",
"account" : "aaaaaaaaaa",
"invoiceLines" : [
{
"lineText" : "Booking fee",
"amount" : 1000
},
{
"lineText" : "Discount",
"amount" : -200
},
{
"lineText" : "Whatever extra",
"amount" : 400
}
]
}
this is the result
{
"_id" : "KLKIU",
"invoiceNumber" : 1,
"bookingId" : "0J0DR",
"dueDate" : "148100000",
"account" : "aaaaaaaaaa",
"invoiceLines" : [
{
"lineText" : "Booking fee",
"amount" : 1000
},
{
"lineText" : "Discount",
"amount" : -200
},
{
"lineText" : "Whatever extra",
"amount" : 400
}
],
"propertyName" : "Atlantis Condo",
}
please notice the "propertyName" at the bottom
it needs to lookup and add
"propertyName" : "Atlantis Condo",
which will be done like this
db.getCollection('booking').find({
booking._id: invoice.bookingId
})
and then
db.getCollection('property').find({
property._id: booking:propertyId
})
These are the two tables:
Booking
{
"_id" : "0J0DR",
"propertyId" : "58669471869659d70b424ea7",
}
Property
{
"_id" : "58669471869659d70b424ea7",
"propertyName" : "Atlantis Condo",
}
Hope someone can figure this out - right now im doing some horrible sequential loops, and with big amounts of data thats really slow.
You can try below aggregation.
$lookup's to join to Booking and Property collection.
$unwind to flatten the booking array output from $lookup for joining on local field to Property collection.
$addFields to project the propertyName field.
$project to exclude the fields from referenced collection.
db.getCollection('invoice').aggregate([{
$match: {
"dueDate": {
$gte: 148000000,
$lt: 149000000
}
}
}, {
$lookup: {
from: "Booking",
localField: "bookingId",
foreignField: "_id",
as: "booking"
}
}, {
$unwind: "$booking"
}, {
$lookup: {
from: "Property",
localField: "booking.propertyId",
foreignField: "_id",
as: "property"
}
}, {
$unwind: "$property"
}, {
$addFields: {
"propertyName": "$property.propertyName"
}
}, {
$project: {
"booking": 0
}
}, {
$project: {
"property": 0
}
}])