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', ... }
Related
I want to stream a MongoDB query in NodeJS because, in Angular, i have a chart and i want to update the data without making a refresh.
So far, this is my code (NodeJS):
exports.gettoday = function (request, response) {
db.collection("nifi5", function (err, collection) {
collection
.aggregate([
{
$group: {
_id: {
minute: {
$minute: { $dateFromString: { dateString: "$created_at" } },
},
hour: {
$hour: { $dateFromString: { dateString: "$created_at" } },
},
day: {
$dayOfMonth: { $dateFromString: { dateString: "$created_at" } },
},
month: {
$month: { $dateFromString: { dateString: "$created_at" } },
},
year: {
$year: { $dateFromString: { dateString: "$created_at" } },
},
},
avg: { $avg: "$value_temperature" },
},
},
])
.stream()
.toArray(function (err, items) {
if (err) {
response.statusMessage = {
devMessage: err,
clientMessage: "Unexpected error, try again latter",
};
response.send(response.statusMessage).end();
}
let tarray = { labels: [], data: [] };
items.forEach((element) => {
element.date = element._id.hour + ":" + element._id.minute;
element.avg = Math.round(element.avg * 100) / 100;
});
items.sort(function (a, b) {
return a.date < b.date ? -1 : 1;
});
items.forEach((element) => {
tarray.labels.push(element.date);
tarray.data.push(element.avg);
});
return response.json(tarray);
});
});
};
Angular:
gettodaydata(): Observable<any> {
return this.http.get(`${environment.baseURL}gettoday`).subscribe((data) => {
this.data_line.push(...data.data);
this.labels_line.push(...data.labels);
this.isLineChartLoading = false;
});
}
Can u please help me streaming this controller and fetch the data in Angular to update my chart when new data comes do my database ?
Thank you all!
You should give a try to socket.io, it creates a persistent connection and responds to events in real time.
I know there are multiple posts handling similar types of issue but none of them seems to work for me.
In my application, I need to fetch the graphical data for the vertical bar chart from my database. The filtration is based on the two status types and the updatedAt field. The data will be plotted for each month of the year.
I tried two approaches to the same:
First:
exports.leads_based_on_status = async (req, res) => {
const { userId } = req.user;
const fetchMonths = getMonths();
try {
const fetch_leads_new = await fetchMonths.map(async (month) => {
return Lead.aggregate([
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
$and: [
{ updatedAt: { $gt: month._start, $lt: month._end } },
{ "leads.status": "New" },
],
},
},
]);
});
const fetch_leads_pending = await fetchMonths.map(async (month) => {
return Lead.aggregate([
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
$and: [
{ updatedAt: { $gt: month._start, $lt: month._end } },
{ "leads.status": "Pending" },
],
},
},
]);
});
Promise.all([fetch_leads_new, fetch_leads_pending]).then(
(resultnew, resultpending) => {
console.log("show result new", resultnew);
console.log("show result pending", resultpending);
//both these results in Promise <pending>
}
);
const leads_status_statics = [
{
New: fetch_leads_new,
},
{
Pending: fetch_leads_pending,
},
];
res.status(200).json({ message: "Graphical Data", leads_status_statics });
} catch (error) {
console.log(error) || res.status(500).json({ error });
}
};
Second:
exports.leads_based_on_status = async (req, res) => {
const { userId } = req.user;
const fetchMonths = getMonths();
try {
fetchMonths.map(async (month) => {
const fetch_leads_new = await Lead.aggregate([
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
$and: [
{ updatedAt: { $gt: month._start, $lt: month._end } },
{ "leads.status": "New" },
],
},
},
]);
const fetch_leads_pending = await Lead.aggregate([
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
$and: [
{ updatedAt: { $gt: month._start, $lt: month._end } },
{ "leads.status": "New" },
],
},
},
]);
const leads_status_statics = [
{
New: fetch_leads_new,
},
{
Pending: fetch_leads_pending,
},
];
res.status(200).json({ message: "Graphical Data", leads_status_statics });
//de:16484) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
});
} catch (error) {
console.log(error) || res.status(500).json({ error });
}
};
But none of them is able to help me resolve my issue. The first approach keeps returning Promise <Pending>, while the second approach returns Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:467:11)
Any help to rectify the problem is appreciated :)
Regarding your first approach, The Promise.all(iterable) method takes an iterable as input. In your case, fetch_leads_new and fetch_leads_pending is already returning an array of pending Promise, something like
: [ Promise { <pending> }, Promise { <pending> } ].
So currently you are passing an array with arrays or pending promise(Promise.all([fetch_leads_new, fetch_leads_pending])) to the Promise.all function, something like
Promise.all([[ Promise { <pending> }, Promise { <pending> } ], [ Promise { <pending> }, Promise { <pending> } ]])
So I think you can consider having two Promise.all methods with await one for fetch_leads_new other for fetch_leads_pending
const newRecords = await Promise.all(fetch_leads_new);
const pendingRecords = await Promise.all(fetch_leads_pending);
const leads_status_statics = [
{
New: newRecords,
},
{
Pending: pendingRecords,
},
];
Regarding the second approach
When the fetchMonths has more that one entry that means res.status(200).json(... is also called more than once(In each iteration of map function) and that why you are getting Cannot set headers after they are sent to the client error
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);
}
);
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);
});
};
});
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
}
});
}
});
}
});