MongoDB Query returning false results - node.js

I have streamlined this question for simplicity. Value is passed to the function, and if there is no document with {field1: value}, create that document; otherwise, call another function. However, this query will always find a document, or perhaps fires whatever_function() regardless. Why can I not get (!doc) to be true? This database is operative and queries/updates appropriately except for this issue.
db.doc.find({field1: value}, function(err, doc) {
if (!doc) {
db.doc.save({field1: value});
}
else {
whatever_function();
}
});

Find returns a cursor to the selected documents. Then, you should check that doc length is 0.

Related

How to replace a document matched by _id?

Working on an API I want to do a full document update of a MongoDB object.
This is my current code that works but it feels wrong to have to delete the _id every time. Is there a better way to do this?
PutDoco : function(doco){
return new Promise(function(Resolve,Reject){
delete doco._id;
db.collection('docos').updateOne(
{"details.ID":doco.details.ID},
doco,
function(err,result){
if(err)
return Reject(err);
Resolve(result);
}
);
});
},
You can use replaceOne instead when replacing all contents of a document (besides _id which is immutable). And because replaceOne returns a promise if you don't pass it a callback, you can reduce your whole function down to:
return db.collection('docos').replaceOne({"details.ID":doco.details.ID}, doco);
However, it might be clearer (and faster) to find the document to replace using _id instead:
return db.collection('docos').replaceOne({_id: doco._id}, doco);

MongoDB/Mongoose returning empty array

I'm currently working on a project with mongodb/mongoose, and every time I purposely query for something that does not exist in the DB, I am getting a response with an empty array. This is my code for using Express to set up an API and return the data found in the DB:
app.get('/api/:id', function(req, res) {
var id = req.params.id;
Job.find({jobID: id}, function (err, foundJob) {
if (err) {
console.log(err);
}
else {
res.json(foundJob);
}
});
});
However, every time I go to localhost:3000/api/67 (I have no object with jobID: 67 in the database yet), the console does not print the error. It gives me a JSON response with an empty array. Any ideas to why this is happening? The weird part is that when I change jobID: id to _id: id, it does give me an error. Why doesn't it do that for the jobID field?
EDIT: Just to clarify, the reason why I do not want this behavior is because my program will never print the error, even if a job in my DB doesn't exist with that specified jobID.
It does something different than you think it does.
Job.find({jobID: id}, ... )
What this really says is give me array with all the documents in collection "Job" that have field "jobID" equal to some value.
What if there is no such document? Well, then the array will be empty. Without any error, of course, what should be an error here? You asked for all documents (given some filter) and an array with all such documents was returned. It is just a coincidence that the size of the array is zero, because there are no such documents.
If you want to check whether there is no such document then check whether the array is empty.
I don't know why it is giving you error when you change JobID to _id; what error exactly is it?
If you are interested only in one document, then there is method findOne that returns only the first document (or null if no such documents exist) instead of an array.
About error when you try to find something by it's __id: It gives you a error because __id is not String, it's ObjectId. So you should search for document with that __id like this: _id: ObjectId(id)
About empty string: If you want to display some kind of different message you should check if db returned something or is it rather empty string that got returned. Something like this:
app.get('/api/:id', function(req, res) {
var id = req.params.id;
Job.find({jobID: id}, function (err, foundJob) {
if(foundJob){
res.json(foundJob);
}else{
res.json("nothing found");
}
if (err) {
console.log(err);
}
});
});
Edit: I didnt realize that you had check for error, I changed code.
Returning an empty string is normal behavior for mongoose. You should handle your response like this:
if (err) {
//handle error
} else if (foundJob) {
//work with your data
} else {
//nothing was found
}
The error you get with _id must be irrelevant and is probably due to an invalid query.

Mongoose: disable empty query returning a document

When using Mongoose (with bluebird in my case, but using callbacks to illustrate), the following codes all return a document from the collection:
model.findOne({}, function(err, document) {
//returns a document
})
model.findOne(null, function(err, document) {
//returns a document
})
model.findOne([], function(err, document) {
//returns a document
})
I would like to know if and how I can disable this kind of behaviour, as it is becoming a liability to my code where I infer queries from data a user feeds into the system. Especially the null query returning a valid document worries me.
As of right now I check the input for being an non-empty, non-array, non-null object, but it's becoming a bit cumbersome at scale.
What would be the best way to exclude this behaviour?
Not sure if it is the best way to go about it, but right now I've settled on using a pre-hook on the model itself which checks for the _conditions property of the 'this' object (which I inferred from printing seems to hold the query object) to not be empty.
Inserting a self-defined object in the next functionality causes the Promise to reject in which the query was originally called from.
( _ is the underscore package)
//model.js
//model is a mongoose.Schema type in the following code
model.pre('findOne', function(next) {
var self = this
if (_.isEmpty(self._conditions)) {
next(mainErrors.malformedRequest)
} else {
next()
}
})

Why can't I seem to merge a normal Object into a Mongo Document?

I have a data feed from a 3rd party server that I am pulling in and converting to JSON. The data feed will never have my mongoDB's auto-generated _ids in it, but there is a unique identifier called vehicle_id.
The function below is what is handling taking the data-feed generated json object fresh_event_obj and copying its values into a mongo document if there is a mongo document with the same vehicle_id.
function update_vehicle(fresh_event_obj) {
console.log("Updating Vehicle " + fresh_event_obj.vehicleID + "...");
Vehicle.find({ vehicleID: fresh_event_obj.vehicleID }, function (err, event_obj) {
if (err) {
handle_error(err);
} else {
var updated = _.merge(event_obj[0], fresh_event_obj);
updated.save(function (err) {
if (err) {
handle_error(err)
} else {
console.log("Vehicle Updated");
}
});
}
});
}
The structures of event_obj[0] and fresh_event_obj are identical, except that event_obj[0] has _id and __v while the "normal" object doesn't.
When I run _.merge on these two, or even my own recursive function that just copies values from the latter to the former, nothing in the updated object is different from the event_obj[0], despite fresh_event_obj having all new values.
Does anyone have any idea what I'm doing wrong? I feel it is obvious and I'm just failing to see it.
The problem is that if you don't have properties defined in your schema, and if they don't already exist, you can't create them with
doc.prop = value
even if you have {strict:false} in your schema.
The only way to set new properties is to do
doc.set('prop', value)
(You still have to have {strict:false} in your schema if that property doesn't exist in your schema)
As for having too many properties to be defined in schema, you can always use for-in loop to go through object properties
for(key in fresh_event_obj)
event_obj.set(key, fresh_event_obj[key]);

Node.js MongoDB Upsert update

I'm writing a little application which scores keywords. So if "beirut" and "education" get entered in, if they haven't been seen before, I want to create a mongo entry, and give them a score of 1. If they have, I want to increment their score by one. I'm trying to do this with one update command, but I think I might be doing it wrong.
Ranking is the object representing the database
"key" is the keyword
rankingdb.update(
{keyword:key},
{keyword:key, {$inc:{score:1}}},
{upsert:true, safe:false},
function(err, data) {
if (err) {
console.log(err);
}
else {
console.log("score succeeded");
}
}
);
SyntaxError: Unexpected token {
Can you not create a brand new document with an increment?
Your general approach is right, but as the error message suggests, you've got a syntax problem in your code.
Try this instead:
rankingdb.update(
{keyword: key},
{$inc: {score: 1}},
{upsert: true, safe: false},
function(err,data){
if (err){
console.log(err);
}else{
console.log("score succeded");
}
}
);
When an upsert needs to create a new object it combines the fields from the selector (first parameter) and the update object (second parameter) when creating the object so you don't need to include the keyword field in both.
Note that update() is deprecated in the 2.0 driver, so you should now use either updateOne() or updateMany().

Resources