MongoDB agregation and comparing - mongodb

I need to write mongodb query that will agregate items by quantinty (orders collestion). After agregation, or with agregation, it should identify items that can't be supplied in stock collection, and return name of the person who made this order
code of orders insert
db.orders.insertMany([
{client: {firstName: "John",lastName: "Smith" },orderedItems: [{ name: "itemA", qty: 5},{ name: "itemS", qty: 3}]},
{client: {firstName: "Jan",lastName: "Nowak" },orderedItems: [{ name: "itemA", qty: 56},{ name: "itemS", qty: 53}]},
{client: {firstName: "Klara",lastName: "BolÄ…czka" },orderedItems: [{ name: "itemA", qty: 35},{ name: "itemS", qty: 23}]},
{client: {firstName: "Brajan",lastName: "Kowalski" },orderedItems: [{ name: "itemA", qty: 95},{ name: "itemS", qty: 13}]}
]);
code of stocks insert
db.stock.insertMany([
{ item: "itemA", qty: 40},
{ item: "itemS", qty: 113}
]);
Right now i got some mongoDB code, that just agregates orders
db.orders.aggregate([
{$unwind: "$orderedItems"},
{$group: { _id: "$orderedItems.name", total:{$sum: "$orderedItems.qty"}}}
]);
I tried to use lookup, but probably I'm doing something wrong.
Could I get some sugestions, please?

How would you like something like this:
db.orders.aggregate([
{
$unwind: "$orderedItems" // flatten the "orderedItems" array
},
{
$group: {
"_id": "$orderedItems.name", // group by orderItems.name
"total": {
$sum: "$orderedItems.qty" // calculate total quantity
},
"users": {
$addToSet: "$client" // create an array of clients who have ordered each item
}
}
}, {
$lookup: { // retrieve the stock information
from: "stock",
localField: "_id",
foreignField: "item",
as: "stock"
}
}, {
$unwind: "$stock" // flatten the result array - should always return only one document, given your data model
}, {
$addFields: { // add a new field...
"difference": { // ...called "difference"...
$subtract: [ "$stock.qty", "$total" ] // ...in which we store the difference between the stock qty and the ordered qty
}
}
}, {
$match: { // only return documents...
"difference": { // ...where the value for "difference"...
$lt: 0 // ...is less than zero
}
}
}])

Related

How do I use mongo aggregation to lookup data in a subdocument?

I want to $lookup data from a subdocument in another collection. I have survey answers, and I want to group them by the question category's name.
The survey documents looks like this:
{
_id: new ObjectId("62555be60401f0a21553da9a"),
name: 'new survey',
questions: [
{
text: 'question 1',
category_id: new ObjectId("62555be60401f0a21553da99"),
options: [Array],
_id: new ObjectId("62555be60401f0a21553da9c"),
},
...
}
Category collection is just name and _id:
{
_id: new ObjectId("62555be60401r0a27553da99"),
name: "category name"
}
I have answer data like this:
[
{
answers: {
k: '62555be60401f0a21553da9c',
v: new ObjectId("62555880da8fb89651f6a292")
},
},
{
answers: {
k: '62555880da8fb89651f6a29b',
v: new ObjectId("62555880da8fb89651f6a29e")
},
}
...
]
k is a string that matches to the _id in the survey.questions array.
I'd like to get the resulting data like this:
[
{
answers: {
k: 'question 1',
v: new ObjectId("62555880da8fb89651f6a292")
},
category: 'category name'
},
{
answers: {
k: 'question 2',
v: new ObjectId("62555880da8fb89651f6a29e")
},
category: 'other category name'
}
...
]
any help would be greatly appreciated!
I think I could probably figure out the category part, but I cannot figure out how to use $lookup to get info from a subdocument. From the docs I'm guessing its maybe some pipeline within a lookup. Pretty stumped though.
You can do something like this, using a pipeline to match only surveys that have questions._id that matches the answer.k value
db.answer.aggregate([
{
$lookup: {
"from": "survey",
"let": {
"k": {
"$toObjectId": "$answers.k"
}
},
pipeline: [
{
"$match": {
"$expr": {"$in": ["$$k", "$questions._id"]}
}
}
],
as: "details"
}
},
{
$project: {
answers: 1,
details: {"$arrayElemAt": ["$details", 0]}
}
},
{
$project: {
answers: 1,
categoryData: {
$filter: {
input: "$details.questions",
as: "item",
cond: {$eq: ["$$item._id", {"$toObjectId": "$answers.k"}]}
}
}
}
},
{
$project: {
answers: 1,
catData: {"$arrayElemAt": ["$categoryData", 0]}
}
},
{
$lookup: {
from: "Category",
localField: "catData.category_id",
foreignField: "_id",
as: "cat"
}
},
{
$project: {
answers: 1,
_id: 0,
category: {"$arrayElemAt": ["$cat", 0]}
}
},
{
$project: {answers: 1, name: "$category.name"}
}
])
As you can see on the playground
Maybe it is possible to filter the results during the $lookup in order to simplify the rest of the query

$Lookup inside an array with more properties MongoDB

I am trying $lookup in Array but can't merge it in my object after $lookup.
Collection
_id: '5f7a1053477c8a1ae88e22cf',
name: 'Demo'
price: 423,
related: [
{
idProduct: '61140763ab806726a8ab7aea'
quantity: 2
},
{
idProduct: '61140763ab806726a8ab7aeb'
quantity: 6
},
]
Expected Output:
_id: '5f7a1053477c8a1ae88e22cf',
name: 'Demo',
price: 423,
related: [
{
idProduct: {
_id: '61140763ab806726a8ab7aea',
name: 'related1',
price: 22
},
quantity: 2
},
{
idProduct: {
_id: '61140763ab806726a8ab7aeb',
name: 'related2',
price: 53
},
quantity: 6
},
]
I need $lookup, idProduct and keep that data structure.
Any help? Thanks in advance
$unwind deconstruct related array
$lookup with collection 2, pass related.idProduct as local field and _id as foreign field and set in related.idProduct
$group by _id and reconstruct related array and get first required firlds
db.col1.aggregate([
{ $unwind: "$related" },
{
$lookup: {
from: "col2",
localField: "related.idProduct",
foreignField: "_id",
as: "related.idProduct"
}
},
{
$group: {
_id: "$_id",
name: { $first: "$name" },
price: { $first: "$price" },
related: { $push: "$related" }
}
}
])
Playground
The second approach without using $unwind stage,
$lookup with collection 2,
$map to iterate loop of related array
$filter to iterate loop of col2 array by idProduct field
$arrayElemAt to get first element from above filtered result
$mergeObjects to merge current object with updated idProduct field
db.col1.aggregate([
{
$lookup: {
from: "col2",
localField: "related.idProduct",
foreignField: "_id",
as: "col2"
}
},
{
$addFields: {
col2: "$$REMOVE",
related: {
$map: {
input: "$related",
as: "r",
in: {
$mergeObjects: [
"$$r",
{
idProduct: {
$arrayElemAt: [
{
$filter: {
input: "$col2",
cond: { $eq: ["$$r.idProduct", "$$this._id"] }
}
},
0
]
}
}
]
}
}
}
}
}
])
Playground

Mongo Aggregation Grouping and Mapping of Object Array

I'm need to group a mongo collection and aggregate.
Example Collection
[{id:"1", skill: "cooking"},{id:"1", skill: "fishing"}]
Lookup Collection
[{ name: "cooking", value: 3 }, { name: "fishing", value: 2 }]
Desired Result
[{id: "1", skills: [{ value: 3, "cooking" }, { value: 2, "fishing"}]}]
Here's how far I am.
db.talent.aggregate([
{
$group: '$id'
skills: { $addToSet: '$skill' }
},
])
Result:
[{id: "1", skills: ["cooking", "fishing"]}]
I'm wondering if this is even possible.
I miss SQL, need help!
We can do this using $lookup, $group and $project in the aggregation pipeline
Shown below is the mongodb shell query
db.example_collection.aggregate([
{
$lookup: {
from: "lookup_collection",
localField: "skill",
foreignField: "name",
as: "skills"
}
},
{
$group: {
_id: "$id",
skills: {
$push: "$skills"
}
}
},
{
$project: {
"id": "$_id",
"skills.name": 1,
"skills.value": 1,
"_id": 0
}
}
])

mongodb nested array element $lookup

I have two collections, orders and products. I like to join all the order.items[] to products collection to add more fields to the items[]
Sample Data:
orders
[{ _id: 1, items: [
{ product_id: 1, price: 1.99, qty: 2 },
{ product_id: 2, price: 3.99, qty: 5 } ]}]
products
[{ _id: 1, name: "Product 1" }, { _id: 2, name: "Product 2 }]
Expected output:
[{ _id: 1, items: [
{ product_id: 1, name: "Product 1", price: 1.99, qty: 2 },
{ product_id: 2, name: "Product 2",, price: 3.99, qty: 5 } ]}]
I have tried using $lookup and pipeline (mongodb 3.6) and not getting the name value or even the match is not working.
Thanks for a help!
This query will help you, sorry if I didn't use v3.6.
db.orders.aggregate([
{
$unwind: "$items"
},
{
$lookup:
{
from: "products",
localField: "items.product_id",
foreignField: "_id",
as: "tproduct"
}
},
{
$project:
{
"_id" : 1,
"items.product_id" : 1,
"items.name" : { $arrayElemAt: ["$tproduct.name", 0] },
"items.price" : 1,
"items.qty" : 1
}
},
{
$group :
{
_id : "$_id",
items: { $push: "$items" }
}
}
])
They are 4 stages that I will explain:
$unwind will create a single object for each element in the array.
$lookup will find the correct product, keep in mind that Product._id should be unique.
$project will format my documents and in items.name I'm taking the first element of the lookup sentence.
$group will use the _id to group and push each item into a new array.
I'm pretty sure there are cleaner and easier ways to write this, but this should work without problems.

Aggregation filter and lookup on Mongodb

My first collection employeecategory is like below;
[{
name: "GARDENING"
},
{
name: "SECURITY"
},
{
name: "PLUMBER"
}
]
My second collection complaints is like below;
[{
communityId: 1001,
category: "SECURITY",
//other fields
}, {
communityId: 1001,
category: "GARDENING",
//other fields
}]
I am trying to join above tables and get the below result;
[{
"count": 1,
"name": "GARDENING"
}, {
"count": 1,
"name": "SECURITY"
}, {
"count": 0,
"name": "PLUMBER"
}]
Even if there are no records in collection 2 I need count. I tried below aggregation but didn't worked. If I removed match condition it is working but I need to filter on community id. Could some please suggest best way to do achieve this. Mongo DB version is 3.4.0
db.employeecategory.aggregate(
[{
$match: {
"complaints.communityId": 1001
}
}, {
"$lookup": {
from: "complaints",
localField: "name",
foreignField: "category",
as: "embeddedData"
}
}]
)
It was not possible to achieve both filtering for communityId = 1001 and grouping without losing count = 0 category in a single aggregation. The way to do it is first start from complaints collection, and filter the communityId = 1001 objects, and create a temp collection with it. Then from employeecategory collection, $lookup to join with that temp collection, and $group with name, you will have your result at this point, then drop the temp table.
// will not modify complaints document, will create a filtered temp document
db.complaints.aggregate(
[{
$match: {
communityId: 1001
}
},
{
$out: "temp"
}
]
);
// will return the answer that is requested by OP
db.employeecategory.aggregate(
[{
$lookup: {
from: "temp",
localField: "name",
foreignField: "category",
as: "array"
}
}, {
$group: {
_id: "$name",
count: {
$sum: {
$size: "$array"
}
}
}
}]
).pretty();
db.temp.drop(); // to get rid of this temporary collection
will result;
{ _id: "PLUMBER", count: 0},
{ _id: "SECURITY", count: 2},
{ _id: "GARDENING", count: 1}
for the test data I've had;
db.employeecategory.insertMany([
{ name: "GARDENING" },
{ name: "SECURITY" },
{ name: "PLUMBER" }
]);
db.complaints.insertMany([
{ category: "GARDENING", communityId: 1001 },
{ category: "SECURITY", communityId: 1001 },
{ category: "SECURITY", communityId: 1001 },
{ category: "SECURITY", communityId: 1002 }
]);