Mangosta: extendiendo esquemas

Actualmente tengo dos esquemas casi idénticos:

var userSchema = mongoose.Schema({ email: {type: String, unique: true, required: true, validate: emailValidator}, passwordHash: {type: String, required: true}, firstname: {type: String, validate: firstnameValidator}, lastname: {type: String, validate: lastnameValidator}, phone: {type: String, validate: phoneValidator}, }); 

Y

 var adminSchema = mongoose.Schema({ email: {type: String, unique: true, required: true, validate: emailValidator}, passwordHash: {type: String, required: true}, firstname: {type: String, validate: firstnameValidator, required: true}, lastname: {type: String, validate: lastnameValidator, required: true}, phone: {type: String, validate: phoneValidator, required: true}, }); 

Su única diferencia es en la validación: los usuarios no necesitan un nombre, apellido o teléfono. Sin embargo, los administradores deben tener estas propiedades definidas.

Desafortunadamente, el código anterior no es muy SECO, ya que son casi idénticos. Por lo tanto, me pregunto si es posible crear un adminSchema basado en el userSchema . P.ej:

 var adminSchema = mongoose.Schema(userSchema); adminSchema.change('firstname', {required: true}); adminSchema.change('lastname', {required: true}); adminSchema.change('phone', {required: true}); 

Obviamente eso es solo pseudocódigo. ¿Es algo como esto posible?

Otra pregunta muy similar es si es posible crear un nuevo esquema basado en otro y agregarle algunas propiedades más. Por ejemplo:

 var adminSchema = mongoose.Schema(userSchema); adminSchema.add(adminPower: Number); 

Algunas personas han sugerido en otros lugares usar utils.inherits para extender los esquemas. Otra forma sencilla sería configurar un objeto con configuraciones y crear esquemas a partir de él, de esta manera:

 var settings = { one: Number }; new Schema(settings); settings.two = Number; new Schema(settings); 

Aunque es un poco feo, ya que estás modificando el mismo objeto. También me gustaría poder extender complementos y métodos, etc. Por lo tanto, mi método preferido es el siguiente:

 function UserSchema (add) { var schema = new Schema({ someField: String }); if(add) { schema.add(add); } return schema; } var userSchema = UserSchema(); var adminSchema = UserSchema({ anotherField: String }); 

Lo que sucede para responder a su segunda pregunta es que sí, puede add() campos . Entonces, para modificar algunas propiedades del esquema, una versión modificada de la función anterior resolvería su problema:

 function UserSchema (add, nameAndPhoneIsRequired) { var schema = new Schema({ //... firstname: {type: String, validate: firstnameValidator, required: nameAndPhoneIsRequired}, lastname: {type: String, validate: lastnameValidator, required: nameAndPhoneIsRequired}, phone: {type: String, validate: phoneValidator, required: nameAndPhoneIsRequired}, }); if(add) { schema.add(add); } return schema; } 

Mongoose 3.8.1 ahora tiene soporte para Discriminadores. Un ejemplo, desde aquí: http://mongoosejs.com/docs/api.html#model_Model.discriminator

 function BaseSchema() { Schema.apply(this, arguments); this.add({ name: String, createdAt: Date }); } util.inherits(BaseSchema, Schema); var PersonSchema = new BaseSchema(); var BossSchema = new BaseSchema({ department: String }); var Person = mongoose.model('Person', PersonSchema); var Boss = Person.discriminator('Boss', BossSchema); 

Para agregar a esta discusión, también puede anular mongoose.Schema con una definición de esquema base personalizada. Para la compatibilidad del código, agregue la instrucción if que permita crear una instancia de un esquema sin new . Si bien esto puede ser conveniente, piénselo dos veces antes de hacerlo en un paquete público.

 var Schema = mongoose.Schema; var BaseSyncSchema = function(obj, options) { if (!(this instanceof BaseSyncSchema)) return new BaseSyncSchema(obj, options); Schema.apply(this, arguments); this.methods.update = function() { this.updated = new Date(); }; this.add({ updated: Date }); }; util.inherits(BaseSyncSchema, Schema); // Edit!!! // mongoose.Schema = BaseSyncSchema; <-- Does not work in mongoose 4 // Do this instead: Object.defineProperty(mongoose, "Schema", { value: BaseSyncSchema, writable: false }); 

Acabo de publicar un módulo de mongoose-super npm . Aunque hice algunas pruebas, todavía está en una etapa experimental. ¡Me interesa saber si funciona bien para las aplicaciones de mis compañeros usuarios de SO!

El módulo proporciona una función de conveniencia heredada () que devuelve un modelo Mongoose.js secundario basado en un modelo primario y una extensión de esquema secundario. También aumenta los modelos con un método super () para llamar a los métodos del modelo primario. Agregué esta funcionalidad porque es algo que extrañé en otras bibliotecas de extensión / herencia.

La función de conveniencia heredada simplemente usa el método discriminador .

Puede extender el esquema original # obj :

const AdminSchema = new mongoose.Schema ({}, Object.assign (UserSchema.obj, {…}))

Ejemplo:

 const mongoose = require('mongoose'); const UserSchema = new mongoose.Schema({ email: {type: String, unique: true, required: true}, passwordHash: {type: String, required: true}, firstname: {type: String}, lastname: {type: String}, phone: {type: String} }); // Extend function const extend = (Schema, obj) => ( new mongoose.Schema( Object.assign({}, Schema.obj, obj) ) ); // Usage: const AdminUserSchema = extend(UserSchema, { firstname: {type: String, required: true}, lastname: {type: String, required: true}, phone: {type: String, required: true} }); const User = mongoose.model('users', UserSchema); const AdminUser = mongoose.model('admins', AdminUserSchema); const john = new User({ email: 'user@site.com', passwordHash: 'bla-bla-bla', firstname: 'John' }); john.save(); const admin = new AdminUser({ email: 'admin@site.com', passwordHash: 'bla-bla-bla', firstname: 'Henry', lastname: 'Hardcore', // phone: '+555-5555-55' }); admin.save(); // Oops! Error 'phone' is required 

O use este módulo npm con el mismo enfoque:

 const extendSchema = require('mongoose-extend-schema'); // not 'mongoose-schema-extend' const UserSchema = new mongoose.Schema({ firstname: {type: String}, lastname: {type: String} }); const ClientSchema = extendSchema(UserSchema, { phone: {type: String, required: true} }); 

Compruebe el repository de github https://github.com/doasync/mongoose-extend-schema

No requería discriminación, ya que estaba tratando de extender los esquemas de sub-documentos que de todos modos se almacenan como parte de un documento principal.

Mi solución fue agregar un método “extendido” al esquema que es el esquema base, para que pueda utilizar el esquema base o generar un nuevo esquema basado en él.

Sigue el código ES6:

 'use strict'; //Dependencies let Schema = require('mongoose').Schema; //Schema generator function extendFooSchema(fields, _id = false) { //Extend default fields with given fields fields = Object.assign({ foo: String, bar: String, }, fields || {}); //Create schema let FooSchema = new Schema(fields, {_id}); //Add methods/options and whatnot FooSchema.methods.bar = function() { ... }; //Return return FooSchema; } //Create the base schema now let FooSchema = extendFooSchema(null, false); //Expose generator method FooSchema.extend = extendFooSchema; //Export schema module.exports = FooSchema; 

Ahora puede usar este esquema tal como está, o “extenderlo” según sea necesario:

 let BazSchema = FooSchema.extend({baz: Number}); 

La extensión en este caso crea una nueva definición de esquema.

Todas estas respuestas parecen innecesariamente complicadas, con funciones de ayuda de extensión o métodos extendidos aplicados a los esquemas o utilizando complementos / discriminadores. He utilizado la siguiente solución, que es simple, limpia y fácil de trabajar. Define un plano para el esquema base, y luego los esquemas reales se construyen usando el plano:

foo.blueprint.js

 module.exports = { schema: { foo: String, bar: Number, }, methods: { fooBar() { return 42; }, } }; 

foo.schema.js

 const {schema, methods} = require('./foo.blueprint'); const {Schema} = require('mongoose'); const FooSchema = new Schema(foo); Object.assign(FooSchema.methods, methods); module.exports = FooSchema; 

bar.schema.js

 const {schema, methods} = require('./foo.blueprint'); const {Schema} = require('mongoose'); const BarSchema = new Schema(Object.assign({}, schema, { bar: String, baz: Boolean, })); Object.assign(BarSchema.methods, methods); module.exports = BarSchema; 

Puede usar el anteproyecto para el esquema original tal como está, y al usar Object.assign puede extender el anteproyecto de la forma que desee para otros esquemas, sin modificar el mismo objeto.

    Intereting Posts