diff --git a/lib/schema/uuid.js b/lib/schema/uuid.js index ca4ab3eba3d..839c27dfaba 100644 --- a/lib/schema/uuid.js +++ b/lib/schema/uuid.js @@ -8,7 +8,6 @@ const MongooseBuffer = require('../types/buffer'); const SchemaType = require('../schematype'); const CastError = SchemaType.CastError; const utils = require('../utils'); -const isBsonType = require('../helpers/isBsonType'); const handleBitwiseOperator = require('./operators/bitwise'); const UUID_FORMAT = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i; @@ -86,7 +85,13 @@ function binaryToString(uuidBin) { function SchemaUUID(key, options) { SchemaType.call(this, key, options, 'UUID'); - this.getters.push(binaryToString); + this.getters.push(function(value) { + // For populated + if (value != null && value.$__ != null) { + return value; + } + return binaryToString(value); + }); } /** @@ -110,7 +115,7 @@ SchemaUUID.prototype.constructor = SchemaUUID; */ SchemaUUID._cast = function(value) { - if (value === null) { + if (value == null) { return value; } @@ -247,11 +252,8 @@ SchemaUUID.prototype.checkRequired = function checkRequired(value) { */ SchemaUUID.prototype.cast = function(value, doc, init) { - if (SchemaType._isRef(this, value, doc, init)) { - if (isBsonType(value, 'UUID')) { - return value; - } - + if (utils.isNonBuiltinObject(value) && + SchemaType._isRef(this, value, doc, init)) { return this._castRef(value, doc, init); } diff --git a/lib/schematype.js b/lib/schematype.js index d70db0eee00..2b62c2208f7 100644 --- a/lib/schematype.js +++ b/lib/schematype.js @@ -1521,6 +1521,7 @@ SchemaType.prototype._castRef = function _castRef(value, doc, init) { const path = doc.$__fullPath(this.path, true); const owner = doc.ownerDocument(); const pop = owner.$populated(path, true); + let ret = value; if (!doc.$__.populated || !doc.$__.populated[path] || diff --git a/lib/utils.js b/lib/utils.js index 2aa75462037..923dc700222 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -4,6 +4,7 @@ * Module dependencies. */ +const UUID = require('bson').UUID; const ms = require('ms'); const mpath = require('mpath'); const ObjectId = require('./types/objectid'); @@ -406,6 +407,7 @@ exports.isNonBuiltinObject = function isNonBuiltinObject(val) { return typeof val === 'object' && !exports.isNativeObject(val) && !exports.isMongooseType(val) && + !(val instanceof UUID) && val != null; }; diff --git a/test/schema.uuid.test.js b/test/schema.uuid.test.js index 8292c233d35..864c276ebb0 100644 --- a/test/schema.uuid.test.js +++ b/test/schema.uuid.test.js @@ -3,9 +3,9 @@ const start = require('./common'); const util = require('./util'); -const bson = require('bson'); - const assert = require('assert'); +const bson = require('bson'); +const { randomUUID } = require('crypto'); const mongoose = start.mongoose; const Schema = mongoose.Schema; @@ -129,6 +129,27 @@ describe('SchemaUUID', function() { assert.equal(organization, undefined); }); + it('works with populate (gh-13267)', async function() { + const userSchema = new mongoose.Schema({ + _id: { type: 'UUID', default: () => randomUUID() }, + name: String, + createdBy: { + type: 'UUID', + ref: 'User' + } + }); + const User = db.model('User', userSchema); + + const u1 = await User.create({ name: 'admin' }); + const { _id } = await User.create({ name: 'created', createdBy: u1._id }); + + const pop = await User.findById(_id).populate('createdBy').orFail(); + assert.equal(pop.createdBy.name, 'admin'); + assert.equal(pop.createdBy._id.toString(), u1._id.toString()); + + await pop.save(); + }); + // the following are TODOs based on SchemaUUID.prototype.$conditionalHandlers which are not tested yet it('should work with $bits* operators'); it('should work with $all operator');