Использование UUID в mongoose для ссылок на ObjectID

Я создаю REST-сервис в стиле CRUD с Node.js, Express и MongoDB, используя mongoose. Этот сервис позволит пользователям уже существующего приложения для Android загружать/синхронизировать содержимое своих отдельных баз данных в Интернете.

Модель данных для уже существующего приложения использует UUID (сгенерированные в Java), которые конфликтуют с более короткими, монотонными полями стиля MongoDB _id. Поскольку модель данных уже существует и заполняется данными от многих пользователей, я не могу преобразовать исходные данные в монотонный стиль MongoDB _id. Это оставляет мне 2 варианта, которые я могу придумать: 1) заставить Mongo/Mongoose (или какой-нибудь другой ODM) играть с полными UUID вместо монотонных _id или 2) добавить поле uuid к модели mongoose в дополнение к _id поле и бороться с подводными камнями этого подхода. Я пытаюсь выбрать вариант № 1 и сталкиваюсь с проблемами со ссылками на ObjectID.

Первоначально я наткнулся на mongoose-uuid, но, к сожалению, это не работает для моего варианта использования должным образом, потому что он перезаписывал мое явно установленное значение _id при создании новых объектов Mongoose. Погружаясь в код плагина, он предполагает, что объект является новым (вызывая проверку значения Mongoose .isNew) и, таким образом, переписывает _id новым uuid. Поскольку мне нужно сохранить исходный uuid при создании новых документов в Mongo, этот плагин не работает для меня.

Затем я нашел пост Аарона Хекмана, создателя mongoose, на аналогичную тему. Это было полезно, однако сейчас я сталкиваюсь с проблемой, когда у меня не может быть, чтобы мои схемы mongoose ссылались друг на друга с помощью ObjectID, так как они технически они теперь ссылаются друг на друга, используя String '_ids.

Пример схемы:

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

Ссылочная схема:

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

Когда я пытаюсь вызвать save() trackPass, который был передан из моего приложения:

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

Я получаю следующую ошибку:

{ [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' }

Я полагаю, что эта ошибка имеет смысл, поскольку теперь я использую строки, которые длиннее типичных идентификаторов объектов Mongo. Не имея ссылки ObjectID, я не верю, что смогу populate() ссылаться на объекты из других коллекций. Я полагаю, что я просто не мог бы ссылаться на другие вложенные объекты в своих определениях схемы, однако мне не нравится этот подход, так как я чувствую, что потерю большую выгоду от использования ODM. Есть еще мысли?

Ответ 1

Вы все еще можете использовать populate() со значениями _id типов помимо ObjectID, но вам нужно использовать тот же тип в определении ссылки.

Таким образом, ваш trackPassSchema должен будет измениться на:

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

Как отмечает Адам в комментариях, вы можете упростить свою ценность default до:

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