2013-08-19 15 views
15

Attualmente ho due schemi quasi identiche:Mongoose: estendere gli schemi

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}, 

}); 

E

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}, 

}); 

La loro unica differenza è nella convalida: gli utenti non hanno bisogno di un nome, cognome o telefono. Tuttavia, gli amministratori devono avere queste proprietà definite.

Sfortunatamente il codice precedente non è molto SECCO, in quanto sono quasi identici. Pertanto mi chiedo se sia possibile costruire uno adminSchema basato sullo userSchema. Es .:

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

Ovviamente questo è solo uno pseudocodice. È possibile qualcosa del genere?

Un'altra domanda molto simile è se è possibile creare un nuovo schema basato su un altro e aggiungerne altre. Ad esempio:

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

E la gente lo fa https://github.com/briankircho/mongoose-schema-extend vedere questo. – diproart

risposta

7

Alcune persone hanno in altri posti suggested using utils.inherits per estendere gli schemi. Un altro modo semplice sarebbe quella di impostare semplicemente un oggetto con le impostazioni e creare schemi da essa, in questo modo:

var settings = { 
    one: Number 
}; 

new Schema(settings); 

settings.two = Number; 
new Schema(settings); 

E 'un po' brutto, però, dal momento che si sta modificando lo stesso oggetto. Inoltre mi piacerebbe essere in grado di estendere i plugin e metodi ecc Così il mio metodo preferito è la seguente:

function UserSchema (add) { 
    var schema = new Schema({ 
    someField: String 
    }); 

    if(add) { 
    schema.add(add); 
    } 

    return schema; 
} 

var userSchema = UserSchema(); 
var adminSchema = UserSchema({ 
    anotherField: String 
}); 

che sembra rispondere alla tua seconda domanda che sì, è possibile add() campi. Quindi, per modificare alcune proprietà dello schema, una versione modificata della funzione di cui sopra avrebbe risolto il 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; 
} 
+0

Grazie. Sfortunatamente sembra che tutte queste soluzioni aggiungano un po 'di complessità. Anche il primo si traduce in una situazione in cui ora abbiamo un userSettings, un userSchema e un userModel. Sarebbe utile avere un approccio più pulito? Post scriptum Penso che ci sia un errore di battitura nel tuo secondo esempio. Dovrebbe essere 'var adminSchema = UserSchema() {..' – Tom

+0

[Questo thread] (https://groups.google.com/forum/#!topic/mongoose-orm/aeqGRRnpFvg) discute tale ereditazione. Non lo trovo più pulito (è tanto più codice ma più brutto) e alcune persone dicono che è buggy. –

24

Mongoose 3.8.1 ora ha il supporto per Discriminators. Un campione, da qui: 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); 
+1

E tutti gli altri lo fanno https://github.com/briankircho/mongoose-schema-extend vedere questo. – diproart

+11

@diproart È un po 'vecchio, ma se stai leggendo questo nel '15, non usare estensioni schema-mongoose.Ci sono molti problemi aperti e il plugin non è stato aggiornato dal '13. Vai per i "Discriminatori" di mangusta se non vuoi provare problemi ... – romualdr

+1

Ora è mantenuto attivamente, ma può essere una buona idea affidarsi comunque ai discriminatori –

2

Per aggiungere a questa discussione, si può anche ignorare mongoose.Schema con una definizione di schema di base personalizzato. Per la compatibilità del codice, aggiungere la dichiarazione if che consente di creare uno schema per l'istanza senza new. Mentre questo può essere conveniente, pensaci due volte prima di farlo in un pacchetto pubblico.

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

Ho appena pubblicato un mongoose-super npm module. Anche se ho fatto alcuni test, è ancora in una fase sperimentale. Mi interessa sapere se funziona bene per le applicazioni dei miei amici SO!

Il modulo fornisce una funzione di ereditarietà() che restituisce un modello Mongoose.js figlio basato su un modello padre e un'estensione dello schema figlio. Aumenta anche i modelli con un metodo super() per chiamare i metodi del modello principale. Ho aggiunto questa funzionalità perché è qualcosa che ho perso in altre librerie di estensione/eredità.

La funzione di ereditabilità utilizza semplicemente lo discriminator method.

0

Non ho richiesto alcuna discriminazione, poiché stavo cercando di estendere lo schema del documento secondario che sono comunque memorizzati come parte di un documento principale.

La mia soluzione era di aggiungere un metodo "estend" allo schema che è lo schema di base, in modo che sia possibile utilizzare lo schema di base stesso o generare un nuovo schema basato su di esso.

codice

ES6 segue:

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

È ora possibile utilizzare questo schema così com'è, o "estendere" in base alle esigenze:

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

estensione in questo caso crea una nuova definizione dello schema.

0

È possibile estendere l'originale schema # obj:

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

Esempio:

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 

Oppure utilizzare questo modulo NPM con lo stesso approccio:

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

Controllare il github repo https://github.com/doasync/mongoose-extend-schema

0

Tutte queste risposte sembrano piuttosto inutilmente complicato, con funzioni di supporto di estensione o estendere i metodi applicati allo schema del o utilizzando plugins/discriminatori. Ho usato la seguente soluzione invece che è semplice, pulita e facile da lavorare. Esso definisce un modello per lo schema di base, e quindi lo schema effettivi sono costruiti utilizzando lo schema:

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; 

è possibile utilizzare il modello per lo schema originale come è, e l'utilizzo di Object.assign puoi estendere il progetto in qualsiasi modo desideri per gli altri schemi, senza modificare lo stesso oggetto.

Problemi correlati