how to change MongoDB output JSON format - node.js

I am new to MongoDb and would appreciate some help with this query. I wrote the following aggregation pipeline
db.collection1.aggregate([
{ "$match": { "type" : "L" }},
{ "$facet": {
"ON": [
{ "$match" : {"lampStatus":'ON'}},
{ "$count": "ON" }
],
"OFF": [
{ "$match" : {"lampStatus": 'OFF'}},
{ "$count": "OFF" }
]
} },
{ "$project": {
"ON": { "$ifNull": [{ "$arrayElemAt": ["$ON.ON", 0] }, 0 ] },
"OFF": { "$ifNull": [{ "$arrayElemAt": ["$OFF.OFF", 0] }, 0 ] },
} }
])
and got output of this type
{
"ON" : 0.0,
"OFF": 30
}
but how to get this type of output with json format
/* 1 */
{
"_id": "ON",
"count" : 0.0
}
/* 2 */
{
"_id": "OFF",
"count" : 30.0
}
any one please suggest me?
Actual Output
{
"ON" : 0.0,
"OFF" : 30
}
Expected output:
{
"_id" : "ON",
"COUNT" : 0.0
}
/* 2 */
{
"_id" : "OFF",
"COUNT" : 30.0
}

If you need some more aggregation trick
db.collection1.aggregate([
{ "$match": { "type" : "L" }},
{ "$facet": {
"ON": [{ "$match" : {"lampStatus": "ON" }}, { "$count": "ON" }],
"OFF": [{ "$match" : {"lampStatus": "OFF" }}, { "$count": "OFF" }]
}},
{ "$project": {
"ON": { "$ifNull": [{ "$arrayElemAt": ["$ON.ON", 0] }, 0 ] },
"OFF": { "$ifNull": [{ "$arrayElemAt": ["$OFF.OFF", 0] }, 0 ] },
}},
{ "$project": {
"data": {
"$map": { "input": { "$objectToArray": "$$ROOT" }, "in": { "_id": "$$this.k", "count": "$$this.v" }}
}
}},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" }}
])

Upodated
db.collection.aggregate([
{ "$match": { "type" : "L" }},
{ "$facet": {
"ON": [
{ "$match" : {"lampStatus":'ON'}},
{ "$count": "ON" }
],
"OFF": [
{ "$match" : {"lampStatus": 'OFF'}},
{ "$count": "OFF" }
]
} },
{"$project": {
myArray: [
{_id:"ON", count: { "$ifNull": [{ "$arrayElemAt": ["$ON.ON", 0] }, 0 ]} },
{_id:"OFF", count: { "$ifNull": [{ "$arrayElemAt": ["$OFF.OFF", 0] }, 0 ] }}
]
}},
{"$unwind":"$myArray"},
{
"$replaceRoot": { newRoot: "$myArray" }
}
])

Related

Mongodb : add new field after $lookup aggregate

i want to add isFavorite as true/false, if user mark that combination as favorite."isFavorite" : 0 means user already marked that item as favorite.my query is
db.getCollection('itemorders').aggregate([
{ "$match": {"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"), 'isCart': 0}},
{ "$lookup": {
"from": "itemlistorders",
"let": { "cartId": { "$toObjectId" : "$_id" }},
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$cartId", "$$cartId"] }}},
],
"as": "itemOrdered"
} },
{$unwind: { path: "$itemOrdered", preserveNullAndEmptyArrays: true }},
{ "$lookup": {
"from": "favoritedrinks",
"let": { "uniqueCartKey": "$itemOrdered.uniqueCartKey" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$uniqueFavKey", "$$uniqueCartKey"] }}},
{ "$addFields": { "isFavorite": true }}
],
"as": "mixerList"
} },
{$unwind: { path: "$itemOrdered", preserveNullAndEmptyArrays: true }},
{
"$group": {
"_id": "$_id",
"grantTotal" : { "$first": "$grantTotal" },
"customerName" : { "$first": "$customerName" },
"barName" : { "$first": "$barName" },
"itemOrdered": { "$addToSet": "$itemOrdered" },
}
},{
$project: {
'_id': 1,
'grantTotal': 1,
'customerName': 1,
'barName': 1,
'itemOrdered': 1,
}
},
]);
My output after excusting this is :
{
"_id" : ObjectId("5e8224768910e83e908d0108"),
"itemOrdered" : [
{
"_id" : ObjectId("5e82257250c82e1abc16d856"),
"barId" : ObjectId("5e55f60eff2f842de4ae6184"),
"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"),
"isCart" : 0,
"isAlcoholActive" : 1,
"alcoholId" : "5e81d13e50c82e1abc0a1ebb",
"alcoholName" : "Irish Coffee",
"cartId" : ObjectId("5e8224768910e83e908d0108"),
"itemTotal" : 250,
"noofDrinks" : 2
},
{
"_id" : ObjectId("5e8224e88910e83e908d0110"),
"isCart" : 0,
"isAlcoholActive" : 1,
"isFavorite" : 0,
"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"),
"barId" : ObjectId("5e55f60eff2f842de4ae6184"),
"noofDrinks" : 1,
"itemTotal" : 200,
"mixerList" : [
{
"isMixerActive" : 0,
"_id" : ObjectId("5e8224e88910e83e908d0111"),
"mixerId" : "5e820b0250c82e1abc12cffc",
"mixerName" : "Pineapple juice"
}
],
"alcoholId" : "5e81d17150c82e1abc0a2607",
"alcoholName" : "Fireball",
"cartId" : ObjectId("5e8224768910e83e908d0108"),
}
]
};
i want to add isFavorite in each subdocument,so that i can identify whether user favorite this item or not.
below query worked for my scenerio.
db.getCollection('itemorders').aggregate([
{ "$match": {"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"), 'isCart': 0}},
{ "$lookup": {
"from": "itemlistorders",
"let": { "cartId": { "$toObjectId" : "$_id" }},
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$cartId", "$$cartId"] }}},
],
"as": "itemOrdered"
} },
{$unwind: { path: "$itemOrdered", preserveNullAndEmptyArrays: true }},
{ "$lookup": {
"from": "favoritedrinks",
"let": { "uniqueCartKey": "$itemOrdered.uniqueCartKey","customerId1": "$itemOrdered.customerId", "modifierfavId": "$itemOrdered._id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$uniqueFavKey", "$$uniqueCartKey"
]
},
{
$eq: [
"$customerId",
"$$customerId1"
]
}
]
}
}
},{ "$addFields": { "isFavorite": 'true' }}
],
"as": "itemListOrdered"
} },
{ "$addFields": { "itemOrdered.isFavorite": { "$gt": ["$itemOrdered.isFavorite", null] } }},
{
"$group": {
"_id": "$_id",
"grantTotal" : { "$first": "$grantTotal" },
"customerName" : { "$first": "$customerName" },
"barName" : { "$first": "$barName" },
"orderDate" : { "$first": "$orderDate" },
"itemOrdered": { "$push": "$itemOrdered" },
}
}
])

How to retun max occurence of value inside mongoDB array

I have an array in mongodb: I want to max occured devDependenciesList value from given array
[{
"_id" : 0,
"repoId" : 460078,
"devDependenciesList" : [
"value1",
"value2",
"value3",
"value4"
]
},{
"_id" : 1,
"repoId" : 1232,
"devDependenciesList" : [
"value1",
"value4",
"value7",
"value93"
]
},{
"_id" : 2,
"repoId" : 5423,
"devDependenciesList" : [
"value1",
"value23",
"value3",
"value4"
]
}]
The output should be :
[value1:3,value4:3,value3:2]
Basically you need to $unwind the array content and then $group on each value as the grouping key with a $sum to count:
db.collection.aggregate([
{ "$unwind": "$devDependenciesList" },
{ "$group": {
"_id": "$devDependenciesList",
"count": { "$sum": 1 }
}}
])
Which would return:
{ "_id" : "value23", "count" : 1 }
{ "_id" : "value93", "count" : 1 }
{ "_id" : "value7", "count" : 1 }
{ "_id" : "value2", "count" : 1 }
{ "_id" : "value3", "count" : 2 }
{ "_id" : "value1", "count" : 3 }
{ "_id" : "value4", "count" : 3 }
That's the basic data right there, but if you really want the "key/count" form you can do:
db.collection.aggregate([
{ "$unwind": "$devDependenciesList" },
{ "$group": {
"_id": "$devDependenciesList",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } },
{ "$group": {
"_id": null,
"items": { "$push": { "k": "$_id", "v": "$count" } }
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$items" }
}}
])
Which would return:
{
"value1" : 3,
"value4" : 3,
"value3" : 2,
"value23" : 1,
"value93" : 1,
"value7" : 1,
"value2" : 1
}
The additional $group and $push are to collect all the results into a single document with an array named with "k" and "v" elements. You want this form for the $arrayToObject operator which is used in the next $replaceRoot stage returning the final output.
You need a MongoDB version which supports those latter operators, but really you don't. This is actually most efficiently done in client code. Such as with JavaScript in the shell:
db.collection.aggregate([
{ "$unwind": "$devDependenciesList" },
{ "$group": {
"_id": "$devDependenciesList",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } }
]).toArray().reduce((o,e) => Object.assign(o, { [e._id]: e.count }),{})
And that produces the same results as above.
And of course if you wanted to exclude all of the singular results or something similar, just add a $match after the $group:
db.collection.aggregate([
{ "$unwind": "$devDependenciesList" },
{ "$group": {
"_id": "$devDependenciesList",
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } },
{ "$sort": { "count": -1 } }
]).toArray().reduce((o,e) => Object.assign(o, { [e._id]: e.count }),{})
Or using the node native driver that would be something like:
let result = (await db.collection('collection').aggregate([
{ "$unwind": "$devDependenciesList" },
{ "$group": {
"_id": "$devDependenciesList",
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } },
{ "$sort": { "count": -1 } }
]).toArray()).reduce((o,{ _id, count }) => ({ ...o, [_id]: count }),{})
Given usage of async/await on the return of an actual array and the use of ES6 features like Object spread and destructuring.
Which of course is just:
{ "value1" : 3, "value4" : 3, "value3" : 2 }
Just for reference, here's a fully reproducible listing:
const { MongoClient } = require('mongodb');
const uri = 'mongodb://localhost:27017';
const opts = { useNewUrlParser: true };
const data = [
{
"_id" : 0,
"repoId" : 460078,
"devDependenciesList" : [
"value1",
"value2",
"value3",
"value4"
]
},{
"_id" : 1,
"repoId" : 1232,
"devDependenciesList" : [
"value1",
"value4",
"value7",
"value93"
]
},{
"_id" : 2,
"repoId" : 5423,
"devDependenciesList" : [
"value1",
"value23",
"value3",
"value4"
]
}
];
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
let client;
try {
client = await MongoClient.connect(uri, opts);
const db = client.db('test');
// Clean data
await db.collection('collection').deleteMany();
// Insert data
await db.collection('collection').insertMany(data);
let result = (await db.collection('collection').aggregate([
{ "$unwind": "$devDependenciesList" },
{ "$group": {
"_id": "$devDependenciesList",
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } },
{ "$sort": { "count": -1 } }
]).toArray()).reduce((o, { _id, count }) => ({ ...o, [_id]: count }),{});
log(result);
let sample = await db.collection('collection').aggregate([
{ "$unwind": "$devDependenciesList" },
{ "$sortByCount": "$devDependenciesList" },
],{ "explain": true }).toArray();
log(sample);
} catch(e) {
console.error(e);
} finally {
if (client)
client.close();
}
})()
And output showing the expected result and the "explain" output to show that $sortByCount is not a "real" aggregation stage and is just a shorter way of typing things that existed way back with MongoDB 2.2:
{
"value1": 3,
"value4": 3,
"value3": 2
}
[
{
"stages": [
{
"$cursor": {
"query": {},
"fields": {
"devDependenciesList": 1,
"_id": 0
},
"queryPlanner": {
"plannerVersion": 1,
"namespace": "test.collection",
"indexFilterSet": false,
"parsedQuery": {},
"winningPlan": {
"stage": "COLLSCAN",
"direction": "forward"
},
"rejectedPlans": []
}
}
},
{
"$unwind": {
"path": "$devDependenciesList"
}
},
{
"$group": {
"_id": "$devDependenciesList",
"count": {
"$sum": {
"$const": 1
}
}
}
},
{
"$sort": {
"sortKey": {
"count": -1
}
}
}
],
"ok": 1,
"operationTime": "6674186995377373190",
"$clusterTime": {
"clusterTime": "6674186995377373190",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
]
Please try to use $sortByCount and $unwind like as below :
db.getCollection("test").aggregate([
{
$unwind: "$devDependenciesList"
},
{
$sortByCount: "$devDependenciesList"
}
]).map((obj)=>{return {[obj._id]:obj.count}})
This is the simple and short solution i could think of.

Use Aggregate with $group in mongodb

I have data in worksheets collection like below:
/* 1 */
{
"_id" : ObjectId("5c21d780f82aa31334ab6506"),
"isBilling" : true,
"hours" : 6,
"userId" : ObjectId("5c1f38a1d7537d1444738493"),
}
/* 2 */
{
"_id" : ObjectId("5c21d780f82aa31334ab6507"),
"isBilling" : true,
"hours" : 4,
"userId" : ObjectId("5c1f38a1d7537d1444738493"),
}
/* 3 */
{
"_id" : ObjectId("5c21e10fae07cc1204a5b647"),
"isBilling" : false,
"hours" : 8,
"userId" : ObjectId("5c1f388fd7537d1444738492"),
}
I have to create a aggregate query to sum the hours, where isBilling equals to true, and where isBilling equals to false.I want the below output:
{
"billingHours":10,
"fixContract":8
}
I have to get data with the particular userId. I tried the below:
Worksheet.aggregate([
{
$match: conditions
},
{
$lookup:{
"from": "worksheets",
"let": {},
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$isBilling",false] } } },
{
"$group": { "_id": null, "totalHours": { "$sum": "$hours" } }
},
],
"as": "billingHours"
}
},
{
"$project":{"billingHours":1}
}
])
I am getting the below result:
[
{
"_id": "5c21d780f82aa31334ab6506",
"billingHours": [
{
"_id": null,
"totalHours": 16
}
]
},
{
"_id": "5c21d780f82aa31334ab6507",
"billingHours": [
{
"_id": null,
"totalHours": 16
}
]
}
]
I don't know why it is giving me 16 hours instead of 10 and giving me two objects instead of 1.
You don't need to use $lookup here. Simple $group with $cond will do the job.
db.collection.aggregate([
{ "$group": {
"_id": null,
"billingHours": {
"$sum": {
"$cond": [{ "$eq": ["$isBilling", true] }, "$hours", 0]
}
},
"fixContract": {
"$sum": {
"$cond": [{ "$eq": ["$isBilling", true] }, 0, "$hours"]
}
}
}}
])

how get count from mongodb with different status from one collection

I have appointment collection in that i have status codes like upcoming, cancelled, completed. i want to write an api to get count of each status using mongoose or mongodb methods.
output should be like below
[{
group : "grp1",
appointments_completed :4
appointments_upcoming :5
appointments_cancelled : 7
}]
thanks in advance.
I hope it help you
db.getCollection('codelist').aggregate([
{
$group:{
_id:{status:"$status"},
count:{$sum:1}
}
}
])
The result will be
[{
"_id" : {
"status" : "cancelled"
},
"count" : 13.0
},
{
"_id" : {
"status" : "completed"
},
"count" : 20.0
}
]
I think you can process it with nodejs
Using Aggregation Pipeline $group we can get this count
db.collection_name.aggregate([
{ $group: {
_id:null,
appointments_completed: {$sum : "$appointments_completed" },
appointments_upcoming:{$sum :"$appointments_upcoming"},
appointments_cancelled:{$sum: "$appointments_cancelled"}
}
}
]);
With MongoDb 3.6 and newer, you can leverage the use of $arrayToObject operator and a $replaceRoot pipeline to get the desired result. You would need to run the following aggregate pipeline:
db.appointments.aggregate([
{ "$group": {
"_id": {
"group": <group_by_field>,
"status": { "$concat": ["appointments_", { "$toLower": "$status" }] }
},
"count": { "$sum": 1 }
} },
{ "$group": {
"_id": "$_id.group",
"counts": {
"$push": {
"k": "$_id.status",
"v": "$count"
}
}
} },
{ "$addFields": {
"counts": {
"$setUnion": [
"$counts", [
{
"k": "group",
"v": "$_id"
}
]
]
}
} },
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$counts" }
} }
])
For older versions, a more generic approach though with a different output format would be to group twice and get the counts as an array of key value objects as in the following:
db.appointments.aggregate([
{ "$group": {
"_id": {
"group": <group_by_field>,
"status": { "$toLower": "$status" }
},
"count": { "$sum": 1 }
} },
{ "$group": {
"_id": "$_id.group",
"counts": {
"$push": {
"status": "$_id.status",
"count": "$count"
}
}
} }
])
which spits out:
{
"_id": "grp1"
"counts":[
{ "status": "completed", "count": 4 },
{ "status": "upcoming", "count": 5 }
{ "status": "cancelled", "count": 7 }
]
}
If the status codes are fixed then the $cond operator in the $group pipeline step can be used effectively to evaluate the counts based on the status field value. Your overall aggregation pipeline can be constructed as follows to produce the result in the desired format:
db.appointments.aggregate([
{ "$group": {
"_id": <group_by_field>,
"appointments_completed": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "completed" ] }, 1, 0 ]
}
},
"appointments_upcoming": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "upcoming" ] }, 1, 0 ]
}
},
"appointments_cancelled": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "cancelled" ] }, 1, 0 ]
}
}
} }
])

$eq not working with $unwind result mongoose

i've simple schema where Events.like store objectId of user. i'd like to match a particular user in array (Events.like) and add a flag isLiked according to match.
Events.aggregate([
{ $unwind: '$like'},
{
"$project":{
// "count" : {$size: "$like"},
"like":1,
"isLiked" :{ "$eq": [ "like", "593564b94642650d5b09f16b" ] },
// "isLiked" : { "$cond": [{ "$eq": [ "like", "593564b94642650d5b09f16b" ] }, true, false ] } }
}
}
], function(err, list) { }
this method always give me false. see results
"data": [
{
"_id": "593647ae9e10082982d3f7a2",
"like": "593563f66d2e9f0b84553fc3",
"isLiked": false
},
{
"_id": "593643a5a1e73a2841d3cddb",
"like": "593564b94642650d5b09f16b",
"isLiked": false
},
{
"_id": "593643a5a1e73a2841d3cddb",
"like": "593563f66d2e9f0b84553fc3",
"isLiked": false
}
]
can anyone tell me where i'm mistaking
########## update ##########
{ $unwind: '$like'},
{
"$project":{
"isLiked" :{ "$eq": [ "$like", mongoose.Types.ObjectId("593564b94642650d5b09f16b") ] },
}
},
{
$group: {
_id: '$_id',
'likes': { $sum: 1},
"like": { $push: '$like' },
"isLiked" : 1
}
}
then
MongoError: the group aggregate field 'isLiked' must be defined as an expression inside an object
i simple want below result
"data": [
{
"_id": "593647ae9e10082982d3f7a2",
"like": ["593563f66d2e9f0b84553fc3","593643a5a1e73a2841d3cddb" ],
"likes":2
"isLiked": true
},
{
"_id": "593643a5a1e73a2841d3cddb",
"like": "[593563f66d2e9f0b84553fc3"],
"likes":1,
"isLiked": false
}
]
Hi the user id you are passing is being passed as a string you will need to convert it to objectId. Try this
EDIT: fixed path of the field as pointed by the asker, #ShivShanker
Events.aggregate([
{ $unwind: '$like'},
{
"$project":{
// "count" : {$size: "$like"},
"like":1,
"isLiked" :{ "$eq": [ "$like", mongoose.Types.ObjectId("593564b94642650d5b09f16b") ] }
}
}
], function(err, list) { }

Resources