Mongoose query with undefined - node.js

I am trying to use mongoose to construct a query equivalent to this SQL:
select * from problems where tutorialNumber is not null
I've tried:
var q = Problem.find().where('tutorialNumber').ne(undefined);
q.exec(callback);
It returned an error: CastError: Cast to string failed for value "undefined" at path "tutorialNumber"
What is the right way to do that?

Thanks for the replies. I found another way to do it:
var q = Problem.find().exists('tutorialNumber', true);
q.exec(callback);

There are several syntax options. I believe your code is OK other than you should use null instead of undefined. I prefer the style that is a little closer to normal mongo shell:
Problem.find({tutorialNumber: {$ne: null}}, callback);
or you can do
Problem.find().ne('tutorialNumber', null).exec(callback);
But I believe the way you are using where and ne are also correct.
However, the CastError can mean there's a problem in your schema (could be trying to nest models instead of nesting schema).

Related

Converting string to ObjectId is failing in mongoose 4.6.0

I am trying to convert a string to ObjectId using
var body={};
var objId="57b40595866fdab90268321e";
body.id=mongoose.Types.ObjectId(objId);
myModel.collection.insert(body,function(err,data){
//causing err;
});
the above code is working fine when mongoose 4.4.16 is used, but if i update my mongoose to latest version(4.6.0) then problem occurs.
Err
object [
{
"_bsontype":"ObjectID",
"id:{"0":87,"1":180,"2":5,"3":235,"4":134,"5":111,"6":218,"7":185,"8":2,"9":104,"10":50,"11":111}
}
]
is not a valid ObjectId
The right way to insert new document is-
var newDocument = new myModel({
_id: mongoose.Types.ObjectId("57b40595866fdab90268321e")
});
newDocument.save();
In you case-
It stops working because the differences between versions of mongoose and mongo native drivers.
although, you are able to perform this by the example above, or, if you still want to use insert, you can use the myModel.insertMany (by passing object instead of array)
look here
http://mongoosejs.com/docs/api.html#model_Model.insertMany
I don't have the time to spike it, but if I remember correctly id is a simple string and _id is the ObjectId, i.e. either
body.id="57b40595866fdab90268321e"
or
body._id=mongoose.Types.ObjectId("57b40595866fdab90268321e");
That said, does it have to be that specific id? If not, you can use new myModel() and an id will be automatically created.

mongoose: how to get string representation of query

I implementing module who automatically generate mongoose query by requested params, so for simplified test process I need to be able to get text representation of final query. How could I do that?
Like we have something like this:
var q = AppModel.find({id:777}).sort({date:-1})
I need to get something like this
"db.appmodels.where({id:777}).sort({date: -1})"
You can set debug for mongoose, which would by default send the queries to console, to use the following:
mongoose.set('debug', function (collectionName, method, query, doc) {
// Here query is what you are looking for.
// so whatever you want to do with the query
// would be done in here
})
Given a query object q you can rebuild the query using its fields, namely q._conditions and q._update. This is undocumented though and could easily break between versions of Mongoose (tested on Mongoose 4.0.4).

Mongoose: CastError: Cast to ObjectId failed for value "[object Object]" at path "_id"

I am new to node.js, so I have a feeling that this will be something silly that I have overlooked, but I haven't been able to find an answer that fixes my problem. What I'm trying to do is create a path that will create a new child object, add it to the parent's array of children, then return the child object to the requester. The problem that I am running into is that if I pass the string id into findById, node crashes with
TypeError: Object {} has no method 'cast'
If I try to pass in an ObjectId instead, I get
CastError: Cast to ObjectId failed for value "[object Object]" at path "_id"
Here is a rough outline of my code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId; //Have also tried Schema.Types.ObjectId, mongoose.ObjectId
mongoose.connect('mongodb://user:password#server:port/database');
app.get('/myClass/:Id/childClass/create', function(request, result) {
var id = new ObjectId(request.params.Id);
MyClass.findById(id).exec( function(err, myClass) {
if (err || !myClass) { result.send("error: " + err + "<br>" + JSON.stringify(id) || ("object '" + request.params.Id + "' not found: " + id)); return; }
var child = ChildClass();
myClass.Children.addToSet(child);
myClass.save();
result.send(child);
});
});
If I execute this code with the path "/myClass/51c35e5ced18cb901d000001/childClass/create", this is the output of the code:
error: CastError: Cast to ObjectId failed for value "[object Object]" at path "_id"
{"path":"51c35e5ced18cb901d000001","instance":"ObjectID","validators":[],"setters":[],"getters":[],"_index":null}
I've tried using findOne and passing in {_id:id} instead, but this appears to be exactly what findById does. I've tried the different classes for ObjectId that I've seen listed on other sites. I've tried calling ObjectId() like a function instead of a constructor and that returns undefined. At this point, I'm running out of ideas and it doesn't seem that googling for an answer is helping. Any ideas on what I'm doing wrong?
Also, like I said, I'm new to node/Mongo/Mongoose/Express, so if there is a better way to accomplish my goal, please let me know. I appreciate all feedback.
EDIT:
After the workaround from Peter Lyons, I googled another error that I was running into and found findByIdAndUpdate, which works as expected and does exactly what I was hoping to do. I'm still not sure why findById and findOne were giving me such issues and I'm curious to know (maybe a bug report needs to be filed), so I'll leave this open in case someone else has an answer.
Short answer: use mongoose.Types.ObjectId.
Mongoose (but not mongo) can accept object Ids as strings and "cast" them properly for you, so just use:
MyClass.findById(req.params.id)
However, the caveat is if req.params.id is not a valid format for a mongo ID string, that will throw an exception which you must catch.
So the main confusing thing to understand is that mongoose.SchemaTypes has stuff you only use when defining mongoose schemas, and mongoose.Types has the stuff you use when creating data objects you want to store in the database or query objects. So mongoose.Types.ObjectId("51bb793aca2ab77a3200000d") works, will give you an object you can store in the database or use in queries, and will throw an exception if given an invalid ID string.
findOne takes a query object and passes a single model instance to the callback. And findById is literally a wrapper of findOne({_id: id}) (see source code here). Just find takes a query object and passes an array of matching model instances to the callback.
Just go slow. It's confusing but I can guarantee you you are getting confused and not hitting bugs in mongoose at this point. It's a pretty mature library, but it takes some time to get the hang of it.
The other suspect thing I see in your snippet is not using new when instantiating ChildClass. Beyond that, you'll need to post your schema code in order for us to help you tract down any CastErrors that remain.
I've faced this error, That was because the value you want to filter in the _id field is not in an ID format, one "if" should solve your error.
const mongoose = require('mongoose');
console.log(mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943'));
// true
console.log(mongoose.Types.ObjectId.isValid('whatever'));
// false
To solve it, always validate if the criteria value for search is a valid ObjectId
const criteria = {};
criteria.$or = [];
if(params.q) {
if(mongoose.Types.ObjectId.isValid(params.id)) {
criteria.$or.push({ _id: params.q })
}
criteria.$or.push({ name: { $regex: params.q, $options: 'i' }})
criteria.$or.push({ email: { $regex: params.q, $options: 'i' }})
criteria.$or.push({ password: { $regex: params.q, $options: 'i' }})
}
return UserModule.find(criteria).exec(() => {
// do stuff
})
For all those people stuck with this problem, but still couldn't solve it: I stumbled upon the same error and found the _id field being empty.
I described it here in more detail. Still have not found a solution except changing the fields in _id to not-ID fields which is a dirty hack to me. I'm probably going to file a bug report for mongoose. Any help would be appreciated!
Edit: I updated my thread. I filed a ticket and they confirmed the missing _id problem. It is going to be fixed in the 4.x.x version which has a release candidate available right now. The rc is not recommended for productive use!
If you are having this issue and you are performing a populate somewhere along the lines, see this Mongoose issue.
Update to Mongoose 4.0 and the issue has been fixed.
Had the same problem, I just coerced the id into a string.
My schema:
const product = new mongooseClient.Schema({
retailerID: { type: mongoose.SchemaTypes.ObjectId, required: true, index: true }
});
And then, when inserting:
retailerID: `${retailer._id}`
I had the same problem, turned out after I have updated my schema, I have forgotten I was calling the model using the old id, which was created by me; I have updated my schema from something like:
patientid: {
type: String,
required: true,
unique: true
},
to
patientid: { type: mongoose.SchemaTypes.ObjectId, ref: "Patient" },
It turned out, since my code is big, I was calling the findOne with the old id, therefore, the problem.
I am posting here just to help somebody else: please, check your code for unknown wrong calls! it may be the problem, and it can save your huge headacles!
My solution is that I want data from all docs, and I don't want _id, so
User.find({}, {_id:0, keyToShow:1, keyToNotShow:0})
I was receiving this error CastError: Cast to ObjectId failed for value “[object Object]” at path “_id” after creating a schema, then modifying it and couldn't track it down. I deleted all the documents in the collection and I could add 1 object but not a second. I ended up deleting the collection in Mongo and that worked as Mongoose recreated the collection.
For the record: I had this error trying to fill a subdocument in a wrong way:
{
[CastError: Cast to ObjectId failed for value "[object Object]" at path "_id"]
message: 'Cast to ObjectId failed for value "[object Object]" at path "_id"',
name: 'CastError',
type: 'ObjectId',
path: '_id'
value:
[ { timestamp: '2014-07-03T00:23:45-04:00',
date_start: '2014-07-03T00:23:45-04:00',
date_end: '2014-07-03T00:23:45-04:00',
operation: 'Deactivation' } ],
}
look ^ value is an array containing an object: wrong!
Explanation: I was sending data from php to a node.js API in this way:
$history = json_encode(
array(
array(
'timestamp' => date('c', time()),
'date_start' => date('c', time()),
'date_end' => date('c', time()),
'operation' => 'Deactivation'
)));
As you can see $history is an array containing an array. That's why mongoose try to fill _id (or any other field) with an array instead than a Scheme.ObjectId (or any other data type). The following works:
$history = json_encode(
array(
'timestamp' => date('c', time()),
'date_start' => date('c', time()),
'date_end' => date('c', time()),
'operation' => 'Deactivation'
));
I am not sure this will help but I resolved the issue by importing mongoose like below and implementing it as below
const mongoose = require('mongoose')
_id: new mongoose.Types.ObjectId(),
I also encountered this mongoose error
CastError: Cast to ObjectId failed for value \"583fe2c488cf652d4c6b45d1\" at path \"_id\" for model User
So I run npm list command to verify the mongodb and mongoose version in my local.
Heres the report:
......
......
├── mongodb#2.2.19
├── mongoose#4.7.2
.....
It seems there's an issue on this mongodb version so what I did is I uninstall and try to use different version such as 2.2.16
$ npm uninstall mongodb, it will delete the mongodb from your node_modules directory. After that install the lower version of mongodb.
$ npm install mongodb#2.2.16
Finally, I restart the app and the CastError is gone!!
I was having the same problem.Turns out my Node.js was outdated. After upgrading it's working.
just change the path it will work for example
app.get('/myClass/:Id/childClass/create', function(request, result) .....
change to
app.get('/myClass**es**/:Id/childClass/create', function(request, result) .....
I just added --es-- to the path (myClass) to become (myClasses)
now should work and will not see that error
For me, the ID was undefined (req.params.id returns undefined)
If you're finding a document by its "_id" for a model, say Drivers, the command below works quite well:
....
const driver = await Drivers.findById(<your id>);
....
Ensure you use the async-await and try-catch ES6 syntax
For me, I was using a put request without sending any data. I changed it to a post request and it worked.
This thread covers more about put requests.
just change the path it will work for example
app.get('/myClass/:Id/childClass/create', function(request, result)
change to
app.get('/myClass**es**/:Id/childClass/create', function(request, result)
I just added --es-- to the path (myClass) to become (myClasses)
now should work and will not see that error

Query Mongoose Schema by ObjectId

Going to need your help again, hopefully for this project, the answer to what I'm having here will be the last. I've seen it's a fairly commonly asked question, but I've tried the tips on another Stack Overflow post, and one by the Google Group, but the solutions haven't worked for me.
My code being a little bit like:
mongoose = require('mongoose');
Schema = mongoose.Schema;
mongoose.connect(MONGO_SERVER);
ObjectId = Schema.ObjectId;
var RacesSchema = new Schema({
venue_id : { type: mongoose.Schema.ObjectId, ref: 'Venues' },
racetype : String
});
var races = mongoose.model('Races', RacesSchema );
function test() {
var MyObjectId = require('mongoose').Types.ObjectId;
queryVenue = new MyObjectId("50e3dcdbf30375180c078f64");
races.find({venue_id: queryVenue, racetype:'Test'})
.exec(function(err,data) {
}
test();
But I get no results, which I know there is.
Many thanks in advance!
UPDATE
Have minimized the above code sample, this test works if I query the string value on its own, just querying for an ObjectId is where it fails, and I know it exists.
JSON UPDATE
Here is what I am looking for:
{
"_id" : ObjectId("50e3dcddf30375180c078f85"),
"venue_id" : "50e3dcdbf30375180c078f64",
"racetype" : "A"
}
And all of a sudden, I believe my answer has become clear to me. Is it simply because the venue_id is actually a string? And if so, can I keep my mongoose schema the way it is, and do casting on the query at the point of doing the find of being a string? Or should I change the way these values are being stored (from a separate .net application I developed) to be inserted as ObjectId's?
Currently right now for another query, the current mongoose schema and the way the database is [actually set up], using populate() works quite well to fill the results of the referenced table (venue_id) with the way this model is currently set up, only difference being on the above query, I don't specify the venue_id...
Thanks.
Right, the problem is happening because the data type of venue_id in the schema (ObjectId) doesn't match the one in the doc (String). So the find is looking for ObjectId values but doesn't find a match because it's a string in the doc.
The right fix for this is to write a little program to update the venue_id values in your docs to be ObjectIds instead of strings and then your query will work. That will also shrink the size of those fields from 24 bytes to 12.

Add a new attribute to existing json object in node.js

I have an object like this
==================records=========={ Id: 5114a3c21203e0d811000088,
userId: 'test',
sUserId: test,
userName: 'test',
url: 'test',
Title: 'test'
}
I need to add a new field Name : 'test' to the above record, I tried giving records.Name = name, it didn't work.
Helps please
Thanks,
Prats
I am assuming you are trying to add a property to a returned Mongoose Document to reuse it somewhere else. Documents returned by Mongoose are not JSON objects directly, you'll need to convert them to an object to add properties to them. The following should work:
//... record is a mongoose Document
var r = record.toObject();
r.Name = 'test';
console.log("Record ",r);
Those finding this problem, OP mentioned in a comment below the original question that the solution for this problem is:
records.set('Name', 'test')
This adds a new attribute called Name having value test.
Just use,
var convertedJSON = JSON.parse(JSON.stringify(mongooseReturnedDocument);
and Then,
convertedJSON.newProperty = 'Hello!'
'Hello!' can be anything, a number, a object or JSON Object Literal.
Cheers! :)
I experienced a similar problem, and hope that my hours of existential frustration help others in the same situation. My inclination was to believe that documents returned via Mongoose are read-only. This is actually not true.
However, you cannot assign a property to your document that is not also in your Schema.
So, unless your schema has the following:
{
Name: {String}
}
you will be constantly frustrated by trying to assign Name to your document.
Now, there are workarounds in the answers above that also worked for me, but they do not get to the root of the problem:
myDocument.toObject();
JSON.parse(JSON.stringify(myDocument);
These will work, but in my opinion they just hide the problem. The real issue is that Mongoose is smarter than we realized and is enforcing your schema, as it should.
You could also use the lean() method, e.g. const users = await Users.find().lean().exec()
From the mongoose documentation:
Documents returned from queries with the lean option enabled are plain
javascript objects, not MongooseDocuments. They have no save method,
getters/setters or other Mongoose magic applied
My variant.
If schema defined with {strict:false} you can simply add new property by
recorcd.newProp = 'newvalue';
If schema defined with {strict:true} you can either convert Mongoose object to object as mentioned earlier or use command
record.set('newProp','newValue',{strict:false})
See http://mongoosejs.com/docs/api.html#document_Document-schema
If you have loaded this object into records, both records.Name = "test" or records['Name'] = "test" will work. You have either not loaded the object correctly, or are inserting an undefined value into it.
To test: add console.log(records.userId), this should print 'test' to the terminal.
Also add console.log(name). If you get ReferenceError: name is not defined, you obviously cannot do: records.Name = name

Resources