Project Specific Fields from a Foreign Collection $lookup result - javascript

I'm writing aggregation to get foreign collection data with local collection.
db.getCollection('orders').aggregate([
{
$match: {
status: "UNASSIGNED",
serviceLocationId: "83177"
}
}, {
$lookup: {
from: "servicelocations",
localField: "serviceLocationId",
foreignField: "serviceLocationId",
as: "locations"
}
}, {
$unwind: "$locations"
}])
I'm getting:
{
"_id" : ObjectId("59d32b5c360198e441b67545"),
"accountId" : 1.0,
"orderId" : "AQ137O1701240",
"serviceLocationId" : "83177",
"orderDate" : "2017-09-18T18:29:00.000Z",
"description" : "AQ137O1701240",
"serviceType" : "Delivery",
"orderSource" : "Import",
"takenBy" : "KARIM",
"plannedDeliveryDate" : ISODate("2017-10-09T00:00:00.000Z"),
"plannedDeliveryTime" : "",
"actualDeliveryDate" : "",
"actualDeliveryTime" : "",
"deliveredBy" : "",
"size1" : 25.0,
"size2" : 464.0,
"size3" : 46.0,
"jobPriority" : 1.0,
"cancelReason" : "",
"cancelDate" : "",
"cancelBy" : "",
"reasonCode" : "",
"reasonText" : "",
"status" : "UNASSIGNED",
"lineItems" : [
{
"ItemId" : "MMGW001",
"size1" : 25.0,
"size2" : 464.38,
"size3" : 46.875
}
],
"locations" : {
"_id" : ObjectId("59ce18e172dbf6926093e189"),
"accountId" : 1.0,
"serviceLocationId" : "83177",
"regionId" : "1",
"zoneId" : "DXBZONE1",
"description" : "EXPRESS BLUE MART SUPERMARKET",
"locationPriority" : 1.0,
"accountTypeId" : 1.0,
"locationType" : "SERVICELOCATION",
"location" : {
"makani" : "",
"lng" : 55.179042,
"lat" : 25.098741
},
"deliveryDays" : "MTWRFSU",
"serviceTimeTypeId" : "1",
"timeWindow" : {
"timeWindowTypeId" : "1"
},
"address1" : "",
"address2" : "",
"phone" : "",
"city" : "",
"county" : "",
"state" : "",
"country" : "",
"zipcode" : "",
"imageUrl" : "",
"contact" : {
"name" : "",
"email" : ""
},
"status" : "ACTIVE",
"createdBy" : "",
"updatedBy" : "",
"updateDate" : ""
}
}
but i need:
{
"_id" : ObjectId("59d32b5c360198e441b67545"),
"accountId" : 1.0,
"orderId" : "AQ137O1701240",
"serviceLocationId" : "83177",
"orderDate" : "2017-09-18T18:29:00.000Z",
"description" : "AQ137O1701240",
"serviceType" : "Delivery",
"orderSource" : "Import",
"takenBy" : "KARIM",
"plannedDeliveryDate" : ISODate("2017-10-09T00:00:00.000Z"),
"plannedDeliveryTime" : "",
"actualDeliveryDate" : "",
"actualDeliveryTime" : "",
"deliveredBy" : "",
"size1" : 25.0,
"size2" : 464.0,
"size3" : 46.0,
"jobPriority" : 1.0,
"cancelReason" : "",
"cancelDate" : "",
"cancelBy" : "",
"reasonCode" : "",
"reasonText" : "",
"status" : "UNASSIGNED",
"lineItems" : [
{
"ItemId" : "MMGW001",
"size1" : 25.0,
"size2" : 464.38,
"size3" : 46.875
}
],
"locations" : {
"lng" : 55.179042,
"lat" : 25.098741
}
}

MongoDB less than 3.4.4
Basically, use $project as a final stage and select ALL the specific fields you want. Unfortunately $addFields is out because it will actually "merge" the sub-keys with the existing ones. So the seemingly simple:
{ "$addFields": {
"locations": {
"lng": "$locations.location.lng",
"lat": "$locations.location.lat"
}
}}
Just gives you all the existing content under "locations" as well as the those newly defined keys. Unless of course you don't $unwind directly after the $lookup, which you can do if this would not cause the BSON limit to be exceeded. ( this is called $lookup + $unwind coalescence )
Then we can use $addFields with $map, because we can simply "re-map" the array:
{ "$addFields": {
"locations": {
"$map": {
"input": "$locations",
"as": "l",
"in": {
"lng": "$$l.location.lng",
"lat": "$$l.location.lat"
}
}
}
}},
{ "$unwind": "$locations" }
And then $unwind if you still need to after that re-mapping.
So with $project it is:
{ "$project": {
"accountId" : 1,
"orderId" : 1,
"serviceLocationId" : 1,
"orderDate" : 1,
"description" : 1,
"serviceType" : 1,
"orderSource" : 1,
"takenBy" : 1,
"plannedDeliveryDate" : 1,
"plannedDeliveryTime" : 1,
"actualDeliveryDate" : 1,
"actualDeliveryTime" : 1,
"deliveredBy" : 1,
"size1" : 1,
"size2" : 1,
"size3" : 1,
"jobPriority" : 1,
"cancelReason" : 1,
"cancelDate" : 1,
"cancelBy" : 1,
"reasonCode" : 1,
"reasonText" : 1,
"status" : 1,
"lineItems" : 1,
"locations" : {
"lng": "$locations.location.lng",
"lat": "$locations.location.lat"
}
}}
Simple but long winded.
MongoDB 3.4.4 Or greater
If you have MongoDB 3.4.4 or greater with $objectToArray and $arrayToObject, then you can be a bit more fancy about it:
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$concatArrays": [
{ "$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": [ "$$this.k", "locations" ] }
}},
{ "$objectToArray": {
"locations": {
"lng": "$locations.location.lng",
"lat": "$locations.location.lat"
}
}}
]
}
}
}}
Which basically takes all the fields presently in the whole document from $$ROOT, turns it into an array format. We then $filter the "location" field by the "key name" and $concatArrays it with the new "location" key and sub-keys again transformed into an array.
Finally of course $arrayToObject takes that and transforms back into an object which is supplied to newRoot of $replaceRoot as the final output.
So using either of those except $addFields after $unwind of course gives you the correct result:
/* 1 */
{
"_id" : ObjectId("59d32b5c360198e441b67545"),
"accountId" : 1.0,
"orderId" : "AQ137O1701240",
"serviceLocationId" : "83177",
"orderDate" : "2017-09-18T18:29:00.000Z",
"description" : "AQ137O1701240",
"serviceType" : "Delivery",
"orderSource" : "Import",
"takenBy" : "KARIM",
"plannedDeliveryDate" : ISODate("2017-10-09T00:00:00.000Z"),
"plannedDeliveryTime" : "",
"actualDeliveryDate" : "",
"actualDeliveryTime" : "",
"deliveredBy" : "",
"size1" : 25.0,
"size2" : 464.0,
"size3" : 46.0,
"jobPriority" : 1.0,
"cancelReason" : "",
"cancelDate" : "",
"cancelBy" : "",
"reasonCode" : "",
"reasonText" : "",
"status" : "UNASSIGNED",
"lineItems" : [
{
"ItemId" : "MMGW001",
"size1" : 25.0,
"size2" : 464.38,
"size3" : 46.875
}
],
"locations" : {
"lng" : 55.179042,
"lat" : 25.098741
}
}
MongoDB 3.6 and greater
As a bit of a preview, $lookup gets a more expressive overhaul with MongoDB 3.6. So you can actually specifically state the fields to return that way:
{ "$lookup": {
"from": "servicelocations",
"let": { "serviceLocationId": "$serviceLocationId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "serviceLocationId", "$$serviceLocationId" ] } }},
{ "$project": {
"_id": 0,
"lng": "$location.lng",
"lat": "$location.lat"
}}
],
"as": "locations"
}}
Little bit more handy when that is actually released. This actually uses $expr instead of the localField and foreignField to define the "join" condition in a $match stage of the sub-pipeline. Then you can simply $project the fields to return, which then go into the array targeted by $lookup.
Going forward, that's the general approach you would want to take as it limits what actually gets returned.

Related

Complex mongodb data how to update

I'm trying to update show_seats.showByDate.shows.showSeats.seat_details.values.seat_status
this my code .........................................................................................................................................................
db.seats.updateOne({'show_seats.showByDate.shows.showSeats.seat_details.values._id':"62a9e044ec028e60180fe28a"},{$set:{'show_seats.showByDate.shows.showSeats.seat_details.values.seat_status' : true}})
.........................................................................................................................................................
please say what did i wrong**
{
"_id" : ObjectId("62a43ac2d7213c7233cd1dee"),
"totalShowByDay" : "2",
"totalShowDays" : 4,
"movieId" : ObjectId("62953ba3cb6ae625ec9433e6"),
"screenId" : ObjectId("6293b9638dde2d92658d5513"),
"createdAt" : 1654930114438,
"showId" : ObjectId("62a43ac2d7213c7233cd14ed"),
"show_seats" : [
{
"showByDate" : {
"ShowDate" : "2022-06-11",
"shows" : [
{
"showTime" : "2022-06-11T10:00",
"showSeats" : [
[
{
"category" : "CLASSIC",
"seat_details" : [
{
"key" : "A",
"values" : [
{
"_id" : ObjectId("62a43ac2d7213c7233cd14ee"),
"seat_number" : "1",
"tag_name" : "A",
"seat_status" : false,
"user_id" : false,
"price" : "140",
"seats_category" : "CLASSIC",
"show_time" : "2022-06-11T10:00"
},
,
{
"_id" : ObjectId("62a43ac2d7213c7233cd14ef"),
"seat_number" : "2",
"tag_name" : "A",
"seat_status" : false,
"user_id" : false,
"price" : "140",
"seats_category" : "CLASSIC",
"show_time" : "2022-06-11T10:00"
},
{
"_id" : ObjectId("62a43ac2d7213c7233cd14f0"),
"seat_number" : "3",
"tag_name" : "A",
"seat_status" : false,
"user_id" : false,
"price" : "140",
"seats_category" : "CLASSIC",
"show_time" : "2022-06-11T10:00",,
{
"_id" : ObjectId("62a43ac2d7213c7233cd14ef"),
"seat_number" : "2",
"tag_name" : "A",
"seat_status" : false,
"user_id" : false,
"price" : "140",
"seats_category" : "CLASSIC",
"show_time" : "2022-06-11T10:00"
},
{
"_id" : ObjectId("62a43ac2d7213c7233cd14f0"),
"seat_number" : "3",
"tag_name" : "A",
"seat_status" : false,
"user_id" : false,
"price" : "140",
"seats_category" : "CLASSIC",
"show_time" : "2022-06-11T10:00"
}
this is what i ended up doing, I need best solution
await db.getDb().collection(coll.seat).aggregate([
{
'$unwind': {
'path': '$show_seats'
}
}, {
'$unwind': {
'path': '$show_seats.showByDate.shows'
}
}, {
'$unwind': {
'path': '$show_seats.showByDate.shows.showSeats'
}
}, {
'$unwind': {
'path': '$show_seats.showByDate.shows.showSeats'
}
}, {
'$unwind': {
'path': '$show_seats.showByDate.shows.showSeats.seat_details'
}
}, {
'$unwind': {
'path': '$show_seats.showByDate.shows.showSeats.seat_details.values'
}
}, {
'$match': {
'show_seats.showByDate.shows.showSeats.seat_details.values._id': ObjectId("62a43ac2d7213c7233cd14ee")
}
}, {
$addFields: { "show_seats.showByDate.shows.showSeats.seat_details.values.seat_status": true }
}
])

How to populate an array inside a document which stores uniqueID as strings and that uniqueID is from another collection rather than objectID

I have a user document as follows which holds an array of systemIDs', which is unique for another collection which holds systemID as unikey key. How to populate user with all system ID details?
User document:
{
"_id" : ObjectId("6gfg85993266db5fdgs578"),
"email" : "xyz#gmail.com",
"role" : "user",
"systemIDs" : [
"12345678",
"87654321"
],
"createdAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"updatedAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("6gfg85993266db5fdgs578"),
"email" : "abc#gmail.com",
"role" : "user",
"systemIDs" : [
"1111111",
"2135684"
],
"createdAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"updatedAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"__v" : NumberInt(0)
}
System IDs document:
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "12345678",
"err" : [
1, 5, 10
],
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "87654321",
"err" : [
3, 7
],
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("62087dsfsdfs97e1"),
"systemID" : "11111111",
"err" : [
],
"__v" : NumberInt(0)
},
I want to find details of all the systemIDs a user holds which results something like this if I query my users collection with email : xyz#gmail.com, I should get the below result or populated result like shown:
{
"_id" : ObjectId("6gfg85993266db5fdgs578"),
"email" : "xyz#gmail.com",
"role" : "user",
"systemIDs" : [
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "12345678",
"err" : [
1, 5, 10
],
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "87654321",
"err" : [
3, 7
],
"__v" : NumberInt(0)
},
]
"createdAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"updatedAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"__v" : NumberInt(0)
},
I can create a foreach loop and call database each time but I suppose that wouldn't be a good practice.
I am new with this so please bear with me and explain it to me in details.
you can use aggregation with $match and $lookup to perform this task
db.users.aggregate([
{
"$match": {
"email": "xyz#gmail.com"
}
},
{
"$lookup": {
"from": "systems",
"localField": "systemIDs",
"foreignField": "systemID",
"as": "systemIDs"
}
}
])
demo

How to filter with data taken from $lookup

I am currently trying to aggregate list of documents by filtering them with data taken with $lookup
Product.aggregate([
{
$lookup: {
from: "categories",
localField: "category",
foreignField: "_id",
as: "category",
},
},
{ $unwind: "$category" }])
I was hoping adding { $match: { "category.left": {$gte: 3}} }, would be able to get all of the products with categories that's left property is greater than specified, but so far I get nothing. what would be the solution for this?
category documents
{ "_id" : ObjectId("570557d4094a4514fc1291d6"), "left" : 1, "right" : "2" }
{ "_id" : ObjectId("570557d4094a4514fc1291d7"), "left" : 3, "right" : "8"}
{ "_id" : ObjectId("570557d4094a4514fc1291d8"), "left" : 4, "right" : "5"}
{ "_id" : ObjectId("570557d4094a4514fc1291d9"), "left" : 6, "right" : "7" }
product documents
{ "_id" : ObjectId("570557d4094a4514fc129120"), "category": ObjectId("570557d4094a4514fc1291d6") }
{ "_id" : ObjectId("570557d4094a4514fc129121"), "category": ObjectId("570557d4094a4514fc1291d7")}
{ "_id" : ObjectId("570557d4094a4514fc129122"), "category": ObjectId("570557d4094a4514fc1291d8")}
{ "_id" : ObjectId("570557d4094a4514fc129123"), "category": ObjectId("570557d4094a4514fc1291d9") }
I was expecting to get
{ "_id" : ObjectId("570557d4094a4514fc129121"), "category": ObjectId("570557d4094a4514fc1291d7")}
{ "_id" : ObjectId("570557d4094a4514fc129122"), "category": ObjectId("570557d4094a4514fc1291d8")}
{ "_id" : ObjectId("570557d4094a4514fc129123"), "category": ObjectId("570557d4094a4514fc1291d9") }
for my response

unable to group date and field in mongodb

I am trying to group date and name field then finding the count of each day.I am not able to differentiate date and time so My approach is doing the grouping based on date as well time in ISO format.
My approach:-
db.getCollection('blog').aggregate([
{ "$group": {
"_id": {"name":"$name","date":"$date"},
"Count": {
"$sum": {
"$sum": "$Count"
}
}
}},
{ "$project": {
"name": "$_id",
"Count": "$Count",
"_id": 0
}}
]).toArray()
input:-
{ "_id" : ObjectId("1"), "Count" : 4 , "name" : Ram, "date" : ISODate("2017-02-01T00:00:00Z") }
{ "_id" : ObjectId("2"), "Count" : 4, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("3"), "Count" :2 , "name" : Ram, "date" : ISODate("2017-02-01T00:00:00Z")}
{ "_id" : ObjectId("4"), "Count" : 2, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("5"), "Count" : 6, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("6"), "Count" : 6, "name" : Shyam, "date" : ISODate("2017-02-09T00:00:00Z")}
{ "_id" : ObjectId("7"), "count" : 1, "name" : Shyam, "date" : ISODate("2017-02-03T00:00:00Z") }
{ "_id" : ObjectId("8"), "loginID" : 2, "name" : Arjun, "date" : ISODate("2017-02-08T00:00:00Z") }
Expected output:-
{
name:Ram,
Count:6,
date:2017-02-01
}
{
name: Arjun,
Count:12,
date:2017-02-08
}
{
name: Arjun,
Count:2,
date:2017-01-08
}
....Something like that
You are using $sum twice in your $group stage, you only need it once.
db.getCollection('blog').aggregate([
{
$group: {
_id: {
name: "$name",
date: "$date"
},
Count: {
$sum: "$Count"
}
}
},
{
$project: {
name: "$_id.name",
date: "$_id.date",
Count: 1,
_id: 0
}
}
])

Filtering realtime database data

I have the following firebase database. I would like to filter and list the orders which's deliveryDuration value is "45"
"orders" : [ null, {
"comment" : "",
"date" : "2018-07-09 10:07:18",
"deliveryDuration" : "45",
"delivery_address" : "",
"item" : [ {
"available" : true,
"category" : "Dessert",
"details" : "(Hausgemacht)",
"name" : "Warmer Topfenstrudel",
"price" : 3.9,
"quantity" : 1
} ],
"orderVerified" : false,
"telefon" : "",
"userID" : "xyc#gmail.com",
"userName" : ""
}, {
"comment" : "",
"date" : "2018-07-10 10:07:33",
"deliveryDuration" : "30",
"delivery_address" : "",
"item" : [ {
"available" : true,
"category" : "Dessert",
"details" : "(Hausgemacht)",
"name" : "Warmer Topfenstrudel",
"price" : 3.9,
"quantity" : 1
} ],
"orderVerified" : false,
"telefon" : "",
"userID" : "xyc#gmail.com",
"userName" : ""
}, {
"comment" : "",
"date" : "2018-07-10 10:13:13",
"deliveryDuration" : "10",
"delivery_address" : "",
"item" : [ {
"available" : true,
"category" : "Spezialitäten",
"details" : "Schweinschnitzel mit Parmesan gebacken auf Spaghetti Napoli",
"name" : "Piccata Milanese",
"price" : 12.9,
"quantity" : 1
} ],
"orderVerified" : false,
"telefon" : "",
"userID" : "xyc#gmail.com",
"userName" : ""
}
}
I tried writing the following function:
getNewItems: function (order) {
if (db.ref('orders').child(order).child('deliveryDuration').equalTo(45)){
return db.ref('orders').child(order).child('deliveryDuration')
}}
How could I get the filtered objects? I was thinking that that i write an if-else statement and the function returns the values what i was looking for.
I think you're looking for this:
getNewItems: function (order) {
return db.ref('orders').orderByChild('deliveryDuration').equalTo(45));
}}
This Firebase query takes each child node of the location you query, orders it by the deliveryDuration property, and then filters to only get the ones who are equal to 45.

Categories

Resources