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
}
}])
Related
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
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" }
}
}
])
I have two collections:
user:
{
"_id" : "9efb42e5-514d-44bd-a4b8-6f74e6313ec2",
"name" : "Haralt",
"age" : 21,
"bloodlineId" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"villageId" : "foovillage"
}
bloodlines:
{
"_id" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"name" : "Tevla",
"legacy" : 0
}
Now I'd like to do an aggregate to replace user.bloodlineId with the whole bloodline document.
This is what I tried to far:
db.getCollection('character').aggregate([
{
"$match": { _id: "9efb42e5-514d-44bd-a4b8-6f74e6313ec2" }
},
{
"$lookup": {
from: "bloodline",
localField: "bloodlineId",
foreignField: "_id",
as: "bloodline"
}
}])
The result is almost where I want it:
{
"_id" : "9efb42e5-514d-44bd-a4b8-6f74e6313ec2",
"name" : "Haralt",
"age" : 21,
"bloodlineId" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"villageId" : "foovillage",
"bloodline" : [
{
"_id" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"name" : "Tevla",
"legacy" : 0
}
]
}
Only two issues here. The first is that bloodlineId is still there and bloodline was just added to the result. I'd like to have bloodline replace the bloodlineId attribute.
The second problem is that bloodline is an array. I'd love to have it a single object.
I think this pipeline might do the trick:
[
{
"$match": {
_id: "9efb42e5-514d-44bd-a4b8-6f74e6313ec2"
}
},
{
"$lookup": {
from: "bloodlines",
localField: "bloodlineId",
foreignField: "_id",
as: "bloodline"
}
},
{
$project: {
"age": 1,
"bloodlineId": {
$arrayElemAt: [
"$bloodline",
0
]
},
"name": 1,
"villageId": 1
}
}
]
Mongo Playground
If there's anything I'm missing, please let me know!
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" } }
])
I want to get all invoices based on duedates - AND based on the status from the booking table.
I want invoices where booking.status < 5
I do NOT want invoices where booking.status >= 5
This is my aggregate:
db.getCollection('invoice').aggregate([
{
$match: {
dueDate: {$gte: 1483596800},
dueDate: {$lte: 1583596800}
}
},
{
$lookup: {
from: "booking",
localField: "bookingId",
foreignField: "_id",
as: "booking"
}
}
])
And here are the tables....
table invoice
{
"_id" : "IKUU",
"bookingId" : "AAAAA",
"dueDate" : 1489470468,
"invoiceLines" : [
{
"lineText" : "Rent Price",
"amountPcs" : "7 x 2071",
"amountTotal" : 14497
},
{
"lineText" : "Discount",
"amountPcs" : "",
"amountTotal" : -347
}
]
}
{
"_id" : "1NYRO",
"bookingId" : "BBBBB",
"dueDate" : 1489471351,
"invoiceLines" : [
{
"lineText" : "Reservation / Booking fee",
"amountPcs" : "1 x 2000",
"amountTotal" : 2000
}
]
}
table booking
{
"_id" : "AAAAA",
"checkin" : 1449756800,
"price" : 5000,
"status" : 1
}
{
"_id" : "BBBBB",
"checkin" : 1449756800,
"price" : 6000,
"status" : 5
}
I tried putting some $match{booking.status: {$lt: 5}} but I cant get it to work.
The result should be the invoice with "bookingId" : "AAAAA".
you need to check for another match for booking status after $lookup like below
db.getCollection('invoice').aggregate([
{
$match: {
dueDate: {$gte: 1483596800},
dueDate: {$lte: 1583596800}
}
},
{
$lookup: {
from: "booking",
localField: "bookingId",
foreignField: "_id",
as: "booking"
}
},
{
$match: {
"booking.status": {$lt: 5},
}
}
])