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?

推荐答案

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'}
    ]
});

Mongodb相关问答推荐

Mongo Aggregate管道-另一个集合中的多个查找

为什么使用 Golang Mongo 驱动程序进行简单查询需要超过 2 秒?

MongoDB:通过嵌套数组中的最后一个元素值获取文档

使用golang的MongoDB错误无法访问服务器

在mongoose 中按键查找嵌套对象

如何使用 Golang 库获取 MongoDB 版本?

_id 和 $oid 的区别; mongo 数据库中的 $date 和 IsoDate

Mongo 删除最后的文件

当属性确实存在时,为什么mongoose模型的 hasOwnProperty 返回 false?

Mongoose 架构引用和未定义类型ObjectID

MongoDB映射/减少多个集合?

MongoDB 3 Java判断集合是否存在

Python - Pymongo 插入和更新文档

使用 MongoDB Java 驱动程序将 DBObject 转换为 POJO

无法从 MongoDb C# 中的 BsonType ObjectId 反序列化字符串

直接从 URL 查询字符串提供的 mongo 查询有多危险?

MongoError: The dollar ($) prefixed field '$push' in '$push' is not valid for storage

如何向 mongodb 2.6 添加身份验证?

MongoDB:删除唯一约束

将日期从毫秒转换为 ISODate 对象