Hi I am trying the below query in my nodejs code
const totalCount = await model.countDocuments({
'createdAt': { $gte: new Date(startDate), $lte: new Date(endDate) },
}).exec();
const activeCount = await model.countDocuments({
'createdAt': { $gte: new Date(startDate), $lte: new Date(endDate) },
'enabled': true,
}).exec();
const inactiveCount = (totalCount - activeCount);
return { totalCount, activeCount, inactiveCount };
Is there any way i can combine the above in a single query using aggregate in mongoose? Kindly guide me to the best solution .
Yes, quite simple using some basic operators, like so:
model.aggregate([
{
$match: {
createdAt: {
$gte: new Date(startDate),
$lte: new Date(endDate)
}
}
},
{
$group: {
_id: null,
totalCount: {
$sum: 1
},
activeCount: {
$sum: {
$cond: [
{
$eq: [
"$enabled",
true
]
},
1,
0
]
}
}
}
},
{
$project: {
_id: 0,
totalCount: 1,
activeCount: 1,
inactiveCount: {
$subtract: [
"$totalCount",
"$activeCount"
]
}
}
}
])
Mongo Playground
How can I get the result from mongoose aggregation and display it with ejs?
This is my code:
Record.aggregate(
[
{
$group:
{
_id: { month: { $month: "$date"}, year: { $year: "$date"} },
totalExpense: { $sum: "$amountExpense"},
totalIncome: { $sum: "$amountIncome"},
count: { $sum: 1 }
}
},
{ "$project": {
"total": { "$subtract": [ "$totalIncome", "$totalExpense" ] }
}},
{
$out: "total"
}
]), function(err, results) {
console.log(results);
res.json(results);
}
I am new to nodejs, I am trying to get the all duplicate documents in a collection in mongoDB for that I have tried the following query in mongo shell
db.collection.aggregate([
{
$group: {
_id: {
ProductName: "$ProductName"
},
uniqueIds: {
$addToSet: "$_id"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
$gte: 2
}
}
},
{
$sort: {
count: -1
}
}
])
In mongo shell result:
{
"_id" : {
"ProductName" : "Sony Mobile"
},
"uniqueIds" : [
ObjectId("5728ce42a069270e00e59910"),
ObjectId("5728cde6a069270e00e5990e")
],
"count" : 2
},
{
"_id" : {
"ProductName" : "Nokia Mobile"
},
"uniqueIds" : [
ObjectId("5728ce42a069270e00e59920"),
ObjectId("5728cde6a069270e00e5990f")
],
"count" : 2
}
In mongo shell it gaves the result what i want correctly, but i tried the same query in nodejs server side function like below
Company.aggregate([
{
$group: {
_id: {
Proname: "$Proname"
},
uniqueIds: {
$addToSet: "$_id"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
$gte: 2
}
}
},
{
$sort: {
count: -1
}
}
]).then(function (dupProds) {
console.log("ALL DUPLICATE PRDCTS : " + JSON.stringify(dupProds));
})
};
It shows me an error that Compnay.aggregate(...).then is not a function, I tried in different ways but no use, now how can get the result same as like I got in mongoshell.
Use exec instead of then for mongoose and in callback function parameters error then result.
Company.aggregate([
{
$group: {
_id: {
Proname: "$Proname"
},
uniqueIds: {
$addToSet: "$_id"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
$gte: 2
}
}
},
{
$sort: {
count: -1
}
}
]).exec(function (err,dupProds) {
if(err) {
// return err;
}
console.log("ALL DUPLICATE PRDCTS : ", dupProds);
// return dupProds
})
};
Actually then worked on promise so if you want to use then you need to promising. so can use like
aggregate([{..}]).exec().then(function(result){..})
where aggregate([{..}]).exec() return promise
You need call exec before then:
Company.aggregate(params).exec().then(function (dupProds) {
console.log("ALL DUPLICATE PRDCTS : " + JSON.stringify(dupProds));
})
};
[ http://mongoosejs.com/docs/api.html#aggregate_Aggregate-exec ]
var UserSchema = Schema (
{
android_id: String,
created: {type: Date, default:Date.now},
interests: [{ type: Schema.Types.ObjectId, ref: 'Interests' }],
});
Users.aggregate([
{ $match: {android_id: {$ne: userID}, interests: {$elemMatch: {$in: ids}} }},
{ $group: { _id: { android_id: '$android_id'},count: {$sum: 1}}},
{ $sort: {count: -1}},
{ $limit: 5 }],
I need the to find the top 5 android_ids of the users with the most interests in common with me (ids array). I can work with the array of only matched elements from the interests array too.
You seemed to be going along the right lines here but you do need to consider that arrays have special considerations for comparisons.
Your basic start here is to find all users that are not the current user, and that you also need at least the "interests" array of the current user as well. You seem to be doing that already, but for here let us consider that you have the whole user object for the current user which will be used in the listing.
This makes your "top 5" basically a product of "Not me, and the most interests in common", which means you basically need to count the "overlap" of interests on each user compared to the current user.
This is basically the $setIntersection of the two arrays or "sets" where the elements in common are returned. In order to count how many are in common, there is also the $size operator. So you apply like this:
Users.aggregate(
[
{ "$match": {
"android_id": { "$ne": user.android_id },
"interests": { "$in": user.interests }
}},
{ "$project": {
"android_id": 1,
"interests": 1,
"common": {
"$size": {
"$setIntersection": [ "$interests", user.interests ]
}
}
}},
{ "$sort": { "common": -1 } },
{ "$limit": 5 }
],
function(err,result) {
}
);
The result returned in "common" is the count of common interests between the current user and the user being examined in the data. This data is then processed by $sort in order to put the largest number of common interests on top, and then $limit returns only the top 5.
If for some reason your MongoDB version is presently lower than MongoDB 2.6 where both the $setIntersection and $size operators are introduced, then you can still do this, but it just takes a longer form of processing the arrays.
Mainly you need to $unwind the arrays and process each match individually:
{ "$match": {
"android_id": { "$ne": user.android_id },
"interests": { "$in": user.interests }
}},
{ "$unwind": "$interests" },
{ "$group": {
"_id": "$_id",
"android_id": { "$first": "$android_id" },
"interests": { "$push": "$interests" },
"common": {
"$sum": {
"$add": [
{ "$cond": [{ "$eq": [ "$interests", user.interests[0] ] },1,0 ] },
{ "$cond": [{ "$eq": [ "$interests", user.interests[1] ] },1,0 ] },
{ "$cond": [{ "$eq": [ "$interests", user.interests[2] ] },1,0 ] }
]
}
}
}},
{ "$sort": { "common": -1 }},
{ "$limit": 5 }
Which is more practically coded to generate the condtional matches in the pipeline:
var pipeline = [
{ "$match": {
"android_id": { "$ne": user.android_id },
"interests": { "$in": user.interests }
}},
{ "$unwind": "$interests" }
];
var group =
{ "$group": {
"_id": "$_id",
"android_id": { "$first": "$android_id" },
"interests": { "$push": "$interests" },
"common": {
"$sum": {
"$add": []
}
}
}};
user.interests.forEach(function(interest) {
group.$group.common.$sum.$add.push(
{ "$cond": [{ "$eq": [ "$interests", interest ] }, 1, 0 ] }
);
});
pipeline.push(group);
pipeline = pipeline.concat([
{ "$sort": { "common": -1 }},
{ "$limit": 5 }
])
User.aggregate(pipeline,function(err,result) {
});
The key elements there being that "both" the current user and the user being inspected have their "interests" separated out for comparison to see if they are "equal". The result from $cond attributes a 1 where this is true or 0 where false.
Any returns ( and only ever expected to be 1 at best, per pair ) are passed to the $sum accumulator which counts the matches in common. You can alternately $match with an $in condition again:
{ "$unwind": "$interests" },
{ "$match": { "interests": { "$in": user.interests } },
{ "$group": {
"_id": "$_id",
"android_id": { "$first": "$android_id" },
"common": { "$sum": 1 }
}}
But this is naturally destructive of the array content as non matches are filtered out. So it depends on what you would rather have in the response.
That is the basic process for getting the "common" counts for use in further processing like $sort and $limit in order to get your "top 5".
Just for fun, here is a basic node.js listing to show the effects of common matches:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/sample');
var interestSchema = new Schema({
name: String
});
var userSchema = new Schema({
name: String,
interests: [{ type: Schema.Types.ObjectId, ref: 'Interest' }]
});
var Interest = mongoose.model( 'Interest', interestSchema );
var User = mongoose.model( 'User', userSchema );
var interestHash = {};
async.series(
[
function(callback) {
async.each([Interest,User],function(model,callback) {
model.remove({},callback);
},callback);
},
function(callback) {
async.each(
[
"Tennis",
"Football",
"Gaming",
"Cooking",
"Yoga"
],
function(interest,callback) {
Interest.create({ name: interest},function(err,obj) {
if (err) callback(err);
interestHash[obj.name] = obj._id;
callback();
});
},
callback
);
},
function(callback) {
async.each(
[
{ name: "Bob", interests: ["Tennis","Football","Gaming"] },
{ name: "Tom", interests: ["Football","Cooking","Yoga"] },
{ name: "Sue", interests: ["Tennis","Gaming","Yoga","Cooking"] }
],
function(data,callback) {
data.interests = data.interests.map(function(interest) {
return interestHash[interest];
});
User.create(data,function(err,user) {
//console.log(user);
callback(err);
})
},
callback
);
},
function(callback) {
async.waterfall(
[
function(callback) {
User.findOne({ name: "Bob" },callback);
},
function(user,callback) {
console.log(user);
User.aggregate(
[
{ "$match": {
"_id": { "$ne": user._id },
"interests": { "$in": user.interests }
}},
{ "$project": {
"name": 1,
"interests": 1,
"common": {
"$size": {
"$setIntersection": [ "$interests", user.interests ]
}
}
}},
{ "$sort": { "common": -1 } }
],
function(err,result) {
if (err) callback(err);
Interest.populate(result,'interests',function(err,result) {
console.log(result);
callback(err);
});
}
);
}
],
callback
);
}
],
function(err) {
if (err) throw err;
//console.dir(interestHash);
mongoose.disconnect();
}
);
Which will output:
{ _id: 55dbd7be0e5516ac16ea62d1,
name: 'Bob',
__v: 0,
interests:
[ 55dbd7be0e5516ac16ea62cc,
55dbd7be0e5516ac16ea62cd,
55dbd7be0e5516ac16ea62ce ] }
[ { _id: 55dbd7be0e5516ac16ea62d3,
name: 'Sue',
interests:
[ { _id: 55dbd7be0e5516ac16ea62cc, name: 'Tennis', __v: 0 },
{ _id: 55dbd7be0e5516ac16ea62ce, name: 'Gaming', __v: 0 },
{ _id: 55dbd7be0e5516ac16ea62d0, name: 'Yoga', __v: 0 },
{ _id: 55dbd7be0e5516ac16ea62cf, name: 'Cooking', __v: 0 } ],
common: 2 },
{ _id: 55dbd7be0e5516ac16ea62d2,
name: 'Tom',
interests:
[ { _id: 55dbd7be0e5516ac16ea62cd, name: 'Football', __v: 0 },
{ _id: 55dbd7be0e5516ac16ea62cf, name: 'Cooking', __v: 0 },
{ _id: 55dbd7be0e5516ac16ea62d0, name: 'Yoga', __v: 0 } ],
common: 1 } ]
I'm trying to remove the _Id from the returned documents, this is my code:
module.exports = function(app) {
// Module dependencies.
var mongoose = require('mongoose'),
Contacts = mongoose.models.Contacts,
api = {},
limit = 10;
api.contacts = function(req, res) {
Contacts.aggregate([{
$group: {
"_id": {
name: "$name",
city: "$city",
state: "$state"
}
}
}, {
$sort: {
AgencyTranslation: 1
}
}, {
$limit: req.query.limit | limit
}],
function(err, contacts) {
if (err) {
res.json(500, err);
} else {
res.json({
contacts: contacts
})
}
})
};
app.get('/api/contacts', api.contacts);
};
the current result-set looks like this:
{
"contacts":[
{"_id":{"name":"Joe","city":"ankorage","state":"AL"}},
{"_id":{"name":"Mark","city":"washington","state":"DC"}}
...
]
}
I tried to replace "_Id" with "$project", or $project, and adding "_Id": 0 to the object, as some have suggested elsewhere, but was not successful.
I also tried res.send(contacts), but that only stripped the super-object ('contacts').
Any suggestions are appreciated.
Like this
Contacts.aggregate( [
{ $group: { "_id": { name: "$name", city: "$city", state: "$state" } } },
{ $project: {_id: 0, name: '$_id.name', city: '$_id.city', state: '$_id.state'} },
{ $sort: { AgencyTranslation: 1 } },
{ $limit: req.query.limit | limit }
], function () {
});
Bunch of time but, here is the answer:
After making $group or $project, do this:
{ $unset: ["_id"] }