Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using UUIDs in mongoose for ObjectID references

Tags:

I'm building a CRUD-style REST service with Node.js, Express and MongoDB using mongoose. This service is going to allow users of an already existing android application to upload/sync the contents of their individual databases online.

The data model for the already-existing application uses UUIDs (generated in Java) which clashes with the shorter, monotonic MongoDB style _id fields. Because the data model already exists and is populated with data from many users, I cannot convert the source data over to monotonic MongoDB-style _ids. This has left me with 2 options that I can think of: either 1) Make Mongo/Mongoose (or some other ODM) play nicely with full UUIDs instead of the monotonic _ids or 2) add a uuid field to the mongoose model in addition to the _id field and fight the pitfalls of this approach. I'm attempting to choose option #1 and running into issues with ObjectID references.

I originally stumbled upon mongoose-uuid, but unfortunately this isn't working for my use-case properly because it was overwriting my explicitly-set _id value when creating new Mongoose objects. Diving into the plugin code, it assumes that an object is new (by calling checking Mongoose's .isNew value) and thus overwrites the _id with a new uuid. Since I need to retain the original uuid when creating new documents in Mongo, this plugin isn't working for me.

Next, I found a post by Aaron Heckmann, creator of mongoose, on a similar topic. This has been helpful, however I am now encountering the problem where I cannot have my mongoose schemas reference each other by ObjectID, since they technically they are now referencing each other using String `_ids.

Schema example:

var mongoose = require('mongoose'); var uuid = require('node-uuid');  var Schema = mongoose.Schema;  var trackPassSchema = new Schema({     _id: { type: String, default: function genUUID() {         uuid.v1()     }},     //Omitting other fields in snippet for simplicity     vehicle: [         {type: Schema.Types.ObjectId, required: true, ref: 'Vehicle'}     ] });  module.exports = mongoose.model('TrackPass', trackPassSchema); 

Referencing schema:

var mongoose = require('mongoose'); var uuid = require('node-uuid');  var Schema = mongoose.Schema;  var vehicleSchema = new Schema({     _id: { type: String, default: function genUUID() {         uuid.v1()     }},     //Omitting other fields in snippet for simplicity     description: {type: String},     year: {type: Number} });  module.exports = mongoose.model('Vehicle', vehicleSchema); 

When I attempt to call save() a trackPass that has been passed in from my application:

var trackPass = new TrackPass(req.body);  //Force the ID to match what was put into the request trackPass._id = req.params.id; trackPass.save(function (err) { ... } 

I get the following error:

{ [CastError: Cast to ObjectId failed for value "b205ac4d-fd96-4b1e-892a-d4fab818ea2a" at path "vehicle"]   message: 'Cast to ObjectId failed for value "b205ac4d-fd96-4b1e-892a-d4fab818ea2a" at path "vehicle"',   name: 'CastError',   type: 'ObjectId',   value: ["b205ac4d-fd96-4b1e-892a-d4fab818ea2a"],   path: 'vehicle' } 

I believe this error makes sense as I'm now using Strings which are longer than typical Mongo ObjectIDs. Without having the ObjectID reference, I don't believe I will be able to populate() referenced objects from other collections. I suppose I could simply not reference the other nested objects in my schema definitions, however I don't like this approach as I feel I will be losing a lot of the benefit of utilizing the ODM. Any other thoughts?

like image 673
JeremyL Avatar asked Dec 12 '14 07:12

JeremyL


People also ask

Is MongoDB ObjectId UUID?

A MongoDB ObjectID is 12 bytes in size, is packed for storage, and its parts are organized for performance (i.e. timestamp is stored first, which is a logical ordering criteria). Conversely, a standard UUID is 36 bytes, contains dashes and is typically stored as a string.

What is ObjectId in mongoose?

ObjectId . A SchemaType is just a configuration object for Mongoose. An instance of the mongoose. ObjectId SchemaType doesn't actually create MongoDB ObjectIds, it is just a configuration for a path in a schema.

Does mongoose auto generate ID?

_id field is auto generated by Mongoose and gets attached to the Model, and at the time of saving/inserting the document into MongoDB, MongoDB will use that unique _id field which was generated by Mongoose.

What is UUID in MongoDB?

MongoDB itself stores UUIDs as Binary fields and when such Binary fields are accessed from software, MongoDB drivers usually convert their value as found in the database to language-specific UUID or GUID objects. For example, when you read a UUID from a MongoDB database using the Java driver, an object of type java.


1 Answers

You can still use populate() with _id values of types besides ObjectID, but you do need to use the same type in the reference definition.

So your trackPassSchema would need to change to:

var trackPassSchema = new Schema({     _id: { type: String, default: function genUUID() {         return uuid.v1()     }},     vehicle: [         {type: String, required: true, ref: 'Vehicle'}     ] }); 

As Adam notes in the comments, you could simplify your default value to:

var trackPassSchema = new Schema({     _id: { type: String, default: uuid.v1 },     vehicle: [         {type: String, required: true, ref: 'Vehicle'}     ] }); 
like image 184
JohnnyHK Avatar answered Oct 21 '22 01:10

JohnnyHK