mongo aggregate how to choose ‘$graphLookup or $lookup’ - mongodb

I don't know how to choose ‘$graphLookup or $lookup’ Other similar
I am looking forward to mongodb officially more complete documentation
example:
{parentId: 0, cid: 1, other: 'a'},
{parentId: 0, cid: 2, other: 'b'},
{parentId: 0, cid: 3, other: 'c'},
{parentId: 1, cid: 11, other: 'aa'},
{parentId: 2, cid: 12, other: 'ab'},
{parentId: 3, cid: 13, other: 'ac'},
result:
{
parentId: 0, cid: 1, other: 'a',
children: [
{parentId: 1, cid: 11, other: 'aa'},
]
},{
parentId: 0, cid: 2, other: 'b',
children: [
{parentId: 2, cid: 12, other: 'ab'},
]
},{
parentId: 0, cid: 3, other: 'c',
children: [
{parentId: 3, cid: 13, other: 'ac'},
]
}
},
how do?

You need to use $graphLookup
db.collection.aggregate([
{
$match: {
"parentId": {
$eq: 0
}
}
},
{
$graphLookup: {
from: "collection",
startWith: "$cid",
connectFromField: "cid",
connectToField: "parentId",
as: "children"
}
}
])
$lookup is used to “joined” collection for processing.

Related

Get last timestamp from an array in pymongo

I've got the following list of documents:
[
{'id': 1,
'name': 'Coco',
'data': [{'X': 10, 'datetime': datetime.datetime(2020, 1, 1, 1, 0)},
{'X': 20, 'datetime': datetime.datetime(2019, 1, 1, 2, 0)}]
},
{'id': 2,
'name': 'Kiki',
'data': [{'X': 30, 'datetime': datetime.datetime(2015, 7, 10, 1, 0)},
{'X': 40, 'datetime': datetime.datetime(2017, 10, 4, 2, 0)}]
]
How do I get the last timestamp from each list in the data field? I would like to get something like:
[
{'id': 1,
'name': 'Coco',
'data': [{'X': 10, 'datetime': datetime.datetime(2020, 1, 1, 1, 0)}]
},
{'id': 2,
'name': 'Kiki',
'data': [{'X': 40, 'datetime': datetime.datetime(2017, 10, 4, 2, 0)}]
]
Try this:
db.collectionName.aggregate([
{
$addFields: {
"data": [{
$reduce: {
input: "$data",
initialValue: { $arrayElemAt: ["$data", 0] },
in: {
$cond: [
{ $gt: ["$$value.datetime", "$$this.datetime"] },
"$$value",
"$$this"
]
}
}
}]
}
}
]);
Output:
/* 1 createdAt:3/3/2021, 9:15:27 PM*/
{
"_id" : ObjectId("603faf17bcece4372062bcf5"),
"id" : 1,
"name" : "Coco",
"data" : [
{
"X" : 10,
"datetime" : ISODate("2020-02-01T01:00:00.000+05:30")
}
]
},
/* 2 createdAt:3/3/2021, 9:15:27 PM*/
{
"_id" : ObjectId("603faf17bcece4372062bcf6"),
"id" : 2,
"name" : "Kiki",
"data" : [
{
"X" : 40,
"datetime" : ISODate("2017-11-04T02:00:00.000+05:30")
}
]
}

Merge 2 array objects into 1

I am trying to merge objects within an array based on another array. What I have is,
Orders
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2 },
{ item_id: 2, quantity: 1}
]
}
Items
{
_id: 43,
item_name: "tshirt"
}
{
_id: 2,
item_name: "jeans"
}
After lookup I am getting the below document with 2 arrays - entries and items. I would like to have entries contain the corresponding item.
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2 },
{ item_id: 2, quantity: 1}
]
items: [
{ item_id: 43, item_name: "tshirt" },
{ item_id: 2, item_name: "jeans" },
]
}
Desired Output:
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2, item_name: "tshirt" },
{ item_id: 2, quantity: 1, item_name: "jeans"}
]
}
I was able to achieve this by unwinding both arrays, addFields and then grouping as mentioned by #whoami.
See current pipeline: https://mongoplayground.net/p/rL-Lzmfuw9h
Is there any way to achieve this without unwinding?

Mongo aggregate, group, max and take entire row that have the max value

LATER EDIT: I managed to solve it using $filter inside $project. $filter will return an Array with a single object (if there is a single maxValue per instanceId-entity combination) and it must be $unwind-ed. Code:
{$group: {
_id: {instanceId: "$instanceId", entity: "$entity"},
maxValue: {$max: "$value"},
originalDocs: {"$push": "$$ROOT"}
}
},
{$project: {
"originalDoc": { "$filter": {
"input": "$originalDocs",
"as": "doc",
"cond": {
"$eq": [ "$maxValue", "$$doc.value" ]
}
}
},
}
},
{ $unwind : "$originalDoc" },
{ $project: {
"instanceId": "$originalDoc.instanceId",
"entity": "$originalDoc.entity",
"value": "$originalDoc.value",
"details": "$originalDoc.details",
}
}
I have this data:
id: 1, instanceId: 1, entity: a, value: 1, details: "some details 1"
id: 2, instanceId: 1, entity: a, value: 3, details: "some details 2"
id: 3, instanceId: 1, entity: a, value: 5, details: "some details 3"
id: 4 ,instanceId: 1, entity: a, value: 10, details: "some details 4"
id: 5, instanceId: 1, entity: b, value: 1, details: "some details 5"
id: 6, instanceId: 1, entity: b, value: 2, details: "some details 6"
id: 7, instanceId: 1, entity: b, value: 5, details: "some details 7"
id: 8, instanceId: 2, entity: a, value: 3, details: "some details 8"
id: 9, instanceId: 2, entity: a, value: 4, details: "some details 9"
id:10, instanceId: 2, entity: a, value: 6, details: "some details 10"
I did an aggregate to group by [instanceId and entity], and take the MAX value:
$group:{
_id:{instanceId: "$instanceId", entityName: "$entityName"},
maxValue: {$max: "$value"},
}
and got this which is fine:
id: 4 ,instanceId: 1, entity: a, maxValue: 10
id: 7, instanceId: 1, entity: b, maxValue: 5
id:10, instanceId: 2, entity: a, maxValue: 6
but i also want the details. This is what i want:
id: 4 ,instanceId: 1, entity: a, maxValue: 10, details: "some details 4"
id: 7, instanceId: 1, entity: b, maxValue: 5, details: "some details 7"
id:10, instanceId: 2, entity: a, maxValue: 6, details: "some details 10"
If i Use "$push": "$$ROOT" then all my documents grouped for each combination will be pushed and will obtain something like this:
id: 4 ,instanceId: 1, entity: a, maxValue: 10, originalDocument: Array[4]
id: 7, instanceId: 1, entity: b, maxValue: 5, originalDocument: Array[3]
id:10, instanceId: 2, entity: a, maxValue: 6, originalDocument: Array[3]
I only want the original document corresponding to the max value
Try this aggregate query, it will solve your problem.
[{
$group: {
_id: {
instanceId: "$instanceId",
entity: "$entity"
},
maxValue: {
$max: "$value"
},
details: {
$push: '$$ROOT'
}
}
}, {
"$project": {
_id: 0,
proj_details: {
"$setDifference": [{
"$map": {
"input": "$details",
"as": "mapped",
"in": {
"$cond": [{
"$eq": ["$maxValue", "$$mapped.value"]
},
"$$mapped",
false
]
}
}
},
[false]
]
}
}
}, {
$unwind: '$proj_details'
}, {
$project: {
"id": '$proj_details.id',
"instanceId": '$proj_details.instanceId',
"entity": "$proj_details.entity",
"value": '$proj_details.value',
"details": "$proj_details.details"
}
}]

How to do mongodb inner join with nested array?

Warehouses schema:
{_id: 1, name: 'A'}
{_id: 2, name: 'B'}
{_id: 3, name: 'C'}
Stocks schema:
{_id: 11, productId: 1, instock: [{warehouse: 'A', qty: 20}, {warehouse: 'B', qty: 5}, {warehouse: 'C', qty: 8}]
{_id: 12, productId: 2, instock: [{warehouse: 'A', qty: 30}]
I am new to MongoDB, but will like to have one row per record to show products' available qty in each of A,B,C warehouses:
Desired array output:
instock: [
{_id: 11, productId: 1, warehouse: 'A', qty: 20},
{_id: 11, productId: 1, warehouse: 'B', qty: 5},
{_id: 11, productId: 1, warehouse: 'C', qty: 8},
{_id: 12, productId: 2, warehouse: 'A', qty: 30},
{_id: 12, productId: 2, warehouse: 'B', qty: 0},
{_id: 12, productId: 2, warehouse: 'C', qty: 0}
]
I read about $lookup, $unwind, $project, and tried something like below but no where near to what I want:
Warehouse.aggregate([
{
$lookup:
{
from: "stocks",
pipeline: [
{ $project: { _id: 0, instock: {qty: 1, warehouse: 1} }},
{ $replaceRoot: { newRoot: { newStock : '$instock' } } }
],
as: "instock"
}
} ,
]);
hi, Anothony Winzlet, your advise works partially, for example:
{_id: 12, productId: 2, instock: [{warehouse: 'A', qty: 30}]
From your solution:
Result show only for warehouse A:
[{_id: 12, productId: 2, warehouse: 'A', qty: 30}]
Can I get for warehouse B & C as well? (will default qty to 0 if not defined)
[{_id: 12, productId: 2, warehouse: 'A', qty: 30},
{_id: 12, productId: 2, warehouse: 'B', qty: 0},
{_id: 12, productId: 2, warehouse: 'C', qty: 0}]
Not sure if above is possible to achieve ... thank you
Solution from Anthony Winzlet:
Warehouse.aggregate([
{ "$unwind": "$instock" },
{ "$replaceRoot": { "newRoot": { "$mergeObjects": ["$$ROOT", "$instock"] } }},
{ "$project": { "instock": 0 } }
])

MongoDB: $filter multiple arrays

I have an collection like:
{
_id: 0,
items: [
{ item_id: 43, quantity: 2, price: 10 },
{ item_id: 2, quantity: 1, price: 240 }
],
T: [
{ item_id: 2993, quantity: 3, price: 110 },
{ item_id: 90103, quantity: 4, price: 5 },
{ item_id: 398, quantity: 1, price: 300 }
]
}
{
_id: 1,
items: [
{ item_id: 23, quantity: 3, price: 110 },
{ item_id: 103, quantity: 4, price: 5 },
{ item_id: 38, quantity: 1, price: 300 }
],
T: [
{ item_id: 23, quantity: 3, price: 110 },
{ item_id: 103, quantity: 4, price: 5 },
{ item_id: 38, quantity: 1, price: 300 }
]
}
{
_id: 2,
items: [
{ item_id: 4, quantity: 1, price: 23 }
],
T: [
{ item_id: 203, quantity: 3, price: 110 },
{ item_id: 003, quantity: 4, price: 5 },
{ item_id: 398, quantity: 1, price: 300 }
]
}
I want to return a all the items in the items array with a price >= 100. That is done with the following:
$project: {
items: {
$filter: {
input: "$items",
as: "item",
cond: { $gte: [ "$$item.price", 100 ] }
}
}
}
How can I expand this expression to $filter on the items array and the T array all elements that have a price >= 100?
You can include both the items and T fields in the same $project, each with its own $filter:
db.test.aggregate([
{$project: {
items: {
$filter: {
input: "$items",
as: "item",
cond: { $gte: [ "$$item.price", 100 ] }
}
},
T: {
$filter: {
input: "$T",
as: "t",
cond: { $gte: [ "$$t.price", 100 ] }
}
}
}}
])