I have two collections called orders and items as shown below. Now, I'm trying to join these collections based on the _id field. Can we use "$lookup" operator for this scenario? Or is there any other method to resolve this problem.
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
])
db.items.insert([
{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },
{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },
{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }
])
Can anyone please help me out regarding this issue ...
Try following code:
db.orders.aggregate([
{
$lookup:
{
from: "items",
localField: "_id",
foreignField: "_id",
as: "item"
}
},
{ $unwind: "$item" },
{
$project: {
"_id": 1,
"price": 1,
"quantity": 1,
"description": "$item.description",
"instock": "$item.instock"
}
}
])
Since you know that there will be 1 to 1 relationship you can unwind $lookup results to have just one embedded item for each order. Then you can project your results to get flat structure of JSON. This will give you results in following shape:
{
"_id" : 1,
"price" : 12,
"quantity" : 2,
"description" : "almond clusters",
"instock" : 120
}
Try This.
db.orders.aggregate([
{ $lookup:
{
from: 'items',
localField: '_id',
foreignField: '_id',
as: 'get_data'
}
}
]).exec(function(err, res) {
if (err) throw err;
console.log(res);
});
Related
This example is following https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#use-lookup-with-mergeobjects
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
])
db.items.insert([
{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },
{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },
{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }
])
Aggregation:
db.orders.aggregate([
{
$lookup: {
from: "items",
localField: "item", // field in the orders collection
foreignField: "item", // field in the items collection
as: "fromItems"
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$fromItems", 0 ] }, "$$ROOT" ] } }
},
{ $project: { fromItems: 0 } }
])
Result:
{ "_id" : 1, "item" : "almonds", "description" : "almond clusters", "instock" : 120, "price" : 12, "quantity" : 2 }
{ "_id" : 2, "item" : "pecans", "description" : "candied pecans", "instock" : 60, "price" : 20, "quantity" : 1 }
Question: How to modify the aggregation to project the specific fields? e.g. project "_id", "item" and "description" only:
{ "_id" : 1, "item" : "almonds", "description" : "almond clusters" }
{ "_id" : 2, "item" : "pecans", "description" : "candied pecans" }
You're getting an empty array, because the $lookup catching anything.
match the types
$addFields to convert
PLAYGROUND
This should be the first stage:
{
$addFields: {
itemId: {
$convert: {
input: "$itemId",
to: "int"
}
}
}
},
If you prefer, there is no need to add a stage
You could also remove addFields and use $lookup+let.
Modify the lookup this way:
{
$lookup: {
from: "items",
let: {
itemId: {
$convert: {
input: "$itemId",
to: "int"
}
}
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$_id",
"$$itemId"
]
}
}
}
],
/** field in the items collection*/
as: "fromItems"
}
}
PLAYGROUND2
The aim of this query is when you are "Given an employee return a total sales number".
My first query was this
db.Employee.aggregate([{$lookup: {from: "Invoice", localField: "_id", foreignField: "_id", as: "Invoices"}}, {$match: {_id: 2}}]).pretty()
which returned the below snippet though it only returns one customer even though the employee has several customers. I'm not totally sure why it returns just one.
{
"_id" : 2,
"LastName" : "Edwards",
"FirstName" : "Nancy",
"Title" : "Sales Manager",
"ReportsTo" : 1,
"BirthDate" : ISODate("1958-12-08T00:00:00Z"),
"HireDate" : ISODate("2002-05-01T00:00:00Z"),
"Address" : "825 8 Ave SW",
"City" : "Calgary",
"State" : "AB",
"Country" : "Canada",
"PostalCode" : "T2P 2T3",
"Phone" : "+1 (403) 262-3443",
"Fax" : "+1 (403) 262-3322",
"Email" : "nancy#chinookcorp.com",
"Invoices" : [
{
"_id" : 2,
"CustomerId" : 4,
"InvoiceDate" : ISODate("2009-01-02T00:00:00Z"),
"BillingAddress" : "Ullevålsveien 14",
"BillingCity" : "Oslo",
"BillingState" : null,
"BillingCountry" : "Norway",
"BillingPostalCode" : "0171",
"Total" : 3.96,
"InvoiceLines" : [
{
"_id" : 3,
"TrackId" : 6,
"UnitPrice" : 0.99,
"Quantity" : 1
},
{
"_id" : 4,
"TrackId" : 8,
"UnitPrice" : 0.99,
"Quantity" : 1
},
{
"_id" : 5,
"TrackId" : 10,
"UnitPrice" : 0.99,
"Quantity" : 1
},
{
"_id" : 6,
"TrackId" : 12,
"UnitPrice" : 0.99,
"Quantity" : 1
}
]
}
]
}
In an attempt to get around this and achieve my aim of returning the total sales number I created this new query
db.Employee.aggregate([{$unwind: "$_id"}, {$lookup: {from: "Invoice", localField: "_id", foreignField: "_id", as: "Invoices"}}, {$match: {_id: 2}}, {$group: {_id: "$_id", Total: {$sum: "$Total"}}}]).pretty()
though it just returns { "_id" : 2, "Total" : 0 }
Having looked at other issues I'm thinking it might be because the documents are nested though having tried the potential solution it has yielded no output. No errors which is good but nothing happens. Here is the query I tried:
db.Employee.aggregate([{$unwind: "$_id"}, {$unwind: "$_id.Invoices"}, {$unwind: "$_id.Invoices.InvoiceLines"}, {$lookup: {from: "Invoice", localField: "_id", foreignField: "_id", as: "Invoices"}}, {$match: {_id: 2}}, {$group: {_id: "$_id", Total: {$sum: "$Total"}}}]).pretty()
I don't understand why this query is not returning the total. Everything I've tried has failed. Any help is appreciated.
Edit:
My database is structured as so: Employee>Customer>Invoice. Customer references Employee via SupportRepId which is the same as an employee id as each customer is assigned an employee and Invoice contains the customer id as each invoice has a customer. So I want to get all invoices and sum there total based on an employee id.
Employee Example:
{
"_id":3,
"LastName":"Peacock",
"FirstName":"Jane",
"Title":"Sales Support Agent",
"ReportsTo":2,
"BirthDate": ISODate("1973-08-29T00:00:00 Z"),
"HireDate": ISODate("2002-04-01T00:00:00 Z"),
"Address":"1111 6 Ave SW",
"City":"Calgary",
"State":"AB",
"Country":"Canada",
"PostalCode":"T2P 5M5",
"Phone":"+1 (403) 262-3443",
"Fax":"+1 (403) 262-6712",
"Email":"jane#chinookcorp.com"
}
Customer Example:
{
"_id":1,
"FirstName":"Luís",
"LastName":"Gonçalves",
"Company":"Embraer - Empresa Brasileira de Aeronáutica S.A.",
"Address":"Av. Brigadeiro Faria Lima, 2170",
"City":"São José dos Campos",
"State":"SP",
"Country":"Brazil",
"PostalCode":"12227-000",
"Phone":"+55 (12) 3923-5555",
"Fax":"+55 (12) 3923-5566",
"Email":"luisg#embraer.com.br",
"SupportRepId":3
}
Invoice Example:
{
"_id":2,
"CustomerId":4,
"InvoiceDate": ISODate("2009-01-02T00:00:00 Z"),
"BillingAddress":"Ullevålsveien 14",
"BillingCity":"Oslo",
"BillingState":null,
"BillingCountry":"Norway",
"BillingPostalCode":"0171",
"Total":3.96,
"InvoiceLines":[
{
"_id":3,
"TrackId":6,
"UnitPrice":0.99,
"Quantity":1
},
{
"_id":4,
"TrackId":8,
"UnitPrice":0.99,
"Quantity":1
},
{
"_id":5,
"TrackId":10,
"UnitPrice":0.99,
"Quantity":1
},
{
"_id":6,
"TrackId":12,
"UnitPrice":0.99,
"Quantity":1
}
]
}
See if this works
db.Employee.aggregate([
{
$match: {
"Employee._id": 2
}
},
{
$lookup: {
from: "Customer",
localField: "_id",
foreignField: "customer.SupportRepId",
as: "customer"
}
},
{
$lookup: {
from: "Invoice",
localField: "customer._id",
foreignField: "invoice.CustomerId",
as: "invoice"
}
}
])
I have two collections called "orders" and "items" as specified below:
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
])
db.items.insert([
{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },
{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },
{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }
])
Now, I'm trying to fetch the document if the "item" field gets matched in both the collections.
I have tried using $lookup operator for this scenario, but somehow the query is getting failed when tried to run on the mongo shell.
My $lookup query:
db.orders.aggregate([
{
$lookup:
{
from: "items",
localField: “item”,
foreignField: “item”,
as: "ite"
}
},
{ $unwind: "$ite" },
{
$project: {
“item”: 1
}
}
])
Can anyone please help me out regarding the above mentioned scenario ...
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
}
}])
I have two collections: one is items and the second one is user_item_history. I want to fetch items with their status. Status of each item is stored in user_item_history, and other details of the item are in the items collection. we have to filter data for particular user and category of item. so user_id and category is in user_item_history collection.
user_item_history:
{
"_id" : NumberLong(25424),
"_class" : "com.samepinch.domain.registration.UserItemHistory",
"user_id" : NumberLong(25416),
"item_id" : NumberLong(26220),
"catagoryPreference" : "BOTH",
"preference" : 0.6546536707079772,
"catagory" : "FOOD",
"status" : 1,
"createdDate" : ISODate("2015-09-02T07:50:36.760Z"),
"updatedDate" : ISODate("2015-09-02T07:55:24.105Z")
}
items:
{
"_id" : NumberLong(26220),
"_class" : "com.samepinch.domain.item.Item",
"itemName" : "Shoes",
"categoryName" : "SHOPPING",
"attributes" : [
"WESTERN",
"CASUAL",
"ELEGANT",
"LATEST"
],
"isAccessed" : false,
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"catagoryPreference" : "FEMALE",
"startDate" : ISODate("2015-11-26T18:30:00Z"),
"endDate" : ISODate("2015-11-27T18:30:00Z"),
"location" : {
"coordinates" : [
77.24149558372778,
28.56973445677584
],
"type" : "Point",
"radius" : 2000
},
"createdDate" : ISODate("2015-11-16T10:49:11.858Z"),
"updatedDate" : ISODate("2015-11-16T10:49:11.858Z")
}
As the final result, I would like to have documents of this format:
{
item_id:26220,
status:1,
imageUrl: "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg"
}
Update to MongoDB 3.2 and you'll be able to use the $lookup aggregation stage, which works similarly to SQL joins.
One-to-many relationship
If there are many corresponding user_item_history documents for each items document, you can get a list of item statuses as an array.
Query
db.items.aggregate([
{
$lookup:
{
from: "user_item_history",
localField: "_id",
foreignField: "item_id",
as: "item_history"
}
},
{
$project:
{
item_id: 1,
status: "$item_history.status",
imageUrl: 1
}
}])
Example Output
{
"_id" : NumberLong(26220),
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"status" : [ 1 ]
},
{
"_id" : NumberLong(26233),
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"status" : [ 1, 2 ]
}
One-to-one relationship
If there's only one corresponding history document for every item, you can use the following approach to get the exact format you requested:
Query
db.items.aggregate([
{
$lookup:
{
from: "user_item_history",
localField: "_id",
foreignField: "item_id",
as: "item_history"
}
},
{
$unwind: "$item_history"
},
{
$project:
{
item_id: 1,
status: "$item_history.status",
imageUrl: 1
}
}])
Example Output
{
"_id" : NumberLong(26220),
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"status" : 1
}
Please bear in mind that with every additional aggregation pipeline stage you add, the performance deteriorates. So you may prefer the one-to-many query even if you have a one-to-one relationship.
Applying filtering
In your edit, you added this:
we have to filter data for particular user and category of item. so user_id and category is in user_item_history collection
To filter your results, you should add a $match step to your query:
db.items.aggregate([
{
$lookup:
{
from: "user_item_history",
localField: "_id",
foreignField: "item_id",
as: "item_history"
}
},
{
$unwind: "$item_history"
},
{
$match:
{
"item_history.user_id": NumberLong(25416),
"item_history.catagory": "FOOD"
}
},
{
$project:
{
item_id: 1,
status: "$item_history.status",
imageUrl: 1
}
}])
Please note that "category" is misspelled as "catagory" in your example data, so I also had to misspell it in the query above.