Nodejs + Mongodb: find data after aggregation - node.js

I'm a new to Nodejs and MongoDB.
Here is a sample of my dataset:
{
'name': ABC,
'age':24,
'gender':male,
...
}
Generally speaking, what I want to do is to aggregate data before using them to find different data clusters.
To be specific, I want to know how many people there are at different ages. Then, to find people(documents) at each age and store them.
Here is my code:
MongoClient.connect(url, function(err, db) {
if(err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
db.collection('test').aggregate(
[
{ $group: { _id: "$age" , total: { $sum: 1 } } },
{ $sort: { total: -1 } }
]).toArray(function(err, result) {
assert.equal(err, null);
age = [];
for(var i in result) {
age.push(result[i]['_id'])
};
ageNodes = {};
for(var i in age) {
nodes = [];
var cursor = db.collection('test').find({'age':age[i]});
// query based on aggregated data
cursor.each(function(err,doc){
if(doc!=null){
nodes.push(doc);
} else {
console.log(age[i]);
ageNodes[age[i]] = nodes;
}
})
}
res.json(ageNodes);
});
};
});
My expected JSON format:
{
age:[different documents]
}
example:
{
20:[{name:A,gender:male,...},{},...],
30:[{name:B,gender:male,...},{},...],
...
}
However, what I got was an empty result, so I think maybe it was caused by the for loop.
I have no idea how to handle the asynchronous callback.

You only need to run the following pipeline which uses $push to add the root document (represented by $$ROOT system variable in the pipeline) to an array per age group:
Using MongoDB 3.4.4 and newer:
MongoClient.connect(url, function(err, db) {
if(err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
db.collection('test').aggregate([
{ '$group': {
'_id': '$age',
'total': { '$sum': 1 },
'docs': { '$push': '$$ROOT' }
} },
{ '$sort': { 'total': -1 } },
{ '$group': {
'_id': null,
'data': {
'$push': {
'k': '$_id',
'v': '$docs'
}
}
} },
{ '$replaceRoot': {
'newRoot': { '$arrayToObject': '$data' }
} }
]).toArray(function(err, results) {
console.log(results);
res.json(results);
});
};
});
Using MongoDB 3.2 and below:
MongoClient.connect(url, function(err, db) {
if(err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
db.collection('test').aggregate([
{ '$group': {
'_id': '$age',
'total': { '$sum': 1 },
'docs': { '$push': '$$ROOT' }
} },
{ '$sort': { 'total': -1 } }
]).toArray(function(err, results) {
console.log(results);
var ageNodes = results.reduce(function(obj, doc) {
obj[doc._id] = doc.docs
return obj;
}, {});
console.log(ageNodes);
res.json(ageNodes);
});
};
});

Related

How to project a new boolean field in Mongoose if another property is listed in existing array?

Consider the query in Mongoose :
let StudentCodes = .... // getting this from somewhere
await Students.aggregate(
[
{
$project: {
StudentCODE: "$StudentCODE",
StudName: "$StudName",
StudProfileDesc: "$StudProfileDesc",
IsReviewed: {
$cond: [{ $eq: [StudentCodes, "$StudentCODE"] }, 1, 0]
}
}
}
],
function(err, results) {
if (err) {
console.log(err);
}
console.log(results);
return res.status(200).json(results);
}
);
How can We project IsReviewed as true or false if the property StudentCODE exists in the array StudentCodes ?
Try as below, you can use $in in $cond to do that :
let StudentCodes = .... // getting this from somewhere
await Students.aggregate(
[
{
$project: {
StudentCODE: "$StudentCODE",
StudName: "$StudName",
StudProfileDesc: "$StudProfileDesc",
IsReviewed: {
$cond: [{ $in: ["$StudentCODE", StudentCodes] }, true, false]
}
}
}
],
function (err, results) {
if (err) {
console.log(err);
}
console.log(results);
return res.status(200).json(results);
}
);

Conditional $sum query in mongoDB

I want to get sum of filed reqAmount in my mongodb database of a particular user.
Here is the schema
const userid = req.body.userId;
const id = mongoose.Types.ObjectId(userid);
fundReq.aggregate([
{ $match : { userId : id } },
{
$group: {
_id : '',
total: {$sum : "$reqAmount"}
}
}
],function (err, result) {
if (err) throw err;
else res.json(result);
})
but getting null in result...
it's Working Fine For You
const ObjectId = require('mongodb').ObjectId;
function fnGetTotalCollectionAmount(callback) {
TransactionModel.aggregate([
{
$match: { '_id': ObjectId(productId) }
},
{
$group: {
_id: null,
grandTotal: {
$sum: '$subTotal'
}
}
}
]).exec(function (err, transaction) {
if (err) {
return callback(err);
} else {
return callback(transaction[0].grandTotal);
}
});
}

UnExpected output from mongoDB

I am using mongoose to connect and query mongoDB.
Now if I have the following code:
return new Promise((resolve) => {
mongoose.connection.db.collection('offer').aggregate([{
$match: {
$or: [
{
ccChecked: {
$lt: new Date(currentDay + 'T00:00:00.000Z')
}
},
{
ccChecked: {
$exists: 0
}
}
],
_id: { $in: offerObjIds }
}
},
{ $sample: { size: 2 } }
], (err, res) => {
console.log('res is', res);
resolve(res);
});
});
The result provided is fine and we get the expected output.
But if we have the following query, and I provide SAMPLE_SIZE as 2:
const sampleSize = process.env.SAMPLE_SIZE
return new Promise((resolve) => {
mongoose.connection.db.collection('offer').aggregate([{
$match: {
$or: [
{
ccChecked: {
$lt: new Date(currentDay + 'T00:00:00.000Z')
}
},
{
ccChecked: {
$exists: 0
}
}
],
_id: { $in: offerObjIds }
}
},
{ $sample: { size: sampleSize } }
], (err, res) => {
console.log('res is', res);
resolve(res);
});
});
In this case, I get the result as undefined. Can someone provide explanation why such behaviour and how to resolve this through process.env variable.
Verify that sampleSize is an integer. It could be string coming from process.env variables.
{ $sample: { size: <positive integer> } }
Update:
I tested out a script on my command line. See the results:
set SAMPLE_SIZE=1&& node example.js
example.js
console.log(process.env);
Output:
{ ... SAMPLE_SIZE: '1', ... }

Update many documents in mongoDB with different values

I am trying to update two documents in mongoDB, with two different values. I made it with two different callbacks, but is it possible to do it with only one request?
My solution:
mongo.financeCollection.update(
{ 'reference': 10 },
{ $push:
{ history: history1 }
}, function (err){
if (err){
callback (err);
}
else {
mongo.financeCollection.update(
{ 'reference': 20 },
{ $push:
{ history: history2 }
}, function (err){
if (err){
callback(err);
}
else {
callback(null);
}
});
}
});
Sorry if it is a stupid question but I just want to optimize my code!
Best to do this update using the bulkWrite API. Consider the following example for the above two documents:
var bulkUpdateOps = [
{
"updateOne": {
"filter": { "reference": 10 },
"update": { "$push": { "history": history1 } }
}
},
{
"updateOne": {
"filter": { "reference": 20 },
"update": { "$push": { "history": history2 } }
}
}
];
mongo.financeCollection.bulkWrite(bulkUpdateOps,
{"ordered": true, "w": 1}, function(err, result) {
// do something with result
callback(err);
}
The {"ordered": true, "w": 1} ensures that the documents will be updated on the server serially, in the order provided and thus if an error occurs all remaining updates are aborted. The {"w": 1} option determines the write concern with 1 being a request acknowledgement that the write operation has propagated to the standalone mongod or the primary in a replica set.
For MongoDB >= 2.6 and <= 3.0, use the Bulk Opeartions API as follows:
var bulkUpdateOps = mongo.financeCollection.initializeOrderedBulkOp();
bulkUpdateOps
.find({ "reference": 10 })
.updateOne({
"$push": { "history": history1 }
});
bulkUpdateOps
.find({ "reference": 20 })
.updateOne({
"$push": { "history": history2 }
});
bulk.execute(function(err, result){
bulkUpdateOps = mongo.financeCollection.initializeOrderedBulkOp();
// do something with result
callback(err);
});

What am I doing wrong trying to make this run with meteor?

I am trying to adapt the full text search made here to work with meteor. I exported the mongodb url to one running 2.6.1. to make full text search compatible but I am getting these errors server/search.js:2:15: Unexpected token .andserver/search.js:42:7: Unexpected token ). What am I missing?
server.js
Meteor.methods({
Meteor.ensureIndex("Posts", {
smaintext: "text"
}, function(err, indexname) {
assert.equal(null, err);
});
)
};
Meteor.methods({
feedupdate: function(req) {
Posts.find({
"$text": {
"$search": req
}
}, {
smaintext: 1,
submitted: 1,
_id: 1,
Posts: {
$meta: "Posts"
}
}, {
sort: {
textScore: {
$meta: "posts"
}
}
}).toArray(function(err, items) {
for (e=0;e<101;e++) {
Meteor.users.update({
"_id": this.userId
}, {
"$addToSet": {
"profile.search": item[e]._id
}
});
}
})
}
)
};
This is wrong definition of method
Meteor.methods({
Meteor.ensureIndex("Posts", {
smaintext: "text"
}, function(err, indexname) {
assert.equal(null, err);
});
)
};
you must specify a method name ( http://docs.meteor.com/#/basic/Meteor-methods )
So it will be something like this
Meteor.methods({
myMethodName : function() { Meteor.ensureIndex("Posts", {
smaintext: "text"
}, function(err, indexname) {
assert.equal(null, err);
});
}
});
in second method there is a semicron and parenthise problem.
Correct version is
Meteor.methods({
feedupdate: function(req) {
Posts.find({
"$text": {
"$search": req
}
}, {
smaintext: 1,
submitted: 1,
_id: 1,
Posts: {
$meta: "Posts"
}
}, {
sort: {
textScore: {
$meta: "posts"
}
}
}).toArray(function(err, items) {
for (e=0;e<101;e++) {
Meteor.users.update({
"_id": this.userId
}, {
"$addToSet": {
"profile.search": item[e]._id
}
});
}
});
}
});

Resources