Node.js: Range Query in MongoDB with multiple criteria, using Mongoose - mongodb

I'm trying to implement a "range query" in MongoDB using Mongoose, ordered by a 'criteria' and then by '_id'.
And I would like to return to the client a string containing both cursors.
I was trying to implement something like the code below, with the commented block 2. However, I'm getting an error. Not even the log messages are being printed.
In my test, the query is empty, because the collection is empty.
I suspected that I was not getting the cursor, so I've tested with 'block 1' instead of block 2, and it worked.
But since I need the last cursor, I guess what I really need to use is the .toArray method, right?
What am I doing wrong?
Feed.find({
"criteria": {$lt: cursorCriteria},
"_id": {$lt: cursorId}
})
.sort({
criteria: -1,
_id: -1
})
.limit( 50 )
// block 1: just to test if I'm getting the cursor
.then( items => {
items.forEach( function(item) {
console.log('an item');
})
})
/* block 2: if I try this block instead of block 1, I get an error
.toArray( items => {
if (items.length > 0) {
console.log('not empty);
} else {
console.log('empty');
}
var nextCursor = '${item.criteria}_${item._id}';
res.status(200).json({item, nextCursor});
})
*/

Mongoose doesn't a have toArray() method
This worked fine:
.then( items => {
if (items.length > 0) {
console.log('not empty');
} else {
console.log('empty');
}
var nextCursor;
if (items.length > 0) {
nextCursor = '' + items[items.length-1].criteria + "_" + items[items.length-1]._id;
} else {
nextCursor = '';
}
res.status(200).json({items, nextCursor});
})

Related

Mongoose: MongoError: >1 field while trying to project out $elemMatch

I'm trying to project out only the matched element of an array, in the updated version. But I'm getting the error: "MongoError: >1 field in obj: { _id: 0, lotes.$: 1 }"
If I remove 'new: true', it works. But then I have the doc before the update. And I would really like the updated version.
What's wrong? How can I fix it?
The Offer doc is something like:
{
_id
series: [ Serie ]
}
Serie structure is something like:
{
_id
public.available: Number
public.expDate: Date
}
I'm using Mongoose:
var query = {
'_id': offerId,
'series': {
$elemMatch: {
'_id': serieId,
'public.available': {$gt:0},
'public.expDate': {$gt: now}
}
}
};
var update = {
$inc: { 'series.$.public.available' : -1 }
};
var options = { // project out just the element found, updated
new:true,
select: {
'_id': 0,
'series.$': 1
}
};
Offers.findOneAndUpdate(query, update, options)
.then( element => {
...
}
For anyone else experiencing this error, it is also the most common error when trying to perform an illegal action such as trying to update a database element inside of a findOne request.
Making sure your request is correct, such as findOneAndUpdate should be your first port of call when you get this error.
As Anthony Winzlet pointed out in the links, there seems to be an issue with Mongoose, in which if you use 'new:true', you can't project out the $elemMatch.
So my solution was to keep using 'new:true' only, without projections. And reduce the array later on to get the $elemMatch:
.then( (result) => {
var aux = result.series.reduce((acu, serie, index) => {
if (serie._id == req.params.serieId) return index;
});
var element = result.series[aux];
}

Mongodb query.update method does not update array

Previously I had tried something like (with mongoose and promises):
var cursor = User.find({email: from.address, token: tokenMatches[1]});
and then
return cursor.update(
{'votes.title': b},
{'$set': { 'votes.$.category': a }}
).then(function (result) {
if(result.nModified == 0) {
return cursor.update({}, {'$push': { votes: { category: a, title: b }}}).then(function (res) {
ServerLog('updatePush', res);
return res;
});
}
});
But it always returned nModified = 0 for the first and second call. Until I found out that the cursor object actually has no update function. So why is it so? And why did it not throw an exception?
Model.find returns a Query object, not a cursor. Query does have an update method that lets you execute the query as an update operation.

MongoDB Aggregation add next document (a Meteor App)

Below is my publish function which is pretty much a copy from Average Aggregation Queries in Meteor
What it's used for: display a list of documents. The user can then click on one item (restaurant) and can look at the details. Within the details there are arrows previous and next which would allow to cicle through the list.
My idea was to use that aggregation method to enrich every document with the previous and next document id, that way I could easily build my previous and next links.
But how? I have no idea. Maybe there is a better way?
Meteor.publish("restaurants", function(coords, options) {
if (coords == undefined) {
return false;
}
if (options == undefined) {
options = {};
}
if (options.num == undefined) {
options.num = 4
}
console.log(options)
/**
* the following code is from https://stackoverflow.com/questions/18520567/average-aggregation-queries-in-meteor
* Awesome piece!!!
*
* */
var self = this;
// This works for Meteor 1.0
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
// Your arguments to Mongo's aggregation. Make these however you want.
var pipeline = [
{
$geoNear: {
near: { type: "Point", coordinates: [ coords.lng , coords.lat ] },
distanceField: "dist.calculated",
includeLocs: "dist.location",
num: options.num,
spherical: true
}
}
];
db.collection("restaurants").aggregate(
pipeline,
// Need to wrap the callback so it gets called in a Fiber.
Meteor.bindEnvironment(
function(err, result) {
// Add each of the results to the subscription.
_.each(result, function(e) {
e.dist.calculated = Math.round(e.dist.calculated/3959/0.62137*10)/10;
self.added("restaurants", e._id, e);
});
self.ready();
},
function(error) {
Meteor._debug( "Error doing aggregation: " + error);
}
)
);
});
If you don't have a lot of documents on result, you can do that with JavaScript. Change your subscription code to something like this (I didn't test this code).
_.each(result, function(e, idx) {
if(result[idx - 1]) e.prev = result[idx - 1]._id;
if(result[idx + 1]) e.next = result[idx + 1]._id;
e.dist.calculated = Math.round(e.dist.calculated/3959/0.62137*10)/10;
self.added("restaurants", e._id, e);
});

How can I sort data in descending order using sails-js?

I want my data sort by "createdAt" column . Sometime in ascending order and also descending order also. I am using mongo db .
Currently, I have created app which have feature like Users data can be sorted by admin request. Admin can view data in ascending order or by descending order.
I have tried option by using sort parameter which gives me data every time in same order. How can I change as admin per admin request.
Please review below that I have tried.
var options = {
limit: request.body.limit || undefined,
skip: request.body.skip || undefined,
sort: request.body.sort || "createdAt",
where: request.body.where || undefined
};
Users.find(options, function (err, Users) {
if (Users === undefined) {
return response.json({message: "No data Found"});
}
else if (err) {
return response.json({message: err});
} else {
return response.json({data: Users});
}
});
try this
var myQuery = Users.find(options);
// if you want to sort in descending order
myQuery.sort('createdAt DESC');
// if you want to sort in ascending order
myQuery.sort('createdAt ASC')
myQuery.exec(function callBack(err,results){
console.log(results)
});
You have to do something like this:
.sort({ createdAt: 'desc' })
In your case:
var options = {};
options[request.body.sort || "createdAt"] = 'desc'; // or 'asc'
var options = {
limit: request.body.limit || undefined,
skip: request.body.skip || undefined,
sort: request.body.sort || "createdAt desc", // Please mention here, desc or asc followed by space with column name
where: request.body.where || undefined
};
Users.find(options, function (err, Users) {
if (Users === undefined) {
return response.json({message: "No data Found"});
}
else if (err) {
return response.json({message: err});
} else {
return response.json({data: Users});
}
});
Better to use promises. Keep the code simple and easy to understand.
let condition = {
someField: 'value',
someOtherField: 'value'
}
let limit = 10;
let skip = 10;
Users.find(condition)
.limit(limit)
.skip(skip)
.sort({createdAt: 1})
.then(result => {
// do whatever you need to do with the result here
console.log(result)
})
For descending sort, change the sort clause to
.sort({createdAt: 0})
in my case working code below
var posts =Posts.find();
posts.sort('createdAt DESC');
posts.exec(function(err, success) {
if(err) {
res.status(500);
return res.view('500', {data: err});
}
res.json(success)
})

findAndModify query not executing in callback to aggregation

I have an aggregation query on a students collection that is returning two sets of results
for each student like this
{ _id: 1543,
name: 'Bill Jackson',
scores: { type: 'homework', score: 38.86823689842918 } }
{ _id: 1543,
name: 'Bill Jackson',
scores: { type: 'homework', score: 15.861613903793295 } }
That's working fine. Now in the callback I want to remove one of the scores for each student. I use ugly nested conditionals below to isolate which of the two records I want to remove, and, once that's achieved I create a find and Modify query to remove the doc but there's no evidence of it getting run. Neither the error or success callback to the findAndModify are getting run, however I am able to log that I'm inside the area where the findAndModify is getting called.
Is it possible to query the db in the callback to an aggregation? If not, how should I perform an operation that persists in the db?
//aggregation query ommitted
, function(err, result) { //callbackstarts here with result of aggregation query that returns two records for each student
for (var i=0; i<result.length; i++) {
var id = result[i]['_id'];
if (id === result[i]['_id']){
if (foo && foo === result[i]['_id']){
//if we're in here, we know we need to remove score associated with this result[i]['_id']
//create findAndModify to remove the record
var query = { '_id' : result[i]['_id']}
var sort = []
var operation = { '$pull' : { 'scores.score' : result[i]['scores']['score'] } };
var options = []
console.log('this code is getting called but findAndModify not')
db.collection('students').findAndModify(query, sort, operation, options,function(err, doc) {
if(err) throw err;
if (!doc) {
console.log("record not found");
}
else {
console.log("changed doc" + doc);
}
});
}else {
var foo = result[i]['_id'] //part of logic to isolate which of two records to remove
}