zstew
zstew

Reputation: 1375

MongoDB's BSON "_id" looks like scientific notation in Node.js

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

Answers (3)

zstew
zstew

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

Stennie
Stennie

Reputation: 65433

Converting from hex string to binary representation of ObjectID

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")

Comparing ObjectIDs

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).

Database References (DBRefs)

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

Jesus Ruiz
Jesus Ruiz

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

Related Questions