MongoDB : - Merge Two Array with same key - javascript

I have 3 collections, users, attendances, shifts.
I am trying to get attendances and shifts with the help of users
I have a date in shifts and the Date key in attendances and I am trying to merge both data into 1 as per date, and if not present then that key should be none
Below is the query I tried it is working to some extent but not got the final result
User.aggregate([
{ $sort: { workerId: 1 } },
{
$lookup: {
from: "shifts",
localField: "_id",
foreignField: "employeeId",
pipeline: [
{
$match: {
date: {
$gte: new Date(fromDate),
$lte: new Date(toDate),
},
},
},
{
$project: {
date: 1,
shiftCode: 1,
},
},
{
$sort: {
date: 1,
},
},
],
as: "shifts",
},
},
{
$project: {
_id: 1,
workerId: 1,
shiftListData: "$shifts",
},
},
{
$lookup: {
from: "attendances",
localField: "_id",
foreignField: "employeeId",
pipeline: [
{
$match: {
Date: { $gte: new Date(fromDate), $lte: new Date(toDate) },
},
},
{
$project: {
inTime: 1,
name: 1,
Date: 1,
},
},
],
as: "attendances",
},
},
]);
OutPut
[
{
"workerId": "1005",
"shiftListData": [
{
"_id": "63875e8182ebbe13ee9531d4",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d407",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf30",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d409",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
],
"attendances": [
{
"_id": "61307cd385b5055a15cec159",
"Date": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name2"
},
{
"_id": "63b236ef3980cffaf7715d62",
"inTime": "2023-01-02T07:14:08.000Z",
"Date": "2023-01-02T00:00:00.000Z",
"name": "name2"
}
]
},
{
"workerId": "1006",
"shiftListData": [
{
"_id": "63875e8182ebbe13ee9531d2",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d403",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf39",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d400",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
],
"attendances": [
{
"_id": "61307cd385b5055a15cec158",
"Date": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name"
},
{
"_id": "63b236ef3980cffaf7715d69",
"inTime": "2023-01-02T07:14:08.000Z",
"Date": "2023-01-02T00:00:00.000Z",
"name": "name"
}
]
}
]
I want to merge shiftListData and attendances as per date into one array
Example : -
[
{
"workerId": "1005",
"newData": [
{
"_id": "63875e8182ebbe13ee9531d4",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z",
"attendanceId": "61307cd385b5055a15cec159",
"attendanceDate": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name2"
},
{
"_id": "63b277a2f6a8eccb2d95d407",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf30",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z",
"attendanceId": "63b236ef3980cffaf7715d62",
"inTime": "2023-01-02T07:14:08.000Z",
"attendanceDate": "2023-01-02T00:00:00.000Z",
"name": "name2"
},
{
"_id": "63b277a2f6a8eccb2d95d409",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
]
},
{
"workerId": "1006",
"newData": [
{
"_id": "63875e8182ebbe13ee9531d2",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z",
"attendanceId": "61307cd385b5055a15cec158",
"attendanceDate": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name"
},
{
"_id": "63b277a2f6a8eccb2d95d403",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf39",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z",
"attendanceId": "63b236ef3980cffaf7715d69",
"inTime": "2023-01-02T07:14:08.000Z",
"attendanceDate": "2023-01-02T00:00:00.000Z",
"name": "name"
},
{
"_id": "63b277a2f6a8eccb2d95d400",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
]
}
]

One option is to add one more step to your pipeline which finds for each shift item its matching item in attendances using $filter:
{$set: {
shiftListData: {$map: {
input: "$shiftListData",
as: "shift",
in: {$mergeObjects: [
"$$shift",
{$ifNull: [
{$first: {
$filter: {
input: "$attendances",
cond: {$eq: ["$$this.Date", "$$shift.date"]}
}
}},
{}
]}
]}
}},
attendances: "$$REMOVE"
}}
See how it works on the playground example

Related

MongoDB match expression query not working

MongoDb query match expression not working.
I have a posts collection and want to return only those posts which match the user Id of the user who created it but my query does not seem to be working.
Sample Dataset
[
// 1
{
"_id": ObjectId("6257047cffd61ab62864c1ae"),
"type": "A",
"source": "B",
"status": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
// 2
{
"_id": ObjectId("6257047cffd61ab62864c1ad"),
"type": "B",
"source": "A",
"status": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
// 3
{
"_id": ObjectId("6257047cffd61ab62864c1ce"),
"type": "A",
"source": "C",
"status": "B",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
// 4
{"_id": ObjectId("6257047cffd61ab62864c1cb"),
"type": "A",
"source": "B",
"status": "C",
"user": ObjectId("622b56250b0af6b049c387d6")
}
]
MongoDb Query:-
db.collection.aggregate([
{
$addFields: {
paramType: "All",
paramSource: "All",
paramStatus: "All",
},
},
{
$match: {
$expr: {
$and: [
{
user: ObjectId("622b55ff0b0af6b049c387d3")
},
{
$or: [
{
$eq: [
"$paramType",
"All"
],
},
{
$eq: [
"$paramType",
"$type"
],
},
],
},
{
$or: [
{
$eq: [
"$paramSource",
"All"
],
},
{
$eq: [
"$paramSource",
"$source"
],
},
],
},
{
$or: [
{
$eq: [
"$paramStatus",
"All"
],
},
{
$eq: [
"$paramStatus",
"$status"
],
},
],
},
],
},
},
},
{
$setWindowFields: {
output: {
totalCount: {
$count: {}
}
}
}
},
{
$sort: {
createdAt: -1
}
},
{
$skip: 0
},
{
$limit: 6
},
{
"$project": {
"paramSource": false,
"paramStatus": false,
"paramType": false,
}
}
])
Query Output:-
[
{
"_id": ObjectId("6257047cffd61ab62864c1ae"),
"source": "B",
"status": "A",
"totalCount": 4,
"type": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
{
"_id": ObjectId("6257047cffd61ab62864c1ad"),
"source": "A",
"status": "A",
"totalCount": 4,
"type": "B",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
{
"_id": ObjectId("6257047cffd61ab62864c1ce"),
"source": "C",
"status": "B",
"totalCount": 4,
"type": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
{
"_id": ObjectId("6257047cffd61ab62864c1cb"),
"source": "B",
"status": "C",
"totalCount": 4,
"type": "A",
"user": ObjectId("622b56250b0af6b049c387d6")
}
]
The query does not work output contains posts created by all users it is not filtering.
The $match part should look like this:
{
$match: {
$and: [
{
user: ObjectId("622b55ff0b0af6b049c387d3")
},
{
$or: [{paramType: {$eq: "All"}},
{$expr: {$eq: ["$paramType", "$type"]}}
]
},
{
$or: [{paramSource: {$eq: "All"}},
{$expr: {$eq: ["$paramSource", "$type"]}}
]
},
{
$or: [{paramStatus: {$eq: "All"}},
{$expr: {$eq: ["$paramStatus", "$type"]}}
]
}
]
}
}
The $expr should only be assigned to cases where both values are in the document. This query returns 3 / 4 documents, the ones in which user: ObjectId("622b55ff0b0af6b049c387d3")
BTW, the last 3 conditions on this $match stage are redundant, as they will always be true, since the query sets them with the value 'All' on the former stage

MongoDB nested aggregation lookup

I wont to connect two collection so as to workouts._id had an assigned value exercise.name.
first collection "workouts":
{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde3"
},
"id_user": {
"$oid": "60fd50ee1e9d064a5cee0bec"
},
"date": {
"$date": "2021-05-20T22:00:00.000Z"
},
"name": "wielki bicek",
"workouts": [{
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"parts": [{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde5"
},
"weight": "30",
"repeating": "10"
}]
}, {
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"parts": [{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde9"
},
"weight": "30",
"repeating": "10"
}, {
"_id": {
"$oid": "60ffbd531b1cba41e8a7fdea"
},
"weight": "30",
"repeating": "10"
}]
}],
"__v": 0
}
second collection "exercises":
{
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"name": "Wyciskanie sztangi",
"image": "uploads\\2021-07-03T15-12-24.173Z-test.gif",
"type": "force",
"groupMuscle": {
"$oid": "60daf099c1aaa20b4cfdf0fa"
},
"instruction": "Opierając tułów znazewnątrz.",
"__v": 0
}
I have one collection that I want to run nested aggregation on this, but I don't know how to run this operation.
$unwind unwind workouts
$lookup lookpup exercises
$addFileds get first item
$addFileds get only name field
$project remove useless field
$group group by original id
db.workouts.aggregate([
{
"$unwind": "$workouts"
},
{
"$lookup": {
"from": "exercises",
pipeline: [
{
"$project": {
name: 1
}
}
],
"localField": "workouts._id",
"foreignField": "_id",
"as": "names"
}
},
{
"$addFields": {
"workouts.i": {
"$first": "$names"
}
}
},
{
"$addFields": {
"workouts.name": "$workouts.i.name"
}
},
{
"$project": {
"names": 0,
"workouts.i": 0
}
},
{
"$group": {
"_id": {
"_id": "$_id",
"id_user": "$id_user",
"date": "$date",
"name": "$name"
},
"workouts": {
$push: "$workouts"
}
}
}
])
mongoplayground

$sum by matching fields in array mongodb

I don't have much experience with mongodb, so the following query is making it difficult for me.
This is the document
[
{
"_id": "31-07-2019",
"date": "31-07-2019",
"grocerie1": [
{
"name": "Flour",
"price": 3.68,
"count": 1
},
{
"name": "Rice",
"price": 3,
"count": 1
},
{
"name": "Rice",
"price": 3,
"count": 1
},
{
"name": "Flour",
"price": 3.68,
"count": 1
}
],
"grocerie2": [
{
"name": "Flour",
"price": 3.68,
"count": 1
}
],
"grocerie1Total": 13.36,
"grocerie2Total": 3.68,
"total": 17.04
},
{
"_id": "09-08-2019",
"date": "09-08-2019",
"grocerie1": [
{
"name": "Rice",
"price": 3,
"count": 1
},
{
"name": "Rice",
"price": 3,
"count": 1
},
{
"name": "Milk",
"price": 5,
"count": 1
}
],
"grocerie2": [
{
"name": "Milk",
"price": 5,
"count": 1
},
{
"name": "Cheese",
"price": 2,
"count": 1
}
],
"grocerie1Total": 11,
"grocerie2Total": 7,
"total": 18
},
{
"_id": "22-08-2019",
"date": "22-08-2019",
"grocerie1": [
{
"name": "Rice",
"price": 3,
"count": 1
},
{
"name": "Cheese",
"price": 2,
"count": 1
},
{
"name": "Cheese",
"price": 2,
"count": 1
},
{
"name": "Rice",
"price": 3,
"count": 1
}
],
"grocerie2": [
{
"name": "Rice",
"price": 3,
"count": 1
},
{
"name": "Rice",
"price": 3,
"count": 1
},
{
"name": "Rice",
"price": 3,
"count": 1
}
],
"grocerie1Total": 10,
"grocerie2Total": 9,
"total": 19
}
]
The document is sorted by date, and contains two grocery stores, each with different products sold. Each product has a name, price and a "count" that I placed to, in the future, obtain the number of times the product was sold through the sum of this field.
Now I want to achieve something like this:
[
{
"_id": "31-07-2019",
"date": "31-07-2019",
"grocerie1": [
{
"name": "Flour",
"total": 7.56,
"count": 2
},
{
"name": "Rice",
"total": 6,
"count": 2
}
],
"grocerie2": [
{
"name": "Flour",
"total": 3.68,
"count": 1
}
],
"grocerie1Total": 13.36,
"grocerie2Total": 3.68,
"total": 17.04
},
{
"_id": "09-08-2019",
"date": "09-08-2019",
"grocerie1": [
{
"name": "Rice",
"total": 6,
"count": 2
},
{
"name": "Milk",
"total": 5,
"count": 1
}
],
"grocerie2": [
{
"name": "Milk",
"total": 5,
"count": 1
},
{
"name": "Cheese",
"total": 2,
"count": 1
}
],
"grocerie1Total": 11,
"grocerie2Total": 7,
"total": 18
},
{
"_id": "22-08-2019",
"date": "22-08-2019",
"grocerie1": [
{
"name": "Rice",
"total": 6,
"count": 2
},
{
"name": "Cheese",
"total": 4,
"count": 2
}
],
"grocerie2": [
{
"name": "Rice",
"total": 9,
"count": 3
}
],
"grocerie1Total": 10,
"grocerie2Total": 9,
"total": 19
}
]
I tried something like this, for example, for "grocerie1",
however, I got disastrous results:
{
$unwind:
{
path: "$grocerie1",
preserveNullAndEmptyArrays: true
}
},
{
"$group": {
"_id": "$grocerie1.name",
"eatHereInfo": {
"$push": {
"name": "$grocerie1.name",
"total": { "$sum": "$grocerie1.price" },
"count": { "$sum": "$grocerie1.count" } }
},
"grocerie2": { "$first": "$grocerie2" },
"date": { "$first": "$date" },
"grocerie1Total": { "$first": "$grocerie1Total" },
"grocerie2Total": { "$first": "$grocerie2Total" },
}
},
Is there any way to achieve it with the aggregation framework? or with javascript? Any help and suggestion are appreciated :)
Note: I assume your objects stored in grocerie collection.
Mongo way (Difficult and rigid)
db.getCollection('grocerie').aggregate([
// ---------------- We start with grocerie1 ------------------
//1. Split grocerie1 array into atomic object
{"$unwind":{ "path": "$grocerie1", "preserveNullAndEmptyArrays": true }},
//2. Group by date + grocerie1 name. If group only by grocerie1.name we may group from other days
// For same grocerie names, we accumulate their name, price, total "grocerie1": { "$push": "$grocerie1" },
{"$group": {
"_id": { "_id": "$_id", "name": "$grocerie1.name" },
"grocerie1": { "$push": "$grocerie1" },
"grocerie2": { "$first": "$grocerie2" },
"date": { "$first": "$date" },
"grocerie1Total": { "$first": "$grocerie1Total" },
"grocerie2Total": { "$first": "$grocerie2Total" }
}
},
//3. Now we have unique date + grocerie1 names + all same items inside grocerie1 array. Split again into atomic value
{"$unwind":{ "path": "$grocerie1", "preserveNullAndEmptyArrays": true }},
//4. We group again date + grocerie1 names, but now we sum price and count
{"$group": {
"_id": { "_id": "$date", "name": "$_id.name" },
"total": { "$sum": "$grocerie1.price" },
"count": { "$sum": "$grocerie1.count" },
"grocerie2": { "$first": "$grocerie2" },
"date": { "$first": "$date" },
"grocerie1Total": { "$first": "$grocerie1Total" },
"grocerie2Total": { "$first": "$grocerie2Total" }
}
},
//5. We group for date and push inside grocerie1 calculated price, total
{"$group":{
"_id": "$_id._id",
"grocerie1": { "$push": {
"name" : "$_id.name",
"total" : "$total",
"count" : "$count"
} },
"grocerie2": { "$first": "$grocerie2" },
"date": { "$first": "$date" },
"grocerie1Total": { "$first": "$grocerie1Total" },
"grocerie2Total": { "$first": "$grocerie2Total" }
}
},
// ---------------- We finished with grocerie1 ---------------
// ---------------- We start with grocerie2 ------------------
//1. Split grocerie2 array into atomic object
{"$unwind":{ "path": "$grocerie2", "preserveNullAndEmptyArrays": true }},
//2. Group by date + grocerie2 name. If group only by grocerie2.name we may group from other days
// For same grocerie names, we accumulate their name, price, total "grocerie2": { "$push": "$grocerie2" },
{"$group": {
"_id": { "_id": "$_id", "name": "$grocerie2.name" },
"grocerie1": { "$first": "$grocerie1" },
"grocerie2": { "$push": "$grocerie2" },
"date": { "$first": "$date" },
"grocerie1Total": { "$first": "$grocerie1Total" },
"grocerie2Total": { "$first": "$grocerie2Total" }
}
},
//3. Now we have unique date + grocerie2 names + all same items inside grocerie2 array. Split again into atomic value
{"$unwind":{ "path": "$grocerie2", "preserveNullAndEmptyArrays": true }},
//4. We group again date + grocerie2 names, but now we sum price and count
{"$group": {
"_id": { "_id": "$date", "name": "$_id.name" },
"total": { "$sum": "$grocerie2.price" },
"count": { "$sum": "$grocerie2.count" },
"grocerie1": { "$first": "$grocerie1" },
"date": { "$first": "$date" },
"grocerie1Total": { "$first": "$grocerie1Total" },
"grocerie2Total": { "$first": "$grocerie2Total" }
}
},
//5. We group for date and push inside grocerie2 calculated price, total
{"$group":{
"_id": "$_id._id",
"grocerie1": { "$first": "$grocerie1" },
"grocerie2": { "$push": {
"name" : "$_id.name",
"total" : "$total",
"count" : "$count"
} },
"date": { "$first": "$date" },
"grocerie1Total": { "$first": "$grocerie1Total" },
"grocerie2Total": { "$first": "$grocerie2Total" },
// Sum total values
"total" : {"$sum":{"$add":["$grocerie1Total", "$grocerie2Total"]}}
}
}
// ---------------- We finished with grocerie2 ---------------
])
Javascript way (Easy and flexible)
/**
* Group groceries with same name and sum fields
*/
function groupGroceries(){
//aux function to group groceries with same name
function _(grocerie){
for(var i=grocerie.length-1; i > -1; i--){
for(var j=0; j<i; j++){
// If grocerie.name already exists, we sum values and remove from array
if(grocerie[j].name == grocerie[i].name){
grocerie[j].price += grocerie[i].price;
grocerie[j].count += grocerie[i].count;
grocerie.splice(i, 1);
break;
}
}
}
//Change price into total
for(var i=0; i<grocerie.length; i++){
//Robo 3T bug: (""+grocerie[i].price).indexOf(".") > -1 ? grocerie[i].price : NumberInt(grocerie[i].price);
grocerie[i].total = grocerie[i].price;
delete grocerie[i].price;
}
}
var result = [];
//Iterate over grocerie collection
db.getCollection('grocerie').find({}).forEach(function(doc){
//Uncomment line below if _id disappears
//doc["_id"];
_(doc.grocerie1);
_(doc.grocerie2);
doc.total = doc.grocerie1Total + doc.grocerie2Total;
result.push(doc);
})
for(var i=0; i<result.length; i++){
print("/* " + (i+1) + " */")
print(result[i])
print("")
}
}
groupGroceries();
==Result==
/* 1 */
{
"_id" : "31-07-2019",
"grocerie1" : [
{
"name" : "Flour",
"total" : 7.36,
"count" : 2
},
{
"name" : "Rice",
"total" : 6,
"count" : 2
}
],
"grocerie2" : [
{
"name" : "Flour",
"total" : 3.68,
"count" : 1
}
],
"date" : "31-07-2019",
"grocerie1Total" : 13.36,
"grocerie2Total" : 3.68,
"total" : 17.04
}
/* 2 */
{
"_id" : "09-08-2019",
"grocerie1" : [
{
"name" : "Rice",
"total" : 6,
"count" : 2
},
{
"name" : "Milk",
"total" : 5,
"count" : 1
}
],
"grocerie2" : [
{
"name" : "Milk",
"total" : 5,
"count" : 1
},
{
"name" : "Cheese",
"total" : 2,
"count" : 1
}
],
"date" : "09-08-2019",
"grocerie1Total" : 11,
"grocerie2Total" : 7,
"total" : 36
}
/* 3 */
{
"_id" : "22-08-2019",
"grocerie1" : [
{
"name" : "Cheese",
"total" : 4,
"count" : 2
},
{
"name" : "Rice",
"total" : 6,
"count" : 2
}
],
"grocerie2" : [
{
"name" : "Rice",
"total" : 9,
"count" : 3
}
],
"date" : "22-08-2019",
"grocerie1Total" : 10,
"grocerie2Total" : 9,
"total" : 19
}

Mongoose Aggregate how to sum 2 objects in 2 arrays in the same collection

Im trying to sum value inside of an array of credits and debts, but i'm getting wrong results(credits: 800 and debits: 8) it's 400 and 4. I dont know what I'm doing wrong.
my collection
{
"_id": "5ca2156960e3fa38a0087cd8",
"name": "Jan",
"data": "2019-01-01T02:00:00.000Z",
"__v": 18,
"credits": [
{
"_id": "5ca24eabbc5e01341c034ecf",
"name": "cred 1",
"valor": 200
},
{
"_id": "5ca24ea9bc5e01341c034ece",
"name": "cred 2",
"valor": 200
},
],
"debits": [
{
"_id": "5ca230cffc553e36ec5841dd",
"name": "debit1",
"value": 2,
"status": "OK"
},
{
"_id": "5ca230cafc553e36ec5841dc",
"name": "debit2",
"value": 2,
"status": "OK"
}] }
Code:
db.collection.aggregate([
{ $unwind: '$credits' },
{ $unwind: '$debits' },
{$project: {
credits: { $sum: '$credits.value' },
debits: { $sum: '$debits.value' }}},
{
$group: {
_id: null,
credits: { $sum: '$credit' },
debits: { $sum: '$debits' }
}},
{$project: {
id: 0,
credits: 1,
debits: 1
}}])

MongoDB aggregate count of items in two arrays across different documents is the same?

Here is my MongoDB collection schema:
company: String
model: String
cons: [String] // array of tags that were marked as "cons"
pros: [String] // array of tags that were marked as "pros"
Here is my query:
[
{ "$project": {
"company": 1,
"model": 1,
"data": {
"$setUnion": [
{ "$map": {
"input": "$pros",
"as": "pro",
"in": {
"type": "$pro",
"value": "$$pro"
}
}},
{ "$map": {
"input": "$cons",
"as": "con",
"in": {
"type": "$con",
"value": "$$con"
}
}}
]
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"company": "$company",
"model": "$model",
"theTag": "$data.value"
},
"sumPros": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "$pro" ] },
1,
0
]
}
},
"sumCons": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "$con" ] },
1,
0
]
}
}
}},
{ "$group": {
"_id": {
"company": "$_id.company",
"model": "$_id.model",
},
"tags": {$push: {
"tag": "$_id.theTag",
"pros": "$sumPros",
"cons": "$sumCons"
}
}}
}]
Here is the output:
{
"_id": {
"company": "Lenovo",
"model": "T400"
},
"tags": [
{
"tag": "Quality",
"pros": 64, // expected value is 54
"cons": 64 // expected value is 10
},
{
"tag": "Value",
"pros": 76, // expected value is 30
"cons": 76 // expected value is 46
}
]
}
...
Notice that pros and cons values are the same. They, for some reason, represent the sum of pros and cons and I can't figure-out why.
What am I doing wrong?
Update:
Here is a document from the collection:
{
"company": "Lenovo",
"model": "X200",
"cons": [
"Quality"
],
"pros": [
"Value",
"Styling"
]
}
As the author of the content you are using in the query and also after asking you to submit some information in the form of data that actually supports the claim in the question here, I have to say that what you are saying is incorrect.
For the record, this is your sample at time of answer:
{
"company": "Lenovo",
"model": "X200",
"cons": [
"Quality"
],
"pros": [
"Value",
"Styling"
]
}
On your sample here, if I run the following query ( and I do extend responsibity for any misleading operations in previous answers and will ammend those immediately ) then the results I see should be what is expected:
db.collection.aggregate([
{ "$project": {
"company": 1,
"model": 1,
"data": {
"$setUnion": [
{ "$map": {
"input": "$cons",
"as": "con",
"in": {
"type": { "$literal": "con" },
"value": "$$con"
}
}},
{ "$map": {
"input": "$pros",
"as": "pro",
"in": {
"type": { "$literal": "pro" },
"value": "$$pro"
}
}}
]
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"company": "$company",
"model": "$model",
"tag": "$data.value"
},
"pros": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "pro" ] },
1,
0
]
}
},
"cons": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "con" ] },
1,
0
]
}
}
}}
])
Which produces from your sample
{
"_id" : {
"company" : "Lenovo",
"model" : "X200",
"tag" : "Quality"
},
"pros" : 0,
"cons" : 1
}
{
"_id" : {
"company" : "Lenovo",
"model" : "X200",
"tag" : "Value"
},
"pros" : 1,
"cons" : 0
}
{
"_id" : {
"company" : "Lenovo",
"model" : "X200",
"tag" : "Styling"
},
"pros" : 1,
"cons" : 0
}
Which clearly correctly allocates both "pros" and "cons" totals across the grouping keys as should be expected.
Therefore what "I see" here is that the values are not in fact "the same" but are actually "different" as matches the different conditions given to each field accumulator.
Therefore taking that further, and based on your original question:
db.collection.aggregate([
{ "$project": {
"company": 1,
"model": 1,
"data": {
"$setUnion": [
{ "$map": {
"input": "$cons",
"as": "con",
"in": {
"type": { "$literal": "con" },
"value": "$$con"
}
}},
{ "$map": {
"input": "$pros",
"as": "pro",
"in": {
"type": { "$literal": "pro" },
"value": "$$pro"
}
}}
]
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"company": "$company",
"model": "$model",
"tag": "$data.value"
},
"pros": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "pro" ] },
1,
0
]
}
},
"cons": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "con" ] },
1,
0
]
}
}
}},
{ "$group": {
"_id": {
"company": "$_id.company",
"model": "$_id.model"
},
"data": { "$push": {
"tag": "$_id.tag",
"pros": "$pros",
"cons": "$cons"
}}
}}
])
Produces:
{
"_id" : {
"company" : "Lenovo",
"model" : "X200"
},
"data" : [
{
"tag" : "Quality",
"pros" : 0,
"cons" : 1
},
{
"tag" : "Value",
"pros" : 1,
"cons" : 0
},
{
"tag" : "Styling",
"pros" : 1,
"cons" : 0
}
]
}
Which is exactly what you are asking for.

Categories