How to access the second level array in mongodb (with group) - mongodb

here is sample collection:
{
"_id":ObjectId("5cc7d8e88c33e065c56b0883"),
"age":70,
"child":[
{
"id":"son1",
"age":40,
"grandSon":[
{
"id":"grand1",
"age":10,
"like":[
{
"id":"like1",
"info":"apple"
},
{
"id":"like2",
"info":"banana"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":10
},
{
"id":"grandGrand2",
"age":13
}
]
},
{
"id":"grand2",
"age":13,
"like":[
{
"id":"like1",
"info":"apple"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":12
},
{
"id":"grandGrand2",
"age":14
}
]
}
]
},
{
"id":"son2",
"age":40,
"grandSon":[
{
"id":"grand1",
"age":10,
"like":[
{
"id":"like1",
"info":"apple"
},
{
"id":"like2",
"info":"banana"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":15
},
{
"id":"grandGrand2",
"age":16
}
]
},
{
"id":"grand2",
"age":14,
"like":[
{
"id":"like1",
"info":"apple"
},
{
"id":"like2",
"info":"banana"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":12
},
{
"id":"grandGrand2",
"age":13
}
]
}
}
]
}
]
I want to result like this
parent age and child'size and like grandSons Count, grandGrandSons count and child's like count
{
"_id": ObjectId("5cc7d8e88c33e065c56b0883"),
"age": 70,
"childCount": 2,
"grandSonsCount": 4
"likeCount": 7
"grandGrandSonsCount": 8,
"grandSonsAgeCount": 105
}
here is my code
{
$lookup:
{
from: "..."
localField: "...",
foreignField: "..",
as: "child"
},
},
{ $unwind: "$child" }
{ $group : {
_id : "$_id",
age: {$first:"$age"},
childCount: {$sum: 1},
grandSonsCount : {$sum : {$size : "$child.grandSon"}},
likeCount: {$sum : {$size : "$child.like"}},
grandGrandSonsCount :
{$sum : {$sum : {$size : "$child.grandSon.grandGrandSon"}}},
//it is return 4(grandSonsCount)
}},
I used lookup, making for above collection(child)
it return grandSon count, but I want to get grandGrandSon Count
how can I get nested array in nested array of size?
how can I have to do??

Add $reduce to the group stage:
{
"$group" : {
....
....
"grandGrandSonsCount" : {
"$sum" : {
"$reduce" : {
"input" : "$child.grandSon",
"initialValue" : 0,
"in" : {
"$sum" : [
"$$value",
{
"$size" : "$$this.grandGrandSon"
}
]
}
}
}
}
}
}

Related

How to use the key of mongoDB collection as a key with some value in my query result?

Suppose this is my collection :
{
{
"productType":"bike",
"productID":1,
"lat":22.7,
"long":77.4
},
{
"productType":"car",
"productID":5,
"lat":25.7,
"long":75.4
},
{
"productType":"bike",
"productID":2,
"lat":26.7,
"long":76.4
}
}
I want to use the product type as key to my mongo query and grouping to get result like this :
{
"bike":{
"1":{
"latitude":"22.7",
"longitude":"77.4"
},
"2":{
"latitude":"26.7",
"longitude":"76.4"
}
},
"car":{
"5":{
"latitude":"25.7",
"longitude":"75.4"
}
}
}
I tried to use $replaceRoot but could not get the desired result.
Try this query:
db.products.aggregate([
{
$group: {
_id: "$productType",
products: {
$push: {
"k": { $toString: "$productID" },
"v": {
latitude: "$lat",
longitude: "$long"
}
}
}
}
},
{
$replaceWith: {
array: [
{
k: "$_id",
v: { $arrayToObject: "$products" }
}
]
}
},
{
$replaceRoot: {
newRoot: { $arrayToObject: "$array" }
}
}
]);
Output:
/* 1 */
{
"bike" : {
"1" : {
"latitude" : 22.7,
"longitude" : 77.4
},
"2" : {
"latitude" : 26.7,
"longitude" : 76.4
}
}
},
/* 2 */
{
"car" : {
"5" : {
"latitude" : 25.7,
"longitude" : 75.4
}
}
}

Mongo Unwind child object and Filter

Here is the MongoPlayground about this problem
Today I have 2 nested arrays of object:
I have an array of meetings and inside of it an array of invites.
I need to project only the meetings, but with the invites filtered by the PartnerId. So I have many invites to different partners, but when each partner logs to the system, he can only see the invites for him and not the others. In other words, the array Invites will have only invites to it´s PartnerId.
I could get to the project part, but filter the the invites I am lost. Should I use a $filter or $group to another array?
here is what I got so far:
MongoPlayground
db.collection.aggregate([
{
"$match": {
"$nor": [
{
"Meetings": {
"$exists": false
}
},
{
"Meetings": {
"$size": 0.0
}
}
]
}
},
{
"$unwind": {
"path": "$Meetings"
}
},
{
"$project": {
"Meetings": "$Meetings"
}
},
{
"$replaceRoot": {
"newRoot": "$Meetings"
}
}
])
ANSWER that I did...
db.getCollection("ClientProject").aggregate(
[
{
"$match" : {
"$and" : [
{
"PartnerIds" : {
"$in" : [
"5f9b247f0ca60a000232cacf"
]
}
},
{
"$nor" : [
{
"Meetings" : {
"$exists" : false
}
},
{
"Meetings" : {
"$size" : 0.0
}
},
{
"Meetings" : {
"$eq" : null
}
}
]
}
]
}
},
{
"$unwind" : {
"path" : "$Meetings"
}
},
{
"$project" : {
"Meetings" : "$Meetings"
}
},
{
"$replaceRoot" : {
"newRoot" : "$Meetings"
}
},
{
"$addFields" : {
"Invites" : {
"$filter" : {
"input" : "$Invites",
"as" : "invite",
"cond" : {
"$eq" : [
"$$invite.PartnerId",
"5f9b247f0ca60a000232cacf"
]
}
}
}
}
}
],
{
"allowDiskUse" : false
}
);
Using $filter is a good way to achieve your goal, and here is how you can do that:
db.collection.aggregate([
{
"$match": {
"$nor": [
{
"Meetings": {
"$exists": false
}
},
{
"Meetings": {
"$size": 0.0
}
}
]
}
},
{
"$unwind": {
"path": "$Meetings"
}
},
{
"$project": {
"invites": {
"$filter": {
"input": "$Meetings.Invites",
"as": "invite",
"cond": {
"$eq": [
"$$invite.PartnerName",
"Thiago Parceiro "
]
}
}
}
}
}
])

How to do a Mongodb $lookup for local and foreign array fields

Trying to do $lookup s for local array fields which is inside an object.
Querying case collection :
{
"no" : "2020921008981",
"sale" : {
"soldItems" : [
{
"itemId" : "5b55ac7f0550de00210a3b24",
},
{
"itemId" : "5b55ac7f0550de00215584re",
}
],
"bills" : [
{
"billNo" : "2020921053467",
"insurancePlanId" : "160",
},
{
"billNo" : "2020921053467",
"insurancePlanId" : "170",
}
]
}
}
Item collection :
{
"_id" : ObjectId("5b55ac7f0550de00210a3b24"),
"code" : "ABCDE"
},
{
"_id" : ObjectId("5b55ac7f0550de00215584re"),
"code" : "PQRST"
}
Insurance collection :
{
"_id" : ObjectId("5b55aca20550de00210a6d25"),
"name" : "HIJKL"
"plans" : [
{
"_id" : "160",
"name" : "UVWZ",
},
{
"_id" : "161",
"name" : "LMNO",
}
]
},
{
"_id" : ObjectId("5b55aca20550de00210a6d25"),
"name" : "WXYZ"
"coveragePlans" : [
{
"_id" : "169",
"name" : "5ABC",
},
{
"_id" : "170",
"name" : "4XYZ",
}
]
}
Desired output :
{
"no" : "2020921008981",
"sale" : {}
"insurances" : "HIJKL \n WXYZ",
"items" : [
{
"_id" : ObjectId("5b55ac7f0550de00210a3b24"),
"code" : "ABCDE"
},
{
"_id" : ObjectId("5b55ac7f0550de00215584re"),
"code" : "PQRST"
}
]
}
The attempt to lookup using the local itemRefId field from the item collection. And to lookup using the local insurancePlanId from the insurance collection and then $reduce the returning array into the desired format for the insurances field:
{
$lookup:
{
from: "item",
let: { iid: "$sale.soldItems.itemId" },
pipeline: [
{
$match: {
$expr: {
$in: ["$_id", {
$map: {
input: "$$iid",
in: { $_id: "$$this" }
}
}
]
}
}
}
],
as: "items"
}
},
{
$lookup:
{
from: "insurance",
let: { iid: "$sale.insurances.insurancePlanId" },
pipeline: [
{
$match: {
$expr: {
$in: ["$insurance.plans._id", {
$map: {
input: "$$iid",
in: { $toObjectId: "$$this" }
}
}
]
}
}
}
],
as: "insurancesList"
}
},
{
$addFields: {
insurances: {
$reduce: {
input: "$insurancesList.name",
initialValue: "",
in: {
$cond: [ { "$eq": [ "$$value", "" ] }, "$$this", { $concat: [ "$$value", "\n", "$$this" ] } ]
}
}
}
}
}
This attempt returns a mongodb error. Any help to get the desired output would be appreciated.
db.case.aggregate([
{
$lookup: {
from: "insurance",
let: { ipids: "$salesOrder.invoices.insurancePlanId" },
pipeline: [
{
$unwind: "$coveragePlans"
},
{
$match: { $expr: { $in: ["$coveragePlans._id", "$$ipids"] } }
},
{
$project: { _id: 0, name: 1 }
}
],
as: "insurances"
}
},
{
$lookup: {
from: "item",
let: { iid: "$salesOrder.purchaseItems.itemRefId" },
pipeline: [
{
$match: {
$expr: {
$in: ["$_id", {
$map: {
input: "$$iid",
in: { $toObjectId: "$$this" }
}
}
]
}
}
}
],
as: "items"
}
},
{
$project: {
_id: 0,
caseNumber: 1,
insurances: {
$reduce: {
input: "$insurances",
initialValue: "",
in: { $concat: ["$$value", "$$this.name", " \n "] }
}
},
items: 1
}
}
])

Mongo aggregation to collect array elements based on field name

I have an aggregated mongo document like below. There are two different batches ("-Minor" and "-Major"), and each batch has "batchElements" too.
{
"_id" : "123",
"info" : {
"batch" : "Batch1-Minor"
},
"batchElements" : {
"elements" : [
{ }, { }, .... { }
]
}
},
{
"_id" : "123",
"info" : {
"batch" : "Batch2-Minor"
},
"batchElements" : {
"elements" : [
{ }, { }, .... { }
]
}
},
{
"_id" : "123",
"info" : {
"batch" : "Batch3-Major"
},
"batchElements" : {
"elements" : [
{ }, { }, .... { }
]
}
},
{
"_id" : "123",
"info" : {
"batch" : "Batch4-Major"
},
"batchElements" : {
"elements" : [
{ }, { }, .... { }
]
}
}
How can I collect all "batchElements" of "-Minor" and "-Major" and create a document as below;
Output:
{
"_id" : "123",
"minorElements" : [
[{}, {}, {}, ..... {} ], // elements of "Batch1-Minor"
[{}, {}, {}, ..... {} ], // elements of "Batch2-Minor"
... // elements of "BatchN-Minor"
],
"majorElements" : [
[{}, {}, {}, ..... {} ], // elements of "Batch3-Major"
[{}, {}, {}, ..... {} ], // elements of "Batch4-Major"
... // elements of "BatchN-Major"
]
}
You can start with $split to get the "type" of your batch as part of your $group _id. Then you can run another $group to make minor and major parts of the same document. In the last step you need $replaceRoot along with $arrayToObject to promote both arrays into root level.
db.collection.aggregate([
{
$group: {
_id: {
id: "$_id",
type: { $arrayElemAt: [ { $split: [ { $toLower: "$info.batch" }, "-" ] }, 1 ] }
},
docs: { $push: "$batchElements.elements" }
}
},
{
$group: {
_id: "$_id.id",
data: { $push: { k: { $concat: ["$_id.type","Elements"] }, v: "$docs" } }
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [ { _id: "$_id" }, { $arrayToObject: "$data" } ]
}
}
}
])
Mongo Playground

MongoDB nested object aggregation sum and sort

I have highly nested mongodb set of objects and i want to sort subofdocuments according to the result of sum their votes for example :
{
"_id":17846384es,
"company_name":"company1",
"products":[
{
"product_id":"123785",
"product_name":"product1",
"user_votes":[
{
"user_id":1,
"vote":1
},
{
"user_id":2,
"vote":2
}
]
},
{
"product_id":"98765",
"product_name":"product2",
"user_votes":[
{
"user_id":5,
"vote":3
},
{
"user_id":3,
"vote":3
}
]
}
]
}
i want to sort as descending products according to the result of sum their votes
the expected output is
{
"_id":17846384es,
"company_name":"company1",
"products":[
{
"product_id":"98765",
"product_name":"product2",
"user_votes":[
{
"user_id":5,
"vote":3
},
{
"user_id":3,
"vote":3
}
]
"votes":8
},
{
"product_id":"123785",
"product_name":"product1",
"user_votes":[
{
"user_id":1,
"vote":1
},
{
"user_id":2,
"vote":2
}
],
"votes":3
}
]
}
Any Idea ?
The following pipeline
db.products.aggregate([
{ $unwind: "$products" },
{
$project: {
company_name: 1,
products: 1,
totalVotes: {
$sum: "$products.user_votes.vote"
}
}
},
{ $sort: { totalVotes: -1 } },
{
$group: {
_id: "$_id",
company_name: { $first: "$company_name" },
products: { $push: "$products" }
}
}
])
would output
{
"_id" : "17846384es",
"company_name" : "company1",
"products" : [
{
"product_id" : "98765",
"product_name" : "product2",
"user_votes" : [
{
"user_id" : 5,
"vote" : 3
},
{
"user_id" : 3,
"vote" : 3
}
]
},
{
"product_id" : "123785",
"product_name" : "product1",
"user_votes" : [
{
"user_id" : 1,
"vote" : 1
},
{
"user_id" : 2,
"vote" : 2
}
]
}
]
}
In case you want to keep the sum of the votes at the product level as shown in your expected output simply modify the $project stage as follows
...
{
$project: {
company_name: 1,
products: {
product_id: 1,
product_name: 1,
user_votes: 1,
votes: { $sum: "$products.user_votes.vote" }
}
}
}
...