I want to aggregate data array inside another array in mongodb - mongodb

I want to aggregate MongoDB documents which is having arrays inside of an array. my document was like the below.
{
"_id": "6257e31d11a9d5231c05c084",
"name": "Test Name 1",
"phone": "1234567891",
"visits": [
{
"_id": "6257e31d11a9d5231c05c069",
"date": "2-7-2021",
"samples": [
"6257f8855197613b641d494e",
....
],
"products_detailed": [
"5d725cd2c4ded7bcb480eab2",
.....
]
},
...........
]
}
and I want to get the output line below
{
"_id": "6257e31d11a9d5231c05c084",
"name": "Test Name 1",
"phone": "1234567891",
"visits": [
{
"_id": "6257e31d11a9d5231c05c069",
"date": "2-7-2021",
"samples": [
{
"_id": "6257f8855197613b641d494e",
"product_name": "Samor",
"price": 250
},
........
],
"products_detailed": [
{
"_id": "5d725cd2c4ded7bcb480eab2",
"product_name": "Pahad",
"price": 100
},
............
]
},
.........................
]
}
how can I get like this? I tried to use $lookup & group to get the output, but I am not getting the output as required me.

Since you have a list of visits on each document, one way to go is to $unwind and then $group at the end, like this:
db.Main.aggregate([
{
$unwind: "$visits"
},
{
"$lookup": {
"from": "Samples",
"localField": "visits.samples",
"foreignField": "_id",
"as": "samples"
}
},
{
"$lookup": {
"from": "Product Detailed",
"localField": "visits.products_detailed",
"foreignField": "_id",
"as": "products_detailed"
}
},
{
$project: {
name: 1,
phone: 1,
"visits._id": 1,
"visits.date": 1,
"visits.products_detailed": "$products_detailed",
"visits.samples": "$samples"
}
},
{
$group: {
_id: 0,
name: {$first: "$name"},
visits: {$push: "$visits"}
}
}
])
As you can see on the playground, on your data sample it will return:
[
{
"_id": 0,
"name": "Test Name 1",
"visits": [
{
"_id": "6257e31d11a9d5231c05c069",
"date": "2-7-2021",
"products_detailed": [
{
"_id": "5d725cd2c4ded7bcb480eab2",
"price": 100,
"product_name": "Pahad"
}
],
"samples": [
{
"_id": "6257f8855197613b641d494e",
"price": 250,
"product_name": "Samor"
}
]
}
]
}
]

Related

How to replace array of object containing ids with the data using MongoDB aggregation

I am having a collection which contains the data like the following and want to have the desirable output which I have mentioned below.
db={
collectionA: [
{
"id": ObjectId("63b7c24c06ebe7a8fd11777b"),
"uniqueRefId": "UUID-2023-0001",
"products": [
{
"productIndex": 1,
"productCategory": ObjectId("63b7c24c06ebe7a8fd11777b"),
"productOwners": [
ObjectId("63b7c2fd06ebe7a8fd117781")
]
},
{
"productIndex": 2,
"productCategory": ObjectId("63b7c24c06ebe7a8fd11777b"),
"productOwners": [
ObjectId("63b7c2fd06ebe7a8fd117781"),
ObjectId("63b7c12706ebe7a8fd117778")
]
},
{
"productIndex": 3,
"productCategory": "",
"productOwners": ""
}
]
}
],
collectionB: [
{
"_id": ObjectId("63b7c2fd06ebe7a8fd117781"),
"fullname": "Jim Corbett",
"email": "jim.corbett#pp.com"
},
{
"_id": ObjectId("63b7c12706ebe7a8fd117778"),
"fullname": "Carry Minatti",
"email": "carry.minatty#pp.com"
},
]
}
Desirable Output = [
{
"id": ObjectId("507f1f77bcf86cd799439011"),
"uniqueRefId": "UUID-2023-0001",
"products": [
{
"productIndex": 1,
"productCategory": ObjectId('614g2f77bff86cd755439021'),
"productOwners": [
{
"_id": ObjectId("63ac1e59c0afb8b6f2d41acd"),
"fullname": "Jim Corbett",
"email": "jim.corbett#pp.com"
}
]
},
{
"productIndex": 2,
"productCategory": ObjectId('614g2f77bff86cd755439021'),
"productOwners": [
{
"_id": ObjectId("63ac1e59c0afb8b6f2d41acd"),
"fullname": "Jim Corbett",
"email": "jim.corbett#pp.com"
},
{
"_id": ObjectId("63ac1e59c0afb8b6f2d41ace"),
"fullname": "Carry Minatti",
"email": "carry.minatty#pp.com"
}
]
},
{
"productIndex": 3,
"productCategory": "",
"productOwners": ""
}
]
}
]
In the collectionA we are having other documents as well, its not just one document.
Similarly for collectionB we are having other documents too.
How we can get this desirable output?
I am expecting the mongodb query for getting this solution.
I have implemented the lookup like the following
db.collectionA.aggregate([
{
"$lookup": {
"from": "collectionB",
"localField": "products.productOwners",
"foreignField": "_id",
"as": "inventory_docs"
}
}
])
You can try this:
db.collectionA.aggregate([
{
"$unwind": "$products"
},
{
"$lookup": {
"from": "collectionB",
"localField": "products.productOwners",
"foreignField": "_id",
"as": "products.productOwners"
}
},
{
"$group": {
"_id": {
id: "$id",
uniqueRefId: "$uniqueRefId"
},
"products": {
"$push": "$products"
}
}
},
{
"$project": {
id: "$_id.id",
uniqueRefId: "$_id.uniqueRefId",
products: 1,
_id: 0
}
}
])
Playground link.
In this query, we do the following:
First we unwind the products array, using $unwind.
Then we calculate productOwners, using $lookup.
Then we group the unwinded elements, using $group.
Finally we, project the desired output using $project.

Mongodb aggregation lookup join two collection array of object fields sum of matched object index field

I have a two collections "datasets" and "users". I tried to lookup for array of object both collections.
I want to join the "datasets.stateHistory.date" field and "users.prices.date" field. get the result of the datasets collection i want sum of "users.prices.price" sum values
Datasets json Data:
"datasets": [
{
"colorDescription": "braun, rose gold",
"stateHistory": [
{
"state": "scanning",
"date": "2022-02-22T13:06:13.493+00:00"
},
{
"state": "scanned",
"date": "2022-02-18T13:06:13.493+00:00"
},
{
"state": "reconstructing",
"date": "2022-02-16T13:06:13.493+00:00"
}
]
},
{
"colorDescription": "beige, silber",
"stateHistory": [
{
"state": "scanning",
"date": "2022-03-22T13:06:13.493+00:00"
},
{
"state": "scanned",
"date": "2022-03-18T13:06:13.493+00:00"
},
{
"state": "reconstructing",
"date": "2022-03-16T13:06:13.493+00:00"
}
]
}
]
Users json Data:
"users": [
{
"name": "Aravinth",
"prices": [
{
"date": "2022-02-16T13:06:13.493+00:00",
"price": 45
},
{
"date": "2022-03-22T13:06:13.493+00:00",
"price": 55
}
]
},
{
"name": "Raja",
"prices": [
{
"date": "2022-02-24T13:06:13.493+00:00",
"price": 75
},
{
"date": "2022-03-23T13:06:13.493+00:00",
"price": 85
}
]
}
]
Expected result json Data:
[
{
"colorDescription": "braun, rose gold",
"cgPrices: 45,
"stateHistory": [
{
"state": "scanning",
"date": "2022-02-22T13:06:13.493+00:00"
},
{
"state": "scanned",
"date": "2022-02-18T13:06:13.493+00:00"
},
{
"state": "reconstructing",
"date": "2022-02-16T13:06:13.493+00:00"
}
]
},
{
"colorDescription": "beige, silber",
"cgPrices: 0,
"stateHistory": [
{
"state": "scanning",
"date": "2022-03-22T13:06:13.493+00:00"
},
{
"state": "scanned",
"date": "2022-03-18T13:06:13.493+00:00"
},
{
"state": "reconstructing",
"date": "2022-03-16T13:06:13.493+00:00"
}
]
}
]
"cgPrice" field i need to sum of matched prices with date of two collection added.
my code:
db.datasets.aggregate([
{
"$lookup": {
"from": "users",
"as": "details",
"localField": "stateHistory.date",
"foreignField": "prices.date"
}
},
{
"$project": {
color: "$details.colorDescription",
prices: "$details"
}
}
])
How to join the lookup and get prices for matched field add the additional field "cgPrice" count sum.
mongo playground link: https://mongoplayground.net/p/vv8R3DlEDYo
You just need to do quite a lot of restructure, here is an example using the $map, $filter and $reduce operators:
db.datasets.aggregate([
{
"$lookup": {
"from": "users",
"as": "details",
"localField": "stateHistory.date",
"foreignField": "prices.date"
}
},
{
"$project": {
colorDescription: 1,
stateHistory: 1,
prices: {
$sum: {
$map: {
input: {
$filter: {
input: {
$reduce: {
input: {
$map: {
input: "$details",
in: "$$this.prices"
}
},
initialValue: [],
in: {
"$concatArrays": [
"$$this",
"$$value"
]
}
}
},
cond: {
$in: [
"$$this.date",
"$stateHistory.date"
]
}
}
},
in: "$$this.price"
}
}
}
}
}
])
Mongo Playground

MongoDB $lookup on array of objects with reference objectId

I have Orders collection and iam getting the data from it as shown below:
[
{
"_id": "628216b7b30bb8aa80c8fd1a",
"promotionsDetails": {
"companyTotalPrice": 27,
"promotionsData": [
{
"_id": "621de063bb5f9f0bf510897f",
"price": 27,
"companyId": "621dd85eb45ca2ae292d9a36"
},
{
"_id": "621de063bb5f9f0bf510897d",
"price": 19,
"companyId": "621dd85eb45ca2ae292d9a32"
}
]
}
},
{
"_id": "628214fcb30bb8aa80c8fd18",
"promotionsDetails": {
"companyTotalPrice": 46,
"promotionsData": [
{
"_id": "621de063bb5f9f0bf510897f",
"price": 46,
"companyId": "621dd85eb45ca2ae292d9a32",
}
]
},
}
]
what I am trying to do is to get the company details from the companies collection using the companyId objectId in each object in the array, like below:
[
{
"_id": "628216b7b30bb8aa80c8fd1a",
"promotionsDetails": {
"companyTotalPrice": 27,
"promotionsData": [
{
"_id": "621de063bb5f9f0bf510897f",
"price": 27,
"companyId": "621dd85eb45ca2ae292d9a36",
"companyData": { "title": "..." }
},
{
"_id": "621de063bb5f9f0bf510897d",
"price": 19,
"companyId": "621dd85eb45ca2ae292d9a32",
"companyData": { "title": "..." }
}
]
}
},
{
"_id": "628214fcb30bb8aa80c8fd18",
"promotionsDetails": {
"companyTotalPrice": 46,
"promotionsData": [
{
"_id": "621de063bb5f9f0bf510897f",
"price": 46,
"companyId": "621dd85eb45ca2ae292d9a32",
"companyData": { "title": "..." }
}
]
}
}
]
i have tried to use lookup and pipeline, but I'm not getting the desired result, thanks!
Actuality $lookup supports arrays, so there is no need to $unwind and change the structure. This will return your expected results:
db.Orders.aggregate([
{
$lookup: {
from: "Company",
localField: "promotionsDetails.promotionsData.companyId",
foreignField: "_id",
as: "companyfullData"
}
},
{
$set: {
"promotionsDetails.promotionsData": {
$map: {
input: "$promotionsDetails.promotionsData",
in: {
$mergeObjects: [
"$$this",
{
companyData: {
$arrayElemAt: [
"$companyfullData",
{$indexOfArray: ["$companyfullData.id", "$$this.id"]}
]
}
}
]
}
}
}
}
},
{$unset: "companyfullData"}
])
Playground
Here, to make it more clear take a look at Mongo playground
db.Orders.aggregate([
{
$unwind: "$promotionsDetails.promotionsData"
},
{
"$lookup": {
"from": "Company",
"localField": "promotionsDetails.promotionsData.companyId",
"foreignField": "_id",
"as": "promotionsDetails.promotionsData.companyData"
}
},
])

How to add embedded field with matching documents

I'm using Python with pymongo to query from the database.
I have 3 different collections:
1st:
# Projects collection
{
"_id": "A",
},
{
"_id": "B",
},
{
"_id": "C"
},
..
2nd:
# Episodes collection
{
"_id": "A/Episode01",
"project": "A",
"name": "Episode01"
},
{
"_id": "A/Episode02",
"project": "A",
"name": "Episode02"
},
{
"_id": "B/Episode01",
"project": "B",
"name": "Episode01"
},
..
3rd:
# Sequences collection
{
"_id": "A/Episode01/Sequence01",
"project": "A",
"episode": "Episode01",
"name": "Sequence01"
},
{
"_id": "A/Episode02/Sequence02",
"project": "A",
"episode": "Episode02",
"name": "Sequence02"
},
{
"_id": "B/Episode01/Sequence01",
"project": "B",
"episode": "Episode01",
"name": "Sequence01"
},
..
I want to use aggregate to query project A and get all of its corresponding episodes and sequences like this:
{
"_id": "A",
"episodes":
[
{
"_id": "A/Episode01",
"project": "A",
"name": "Episode01",
"sequences":
[
{
"_id": "A/Episode01/Sequence01",
"project": "A",
"episode": "Episode01",
"name": "Sequence01"
},
]
},
{
"_id": "A/Episode02",
"project": "A",
"name": "Episode02",
"sequences":
[
{
"_id": "A/Episode02/Sequence02",
"project": "A",
"episode": "Episode02",
"name": "Sequence02"
},
]
},
]
}
I can get as far as getting the proper episodes, but I'm not sure how to add an embed field for any matching sequences. Is it possible to do this all in a single pipeline query?
Right now my query is looking like this:
[
{"$match": {
"_id": "A"}
},
{"$lookup": {
"from": "episodes",
"localField": "_id",
"foreignField": "project",
"as": "episodes"}
},
{"$group": {
"_id": {
"_id": "$_id",
"episodes": "$episodes"}
}}
]
You can do like following
use $match to match the document
use uncorrelated queries to join two collection. But normal joining also possible as you have written. This is easier when we get some complex situations.
Mongo script is given below
[
{
"$match": {
"_id": "A"
}
},
{
$lookup: {
from: "Episodes",
let: {
id: "$_id"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$project",
"$$id"
]
}
}
},
{
$lookup: {
from: "Sequences",
let: {
epi: "$name"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$episode",
"$$epi"
]
}
}
}
],
as: "sequences"
}
}
],
as: "episodes"
}
}
]
Working Mongo playground
Update 01
Using standard lookup
[
{
"$match": {
"_id": "A"
}
},
{
"$lookup": {
"from": "Episodes",
"localField": "_id",
"foreignField": "project",
"as": "episodes"
}
},
{
$unwind: "$episodes"
},
{
"$lookup": {
"from": "Sequences",
"localField": "episodes.name",
"foreignField": "episode",
"as": "episodes.sequences"
}
},
{
$group: {
_id: "$episodes._id",
episodes: {
$addToSet: "$episodes"
}
}
}
]
Working Mongo playground

MongoDb aggregation $lookup with foreign _ids in arrays

I'm a MongoDb novice. I'm getting pretty good, but no expert yet. I'm trying setup my collections in a way that makes sense. I'd like to keep some links to foreign docs inside arrays of just _ids and also arrays of objects that have _ids.
I created a JSON doc with notes that I think fully shows what I'm trying to do...
// ( item ) Character Inventory/Items collection
[
{
"_id": "1234",
"name": "Sword",
"descr": "Long sword, well worn, light rust",
"encumber": 2,
"del": false
},
{
"_id": "1271",
"name": "Pouch",
"descr": "Small leather waist pouch, suitable for coins",
"encumber": 0,
"del": false
}
]
// ( charnpcclass ) Character Classes collection
[
{ "_id": "2", "name": "Thief", "del": false },
{ "_id": "3", "name": "Cleric", "del": false }
]
// ( charnpcalign ) Character Alignments collection
[
{ "_id": "3", "name": "Lawful Good", "del": false },
{ "_id": "4", "name": "Neutral", "del": false }
]
// ( character ) Characters collection
[
{
"_id": "3345",
"name": "Offut 'Dead Dog' Dubro",
"description": "Halfling, scruffy, looks homeless",
"align": ObjectId("4"),
"classes": [
ObjectId("2"),
ObjectId("3")
],
"carrying": [
{ "itemId": ObjectId("1271"), "qty":1, "where": "Sheath inside vest", "visible": false }
{ "itemId": ObjectId("1234"), "qty":1, "where": "Sword scabbard at waist", "visible": true }
],
"del": false
}
]
// ------------------------------------------------------------
// This is my MongoDb aggregation in the REST api routes
var linkedModels = [
{
"$match": { "del": false }
}, {
"$lookup": {
from: "charnpcclass",
localField: "classes",
foreignField: "_id",
as: "linked_classes"
}
}, {
"$lookup": {
from: "charnpcalign",
localField: "alignId",
foreignField: "_id",
as: "linked_align"
}
}, {
"$lookup": {
from: "item",
localField: "carrying.itemId",
foreignField: "_id",
as: "linked_carrying"
}
}
];
db.collection('character').aggregate(linkedModels).toArray(function (err, docs) {
res.json(201, docs);
next();
});
// Query for Character, return items carrying with data from items collection
// ------------------------------------------------------------
// WHAT I *WANT* IN RESPONSE...
{
"id": "3345",
"name": "Offut 'Dead Dog' Dubro",
"description": "Halfling, scruffy, looks homeless",
"align": "4",
"classes": [
"2",
"3"
],
"carrying": [
{ "itemId": "1271", "qty":1, "where": "Sheath inside vest", "visible": false }
{ "itemId": "1234", "qty":1, "where": "Sword scabbard at waist", "visible": true }
],
"linked_align": [
{ "_id": "4", "name": "Neutral" },
],
"linked_classes": [
{ "_id": "2", "name": "Thief" },
{ "_id": "3", "name": "Cleric" }
],
"linked_carrying": [
{ "_id": "1271", "name": "Dagger", "encumber": 0 },
{ "_id": "1234", "name": "Sword", "encumber": 2 }
]
}
// ------------------------------------------------------------
// WHAT I ACTUALLY GET IN RESPONSE
{
"id": "3345",
"name": "Offut 'Dead Dog' Dubro",
"description": "Halfling, scruffy, looks homeless",
"align": "4",
"classes": [
"2",
"3"
],
"carrying": [
{ "itemId": "1271", "qty":1, "where": "Sheath inside vest", "visible": false }
{ "itemId": "1234", "qty":1, "where": "Sword scabbard at waist", "visible": true }
],
"linked_align": [
{ "_id": "4", "name": "Neutral" },
],
"linked_classes": [],
"linked_carrying": []
}
The problem that I hope you noticed is just above, at bottom of JSON response example. My linked arrays are empty and I'm not sure how to solve this.
I would greatly appreciate your expert MongoDb querying advice :-)
You have to $unwind to flatten the both scalar and sub document foreign _ids and add $group stage at the end of the pipeline to get back the original structure.
$first accumulator to keep the fields and $push with $arrayElemAt to accumulate the array values to adjust for $unwind
var linkedModels = [
{
"$match": { "del": false }
},
{
"$lookup": {
from: "charnpcalign",
localField: "align",
foreignField: "_id",
as: "linked_align"
}
},
{
"$unwind":"$classes"
},
{
"$lookup": {
from: "charnpcclass",
localField: "classes",
foreignField: "_id",
as: "linked_classes"
}
},
{
"$group": {
"_id": "$_id",
"name": {"$first":"$name"},
"align": {"$first":"$align"},
"classes":{"$push":"$classes"},
"carrying":{"$first":"$carrying"},
"linked_align":{"$first":"$linked_align"},
"linked_classes":{"$push":{"$arrayElemAt":["$linked_classes",0]}}
}
},
{
"$unwind":"$carrying"
},
{
"$lookup": {
from: "item",
localField: "carrying.itemId",
foreignField: "_id",
as: "linked_carrying"
}
},
{
"$group": {
"_id": "$_id",
"name": {"$first":"$name"},
"align": {"$first":"$align"},
"classes":{"$first":"$classes"},
"linked_align":{"$first":"$linked_align"},
"carrying":{"$push":"$carrying"},
"linked_carrying":{"$push":{"$arrayElemAt":["$linked_carrying",0]}}
}
}
]
You don't need the $unwind on the scalar array (classes) in 3.4 version and you can replace the {"classes":{"$push":"$classes"}} & {"linked_classes":{"$push":{$arrayElemAt:["$linked_classes",0]}}} with {"classes":{"$first":"$classes"}} & {"linked_classes":{"$first":"$linked_classes"}} respectively.