Mongoose: Advanced custom schema type for custom objects or value-objects

Created on 21 Jul 2016  路  7Comments  路  Source: Automattic/mongoose

I couldn't find any example of an _advanced_ custom schema type involving custom objects (or value-objects) in Mongoose >=4.4.

I already posted a question on StackOverflow without success: http://stackoverflow.com/questions/38094817/mongoose-advanced-custom-schema-object-type.

Resuming, how can I achieve that:

  1. Every time a new model is "hydrated" from db (mongoose _init_), the field will be an instance of my custom object and not a plain object. For this I understand it will use the SchemaType.prototype.cast function.
  2. Every time I will save my Model the custom type field should be
    encoded and stored in a custom way (as plain object, array, string or any BSON type).
  3. If I use model.toObject() it will recursively call the model.myfield.toObject() to have a full plain object representation of the document.

Most helpful comment

I added the toBSON() check in 1a93d1f. Here's a better example of how to do the polygon type you're looking at:

'use strict';

var assert = require('assert');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/gh4356');
mongoose.set('debug', true);

class Polygon {
  constructor(v) {
    this.v = v;
  }

  toBSON() {
    return this.v;
  }
}

class PolygonSchema extends mongoose.SchemaType {
  cast(v) {
    return new Polygon(v);
  }
}

mongoose.Schema.Types.Polygon = PolygonSchema;

const schema = new Schema({ test: Polygon });
const M = mongoose.model('Test', schema);

M.create({ test: 5 }).then(doc => {
  console.log('done', doc);
  console.log(doc.test.toBSON());
  process.exit(0);
});

Output:

$ node gh-4356.js 
(node:14709) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
Mongoose: tests.insert({ test: 5, _id: ObjectId("581e684cc4d5933975526934"), __v: 0 })
done { __v: 0, test: Polygon { v: 5 }, _id: 581e684cc4d5933975526934 }
5

All 7 comments

  1. Yep, cast()
  2. Give your Polygon class a toBSON() function https://mongodb.github.io/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#tobson-method
  3. It should already do this, no?

Thank you for the reply!

Give your Polygon class a toBSON() function https://mongodb.github.io/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#tobson-method

Yes, it work. But the debug logs of Mongoose seem to don't call it and shows it wrong (object instead of array):

Mongoose: shapes.update({ _id: ObjectId("...") }) { '$set': { bounds: { ... } } }
Trace: CALLING toBSON: [ /* array data */ ]
    at Polygon.toBSON (/var/www/test/polygons.js:136:47)
    at serializeInto (/var/www/node_modules/mongoose/test/node_modules/bson/lib/bson/parser/serializer.js:664:23)
    at serializeObject (/var/www/test/node_modules/bson/lib/bson/parser/serializer.js:280:18)
    at serializeInto (/var/www/test/node_modules/bson/lib/bson/parser/serializer.js:705:17)
    at serialize (/var/www/test/node_modules/bson/lib/bson/bson.js:47:27)
    ...
    at Collection.update (/var/www/test/node_modules/mongoose/node_modules/mongodb/lib/collection.js:1041:44)
    at NativeCollection.(anonymous function) [as update] (/var/www/test/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js:125:28)
    at model.Model.$__handleSave (/var/www/test/node_modules/mongoose/lib/model.js:161:23)
    at model.Model.$__save (/var/www/test/node_modules/mongoose/lib/model.js:185:9)
    at model.Model.save (/var/www/test/node_modules/mongoose/lib/model.js:283:15)
    at model._done (/var/www/test/node_modules/hooks-fixed/hooks.js:101:24)
    at _next (/var/www/test/node_modules/hooks-fixed/hooks.js:64:28)
    at fnWrapper (/var/www/test/node_modules/hooks-fixed/hooks.js:186:18)
    at model.Object.defineProperty.value.fn (/var/www/test/node_modules/mongoose/lib/schema.js:220:11)
    at _next (/var/www/test/node_modules/hooks-fixed/hooks.js:62:30)
    at fnWrapper (/var/www/test/node_modules/hooks-fixed/hooks.js:186:18)
    at /var/www/test/node_modules/mongoose/lib/schema.js:201:17
    at /var/www/test/node_modules/mongoose/node_modules/kareem/index.js:127:16
    at process._tickDomainCallback (node.js:381:11)

It should already do this, no?

No, I tried to call the toObject() on the main model and it don't call it on the custom type.

Ok will make sure that debug logs handle .toBSON() and .toObject() will work for the custom type, thanks for pointing this out

Thank you!

I added the toBSON() check in 1a93d1f. Here's a better example of how to do the polygon type you're looking at:

'use strict';

var assert = require('assert');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/gh4356');
mongoose.set('debug', true);

class Polygon {
  constructor(v) {
    this.v = v;
  }

  toBSON() {
    return this.v;
  }
}

class PolygonSchema extends mongoose.SchemaType {
  cast(v) {
    return new Polygon(v);
  }
}

mongoose.Schema.Types.Polygon = PolygonSchema;

const schema = new Schema({ test: Polygon });
const M = mongoose.model('Test', schema);

M.create({ test: 5 }).then(doc => {
  console.log('done', doc);
  console.log(doc.test.toBSON());
  process.exit(0);
});

Output:

$ node gh-4356.js 
(node:14709) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
Mongoose: tests.insert({ test: 5, _id: ObjectId("581e684cc4d5933975526934"), __v: 0 })
done { __v: 0, test: Polygon { v: 5 }, _id: 581e684cc4d5933975526934 }
5

I couldn't find any example for storing a composite object in MongoDB using Mongoose and retrieving the inner object with all the methods without typecasting.

I already posted a question on StackOverflow:
https://stackoverflow.com/questions/48960604/how-to-store-composite-object-in-mongodb-using-mongoose

Please help.

@NikhilAshodariya I opened up a separate issue for this #6174.

Was this page helpful?
0 / 5 - 0 ratings