Mongoose nodejs find not working correctly - node.js

so I'm doing a mongoose find and I've defined collections etc and it all works fine EXCEPT when I try to do a find by a value.
var searchID = req.user.id;
console.log(searchID);
Locations.find({userid: '541f69e7fd4c3b07108c92c0'}, function(err, location) {
if (err) return console.error(err);
console.log(location);
});
the userid is a property which is the ID of the user that created it. The find doesnt work in mongo console either.
Any ideas? I can do finds by the actual _id of the location or any other value.

As Neil Lunn commented, your problem is almost certainly your schema not being correct. Once your mongoose schema clearly defines the userid property as being of type mongoose.Schema.Types.ObjectId, mongoose will start casting your string value from the query to a proper ObjectId instance before sending it to mongodb, and thus the query will start matching documents and you'll see results.
{userid: {type: mongoose.Schema.Types.ObjectId, ref: 'Users'}}

Related

Mongoose - get length of array in model

I have this Mongoose schema:
var postSchema = mongoose.Schema({
postId: {
type: Number,
unique: true
},
upvotes: [
{
type: Number,
unique: true
}
]
});
what the best query to use to get the length of the upvotes array? I don't believe I need to use aggregation because I only want to query for one model, just need the length of the upvotes array for a given model.
Really struggling to find this info online - everything I search for mentions the aggregation methodology which I don't believe I need.
Also, as a side note, the unique schema property of the upvotes array doesn't work, perhaps I am doing that wrong.
find results can only include content from the docs themselves1, while aggregate can project new values that are derived from the doc's content (like an array's length). That's why you need to use aggregate for this, even though you're getting just a single doc.
Post.aggregate([{$match: {postId: 5}}, {$project: {upvotes: {$size: '$upvotes'}}}])
1Single exception is the $meta projection operator to project a $text query result's score.
I'm not normally a fan of caching values, but it might be an option (and after finding this stackoverflow answer is what I'm going to do for my use case) to calculate the length of the field when the record is updated in the pre('validate') hook. For example:
var schema = new mongoose.Schema({
name: String,
upvoteCount: Number,
upvotes: [{}]
});
schema.pre('validate', function (next) {
this.upvoteCount = this.upvotes.length
next();
});
Just note that you need to do your updates the mongoose way by loading the object using find and then saving changes using object.save() - don't use findOneAndUpdate
postSchema.virtual('upvoteCount').get(function () {
return this.upvotes.length
});
let doc = await Post.findById('foobar123')
doc.upvoteCount // length of upvotes
My suggestion would be to pull the entire upvotes fields data and use .length property of returned array in node.js code
//logic only, not a functional code
post.find( filterexpression, {upvote: 1}, function(err, res){
console.log(res.upvotes.length);
});
EDIT:
Other way of doing would be stored Javascript. You can query the
upvote and count the same in mongodb side stored Javascript using
.length

MongoDB, Mongoose, and composite _id

New to Mongodb & Mongoose.js.
I have created the following schema & model:
var schema = new Schema({
_id: {part1: String, part2: Number},
name: String
});
mongoose.model('myDoc', schema);
I can save this, and when I view it on the mongo command line, it looks fine.
However in mongoose when I do:
myDoc.find({}, function(err, recs) {
var rec = recs[0];
console.log('----' + JSON.stringify(rec));
});
I get my object printed out, but with the following exception: Cast to ObjectId failed for value "[object Object]" at path "_id"
I've seen a few explanations, but I don't understand what I'm doing wrong, and how I need to fix it.
According to mongodb documentation the _id can be bson-type. What am I doing wrong? Isn't {part1: String, part2: Number} a bson?
According to this post from the Mongoose author, compound _id fields aren't yet supported by Mongoose.

Mongoose searching on array subdocument ObjectId not working

This is honestly driving me crazy. Here's the problem:
I'm running the following query in Mongoose:
s.findSubdocument=function(uid, cb)
{
this.model('User').find({"flwng._id":uid.toString()}).select('flwng').exec(cb);
}
on the following User Schema:
var userSchema= new mongoose.Schema(
{
uname:{type:String, index:{sparse:true, unique:true, dropDups:true}}, //the username
email:String,
pwd:{type:String},
flwng:[{_id : {type : mongoose.Schema.ObjectId},uname : String}], //_id of users I am following
flwrsz:Number,
flwngsz:Number,
feedsz:Number,
}, {j: 1});//j:1 means guarantee it is written to the journal.
userSchema.set('autoIndex', true);
userSchema.index({"fid":1}, {sparse:true, unique:true, dropDups:true});
userSchema.index({"flwng._id" : 1});
Where uid="53c4f16c431247694f0000a3" ==> but I get an empty array back :(
When I run the same exact query in the mongo shell:
db.users.find({"flwng._id":"53c4f16c431247694f0000a3"});
I get the right set of results back. I tried with and without an index and schema on "flwng._id", I tried to drop the index, reIndex and I'm now running out of ideas. Am I doing something wrong with Mongoose?
Any help would be appreciated - thanks!
Henri
There's a mismatch between your existing documents in mongodb and your schema. Your data has records where flwng._id is a String, which is why you get results in the mongo shell. But your mongoose schema defines that as an ObjectId, so when you query with mongoose, mongoose casts your string value to an ObjectId and the query doesn't match. Either write a migration to fix your existing data or update your schema to match your data in terms of String vs ObjectId data type and things should start working through mongoose.

Mongoose: Using addToSet with ObjectIds Results in Orphan Id

I am having a rather interesting problem using mongoDB's $addToSet to an array full of ObjectIds.
In my mongoose schema ("Happening"), I declare an array of ObjecIds called "expected", to be used by .populate().
expected: [{type: Schema.Types.ObjectId, ref: "User" }]
... which works nicely everywhere I use it. So far so good.
I then attempt to update the Happening.expected array using $addToSet as outlined here:
http://docs.mongodb.org/manual/reference/operator/addToSet/
like so:
app.get("/happening/yamobethere/:id", ensureLoggedIn("/login"),
function (req, res) {
// userId is the mongo ObjectId of the user record
var userId = req.session.user.id,
eventId = req.params.id;
models.Happening.update(
{_id: eventId}, {
$addToSet: {expected: userId}
},
function(err, updated){
if (err) {
res.json({"error": err});
}
res.json({"updated": updated});
});
});
... which always yields:
{updated: 1}
Now the docs lead me to expect the actual userId that I passed in, so the "1" is a bit odd. I expected it to be a fail, and in light of the weirdness that happens next, it appears to be a mongodb error of some sort percolating it's way back to me as results.
The weirdness is, when I check my database, I see that indeed a new ObjectId has been added: just not the one I passed in.
"expected" : [
ObjectId("51cb18623ade2b9f1e000004"),
ObjectId("51cdb7c12f0e58bdb3000001")
],
becomes
"expected" : [
ObjectId("51cb18623ade2b9f1e000004"),
ObjectId("51cdb7c12f0e58bdb3000001"),
ObjectId("51cdb80e09612bfab3000002")
],
The new ObjectId does not appear in any of my collections. It appears to be an orphan, but I'm a mongo noob, so I may be full of compost on this.
I did attempt to cast the userId as an ObjectId:
$addToSet: {expected: mongoose.Types.ObjectId.fromString(userId)}
but that changed nothing, and really should not be necessary, since the schema should handle it.
I'd really rather not resort to downloading the entire object, appending the value to the "expected" array, then sending the whole schmear back for an update.
Any help appreciated, folks. Thanks!
Update:
A colleague suggested the following technique:
var addMe = {$addToSet: {expected: userId}};
models.Happening.findByIdAndUpdate(eventId, addMe, function(err, me) {
if (err) {
return json(err);
}
res.json(200, me);
});
... which is a bit of an improvement, since it actually returns an object for me to inspect. Unfortunately, it also results in orphaned ObjecIds appearing in the array, rather than the existing userId value I specified.
Thanks again!
It appears that my passport strategy is returning the ObjectID of the rejected attempted creation of a new user in the db via data from oauth. So, the code is fine, my data is garbage.
Never trust anything, and be prepared to look like a boob. :-)
Thanks for the clarification on my return values JohnnyHK.

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

Resources