Mongoose query where with missing (or undefined) fields - node.js

I have an issue that I have a messy MongoDB database with a revised Mongoose scheme.
I want to be able to query using the where command, but if the field is missing or undefined in any found record then Mongoose returns an error.
For instance:
return this.find({personID: req.user._id})
.where('rangeBefore').lt(time);
This returns an error because some records do not have rangeBefore.
What I want to achieve is that any record without the rangeBefore field is filtered out (i.e. it fails the where test).
I have tried prefixing with exists like this:
return this.find({personID: req.user._id})
.exists('rangeBefore').where('rangeBefore').lt(time);
but I still get an error.
Can anyone suggest a way to ignore records with undefined fields, rather than returning an error?

If you just want to modify your existing query:
return this.find({ personID: req.user._id,
rangeBefore: { $exists: true, $ne: null } //also check for nulls
})
.where('rangeBefore').lt(time);
Or you can use the callback approach too if it fits:
this.find({personID: req.user._id,
rangeBefore: { $exists: true, $ne: null, $lt: time }
})
.exec(function(err, record) { });

You can do it like this, which returns a promise:
find({rangeBefore: { $exists: 1, $lt: time } }).exec()
Or use a callback:
find({rangeBefore: { $exists: 1, $lt: time } }).exec(function(err, record){
})

Your error message of:
Cast to number failed for value "undefined" at path "rangeBefore"
means that your time variable is undefined. A query like this will naturally ignore docs where rangeBefore doesn't exist.
So the problem is with your time variable, not your docs or query.

Related

Cannot read property 'columnName' of undefined in sails Js Mongo DB

I am using sails JS with Mongo DB.
My model is:
module.exports = {
attributes: {
title:{type:"string",required:true},
content:{type:"string",required:true},
date:{type:"string",required:true},
filename:{type:"string",required:true},
},
};
My Controller is:
fetchposts:function(req,res){
console.log("in fetch posts")
mysort={$id:-1}
Cyberblog.find().sort(mysort).limit(5).exec(function(err, result) {
if (err || !result) {
message="no records fetched";
console.log(message);
res.redirect('/newpost');
}
else{
console.log(result)
}
I am facing an error saying that
"Warning: The sort clause in the provided criteria is specified as a dictionary (plain JS object),
meaning that it is presumably using Mongo-Esque semantics (something like { fullName: -1, rank: 1 }).
But as of Sails v1/Waterline 0.13, this is no longer the recommended usage. Instead, please use either
a string like 'fullName DESC', or an array-like [ { fullName: 'DESC' } ].
(Since I get what you mean, tolerating & remapping this usage for now...)
and I am unable to fetch any records. It is showing no records fetched.
So I have one warning on Sort and no records coming from DB. Please help me resolve the issue.
Sort clause allow send string:
var users = await User.find({ name: 'Jake'})
.sort('age ASC');
return res.json(users);
Or an array:
var users = await User.find({ name: 'Finn'})
.sort([
{ age: 'ASC' },
{ createdAt: 'ASC' },
]);
return res.json(users);
Check this out in the documentation:
https://sailsjs.com/documentation/reference/waterline-orm/queries/sort

Mongoose error Unsupported projection option: sort: { createdAt: -1 }

I'm trying to sort my mongoose entries by date, most recent first in node.js express with mongoose.
the mongoose query is:
FecalRequest.findOne({uid: req.user.id}, {sort:{'createdAt':-1}}, (err, fecalkits) => {
console.log(fecalkits);
done(err, respiratorykits, fecalkits)
});
I've tried both created_at and createdAt. The error is
eMongoError: Unsupported projection option: sort: { createdAt: -1 }
For reference, the FecalRequest model looks like:
const fecalSchema = new mongoose.Schema(
{
uid: String,
email: String,
status: {type: String, default: 'No Request' },
},
{ timestamps: true });
I just want to return the most recently made entry for a particular user.
Include an empty object as a second (projection) parameter so that your third parameter to findOne is properly interpreted as query options:
FecalRequest.findOne({uid: req.user.id}, {}, {sort:{'createdAt':-1}}, (err, fecalkits) => {
console.log(fecalkits);
done(err, respiratorykits, fecalkits)
});
Definitely the most times I've used the word "fecal" in an answer.
You wouldn't use findOne for this. You'd use find() with sort() and limit().
findOne is just going to return the first document found that matches your search criteria.
In recent versions of Mongoose, you cannot use sort with findOne. (See the query options doc.)
Your query would become:
FecalRequest.find({uid: req.user.id}, {}, {sort: {'createdAt':-1}, limit: 1}, (err, fecalkits) => {
console.log(fecalkits);
done(err, respiratorykits, fecalkits);
});
And keep in mind this returns a list.
I don't think its possible to sort if you're finding just one document.
I believe that "Find" is the function you should be using?

How to properly do a Bulk upsert/update in MongoDB

I'm trying to:
Find a document according to a search criteria,
If found, update some attributes
If not insert a document with some attributes.
I'm using a Bulk.unOrderedOperation as I'm also performing a single insert. And I want to do everything in one operation againast DB.
However something it's causing nothing is being inserted for the update/upsert operation.
This is the insert document:
var lineUpPointsRoundRecord = {
lineupId: lineup.id, // String
totalPoints: roundPoints, // Number
teamId: lineup.team, // String
teamName: home.team.name, // String
userId: home.iduser, // String
userName: home.user.name, // String
round: lineup.matchDate.round, // Number
date: new Date()
}
This is the upsert document:
var lineUpPointsGeneralRecord = {
teamId: lineup.team, // String
teamName: home.team.name, // String
userId: home.iduser, // String
userName: home.user.name, // String
round: 0,
signupPoints: home.signupPoints, // String
lfPoints: roundPoints+home.signupPoints, // Number
roundPoints: [roundPoints] // Number
};
This is how I'm trying to upsert/update:
var batch = collection.initializeUnorderedBulkOp();
batch.insert(lineUpPointsRoundRecord);
batch.find({team: lineUpPointsRoundRecord.teamId, round: 0}).
upsert().
update({
$setOnInsert: lineUpPointsGeneralRecord,
$inc: {lfPoints: roundPoints},
$push: {roundPoints: roundPoints}
});
batch.execute(function (err, result) {
return cb(err,result);
});
Why wouldn't it be upserting/updating?
Note
That is JS code using waterline ORM which also uses mongodb native driver.
Your syntax here is basically correct, but your general execution was wrong and you should have "seperated" the "upsert" action from the other modifications. These will otherwise "clash" and produce an error when an "upsert" occurs:
LineupPointsRecord.native(function (err,collection) {
var bulk = collection.initializeOrderedBulkOp();
// Match and update only. Do not attempt upsert
bulk.find({
"teamId": lineUpPointsGeneralRecord.teamId,
"round": 0
}).updateOne({
"$inc": { "lfPoints": roundPoints },
"$push": { "roundPoints": roundPoints }
});
// Attempt upsert with $setOnInsert only
bulk.find({
"teamId": lineUpPointsGeneralRecord.teamId,
"round": 0
}).upsert().updateOne({
"$setOnInsert": lineUpPointsGeneralRecord
});
bulk.execute(function (err,updateResult) {
sails.log.debug(err,updateResult);
});
});
Make sure your sails-mongo is a latest version supporting the Bulk operations properly be the inclusion of a recent node native driver. The most recent supports the v2 driver, which is fine for this.
I recommend use bulkWrite exemplary code with bulk upsert of many documents:
In this case you will create documents with unique md5. If document exists then will be updated but no new document is created like in classical insertMany.
const collection = context.services.get("mongodb-atlas").db("master").collection("fb_posts");
return collection.bulkWrite(
posts.map(p => {
return { updateOne:
{
filter: { md5: p.md5 },
update: {$set: p},
upsert : true
}
}
}
),
{ ordered : false }
);
https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/
Typically I have always set upsert as a property on update. Also update should be able to find the record itself so no need to find it individually.
Depending on the environment the $ may or may not be necessary.
batch.update(
{team: lineUpPointsRoundRecord.teamId, round: 0},
{
$setOnInsert: lineUpPointsGeneralRecord,
$inc: {lfPoints: roundPoints},
$push: {roundPoints: roundPoints},
$upsert: true
});

Keeping track of WHERE clause value that don't yield result

I have a function that checks whether a given email address exists in a "user" table. I use sequelize's findone method to do that. This method is simple enough for finding particular value in the table. But, what I like to do is to keep track of those email addresses that aren't in that table. I'm not sure if there is any event that I can intercept for that matter. But my google attempts didn't yield any solution. So any help is appreciated. This is my code
function IsUserExist(email, callback)
{
var userModel = sequelize.define('user', {
email_address: Sequelize.STRING(255)
},
{ timestamps: false,
freezeTableName: true
});
userModel.findOne({
where: { email_address: emailAddress }
})
.then(function(users){
callback();
})
.catch(function(err){
console.log("Error while accessing user table");
})
.done();
}
So, I finally figure out how to do it. Basically, the parameter of "then" method is null if findOne does not yield any result. So, in my case "users" is null if a query does not return any result. If it does, "users" contains a record.

Query mongo document based on its subdocument using $exists

I am trying to fetch records from mongo using mongoose.
I have document called projects that has sub-document work. work has user, to and from properties. I want to fetch the document that has specific user, and doesn't have to value set.
So far i tried:
models.project.findOne {'work.user': user,'work.to':{$exists: false}},
(err, data) -> # data is always null, no error.
It seems that if just one of objects in work contains property 'to' then $exists: equals to true. Is there a way to do this?
Also here is same code in JS if you prefer:
models.project.findOne({
'work.user': user,
'work.to': {
$exists: false
}
}, function(err, data) {});
Thanks for your time.
You can use $elemMatch in this case, which will return results only when specific user matched and doesn't have to value set in the same array element:
models.project.users.findOne({
"work" : { $elemMatch: {"user":user, "to": {$exists: false}}}
}, function(err, data) {});
you could use "null" for this case instead of $exists
models.project.findOne({
'work.user': user,
'work.to': null
}, function(err, data) {});

Resources