Actuellement, j'ai deux schémas presque identiques:
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},
});
Et
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},
});
Leur seule différence réside dans la validation: les utilisateurs n'ont pas besoin de prénom, nom ou téléphone. Les administrateurs doivent toutefois avoir ces propriétés définies.
Malheureusement, le code ci-dessus n'est pas très sec, car ils sont presque identiques. Par conséquent, je me demande s’il est possible de construire une adminSchema
basée sur la userSchema
. Par exemple.:
var adminSchema = mongoose.Schema(userSchema);
adminSchema.change('firstname', {required: true});
adminSchema.change('lastname', {required: true});
adminSchema.change('phone', {required: true});
Évidemment, c'est juste un pseudocode. Est-ce que quelque chose comme ça est possible?
Une autre question très similaire est de savoir s'il est possible de créer un nouveau schéma basé sur un autre et d'y ajouter des propriétés supplémentaires. Par exemple:
var adminSchema = mongoose.Schema(userSchema);
adminSchema.add(adminPower: Number);
Certaines personnes ont dans d'autres endroits suggéré d'utiliser utils.inherits pour étendre les schémas. Une autre méthode simple consiste à configurer simplement un objet avec des paramètres et à en créer des schémas, comme suit:
var settings = {
one: Number
};
new Schema(settings);
settings.two = Number;
new Schema(settings);
C'est un peu moche cependant, puisque vous modifiez le même objet. J'aimerais aussi pouvoir étendre les plugins, les méthodes, etc. Ainsi, ma méthode préférée est la suivante:
function UserSchema (add) {
var schema = new Schema({
someField: String
});
if(add) {
schema.add(add);
}
return schema;
}
var userSchema = UserSchema();
var adminSchema = UserSchema({
anotherField: String
});
Ce qui arrive à répondre à votre deuxième question que oui, vous pouvez add()
champs. Donc, pour modifier certaines propriétés du schéma, une version modifiée de la fonction ci-dessus résoudrait votre problème:
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 prend désormais en charge les discriminateurs. Un exemple, à partir d'ici: 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);
Vous pouvez étendre l'original Schema # obj:
const AdminSchema = new mongoose.Schema ({}, Object.assign (UserSchema.obj, {...}))
Exemple:
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: '[email protected]',
passwordHash: 'bla-bla-bla',
firstname: 'John'
});
john.save();
const admin = new AdminUser({
email: '[email protected]',
passwordHash: 'bla-bla-bla',
firstname: 'Henry',
lastname: 'Hardcore',
// phone: '+555-5555-55'
});
admin.save();
// Oops! Error 'phone' is required
Ou utilisez ce module npm avec la même approche:
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}
});
Vérifiez le dépôt github https://github.com/doasync/mongoose-extend-schema
Je viens de publier un module mongoose-super npm . Bien que j'ai fait quelques essais, il en est encore au stade expérimental. Je suis intéressé de savoir si cela fonctionne bien pour les applications de mes collègues SO utilisateurs!
Le module fournit une fonction de commodité inherit () qui renvoie un modèle Mongoose.js enfant basé sur un modèle parent et une extension de schéma enfant. Il augmente également les modèles avec une méthode super () pour appeler des méthodes de modèle parent. J'ai ajouté cette fonctionnalité parce que c'est quelque chose qui m'a échappé dans d'autres bibliothèques d'extension/d'héritage.
La fonction de commodité héritée utilise simplement la méthode discriminator .
Pour ajouter à cette discussion, vous pouvez également remplacer mongoose.Schema par une définition de schéma de base personnalisée. Pour la compatibilité du code, ajoutez l’instruction if permettant à un schéma d’être instancié sans new
. Bien que cela puisse être pratique, réfléchissez-y à deux fois avant de le faire dans un paquet public.
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
});
Toutes ces réponses semblent plutôt inutilement compliquées, avec des fonctions d'assistance d'extension ou des méthodes d'extension appliquées au schéma ou à l'aide de plugins/discriminateurs. J'ai utilisé à la place la solution suivante qui est simple, propre et sur laquelle il est facile de travailler. Il définit un schéma directeur pour le schéma de base, puis les schémas réels sont générés à l'aide du schéma directeur:
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;
Vous pouvez utiliser le plan directeur pour le schéma d'origine tel quel et utiliser Object.assign
pour étendre le plan à votre guise pour d'autres schémas, sans modifier le même objet.
Vous pouvez créer une fonction Usine de schéma acceptant une définition de schéma et des options de schéma optional, qui fusionne ensuite la définition et les options de schéma passées avec les champs et options de schéma que vous souhaitez partager entre schémas .. ceci (en supposant que vous souhaitiez partager ou étendre un schéma pour lequel les champs email
et is_verified
et l'option timestamps
sont activés):
// schemaFactory.js
const mongoose = require('mongoose');
const SchemaFactory = (schemaDefinition, schemaOptions) => {
return new mongoose.Schema({
{
email: {type: String, required: true},
is_verified: {type: Boolean, default: false},
// spread/merge passed in schema definition
...schemaDefinition
}
}, {
timestamps: true,
// spread/merge passed in schema options
...schemaOptions
})
}
module.exports = SchemaFactory;
La fonction SchemaFactory
peut ensuite être appelée avec:
// schemas.js
const SchemaFactory = require("./schemaFactory.js")
const UserSchema = SchemaFactory({
first_name: String,
password: {type: String, required: true}
});
const AdminSchema = SchemaFactory({
role: {type: String, required: true}
}, {
// we can pass in schema options to the Schema Factory
strict: false
});
Désormais, les variables UserSchema
et AdminSchema
contiendront à la fois les champs email
et is_verified
et l'activation de l'option timestamps
, ainsi que les champs de schéma et les options que vous aurez transmis.
Je n'avais pas besoin de discrimination, car j'essayais de toute façon d'étendre les schémas de sous-document qui sont stockés en tant que partie d'un document parent.
Ma solution consistait à ajouter une méthode "extend" au schéma qui est le schéma de base, afin que vous puissiez utiliser le schéma de base lui-même ou générer un nouveau schéma basé sur celui-ci.
Le code ES6 suit:
'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;
Vous pouvez maintenant utiliser ce schéma tel quel ou le "prolonger" au besoin:
let BazSchema = FooSchema.extend({baz: Number});
Dans ce cas, l’extension crée une nouvelle définition de schéma.