Reputation: 1375
Occasionally I get a document with an _id value which javascript could and does interpret as scientific notation.
ONLY illustrate the problem I ran the following query.
db.users.find({$where:'this._id > 1'}).count()
2
There are hundreds of docs in this collection, but those 2 that evaluate as numbers cause problems when they get used in {$in:[]} clauses.
db.users.findOne({$where:'this._id > 1'})._id
ObjectId("5225141850742e0000002331") - see it's looks like scientific notation right?
I think I run into trouble when I want to store that _id as a string in another collection say as
friendsCollection: { _uid:"5225141850742e0000002331" //more properties here }
When I retrieve that value, Node (or Mongoose) interprets it as a number like "Infinity". Then my code ends up trying to search for the user with {_id:{$in:[Infinity]}} which throws an error.
I'm guessing there's a more robust way to store _id values or to handle properties you know to be _ids, but I don't know how.
Upvotes: 0
Views: 1610
Reputation: 1375
Ahhh, maybe I should use the Mongoose data type "ObjectId" in my schemas.
For example I was using mongoose schemas like this:
locations:{
_uid:{type:String},//<--probably not good
lat:{type:Number},
lng:{type:Number}
},
Started using the Schema type "ObjectId":
locations:{
_uid:Schema.Types.ObjectId,//<--better?
lat: {type:Number},
lng: {type:Number}
},
I have to rebuild a lot of data to support this change. Also, I won't be sure until another one of those scientific notation _ids pop up, (I deleted the offending docs). However, this seams promising.
Upvotes: 0
Reputation: 65433
If you want to convert from a 24-byte hex string representation of an _id
value into a binary ObjectID as stored in MongoDB, you can use ObjectID.createFromHexString:
// Need to require ObjectID class if not already included
ObjectID = require('mongodb').ObjectID;
var uid = ObjectID.createFromHexString("5205c4bd7c21105d0d99648c")
You should be using the $gt
operator for ObjectID comparison rather than $where
. The $where
operator evaluates a JavaScript string and cannot take advantage of indexes; it will be much less performant (particularly for this use case).
So the findOne()
example to find an _id
greater than a given ObjectID should instead be:
db.users.findOne(
{ _id: { $gt: ObjectID("5205c4bd7c21105d0d99648c") } }
)._id
For a predictable outcome on finding the next higher ObjectID you should specify an explicit sort order using find()
with sort
and limit
:
// Find next _id greater than ObjectID("5205c4bd7c21105d0d99648c")
db.users.find(
{_id: { $gt: ObjectID("5205c4bd7c21105d0d99648c") } }
).sort({_id:1}).limit(1).toArray()[0]._id
You'll notice that these find examples doesn't explicitly call createFromHexString
. The default ObjectID()
constructor will try to create an appropriate ObjectID
based on whether the given value is a 24 byte hex string, 12 byte binary string, or a Number. If you know what sort of value you are providing, it is better to call the expected constructor to limit unexpected conversions (for example if you accidentally provided a Number instead of a hex string).
MongoDB explicitly does not support joins, however there is a convention for storing database references (DBRefs) when you want to store the _id
of a related document as a reference in another document. Mongoose has a ref
option that simplifies working with references; see 'Population' in the Mongoose docs.
Upvotes: 3
Reputation: 1385
At some point I had problems when querying _id using the native driver. I fixed it by using ObjectId in my queries. You might find this helpful:
var ObjectId = require("mongodb").ObjectID;
query._id = { $gt: ObjectId(idString) }
Upvotes: 0