34

目前我有两个几乎相同的模式:

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

});

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

});

他们唯一的区别在于验证:用户不需要名字、姓氏或电话。但是,管理员必须定义这些属性。

不幸的是,上面的代码不是很干,因为它们几乎相同。因此我想知道是否有可能建立一个adminSchema基于userSchema. 例如:

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

显然这只是伪代码。这样的事情可能吗?

另一个非常相似的问题是,是否可以基于另一个模式创建新模式,并为其添加更多属性。例如:

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

10 回答 10

35

Mongoose 3.8.1 现在支持鉴别器。一个样本,来自这里: 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);
于 2013-11-25T20:54:36.533 回答
18

扩展猫鼬模式的最简单方法

import { model, Schema } from 'mongoose';

const ParentSchema = new Schema({
  fromParent: Boolean
});

const ChildSchema = new Schema({
  ...ParentSchema.obj,
  fromChild: Boolean // new properties come up here
});

export const Child = model('Child', ChildSchema);
于 2019-11-07T15:36:27.520 回答
17

有些人在其他地方建议使用 utils.inherits来扩展模式。另一种简单的方法是简单地设置一个带有设置的对象并从中创建模式,如下所示:

var settings = {
  one: Number
};

new Schema(settings);

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

不过这有点难看,因为您正在修改同一个对象。我也希望能够扩展插件和方法等。因此我的首选方法如下:

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

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

  return schema;
}

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

这恰好回答了你的第二个问题,是的,你可以add()fields。因此,要修改 Schema 的某些属性,上述函数的修改版本将解决您的问题:

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;
}
于 2013-08-19T15:53:26.993 回答
11

您可以扩展原始Schema#obj

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

例子:

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

或者用同样的方法使用这个 npm 模块:

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

检查 github 存储库https://github.com/doasync/mongoose-extend-schema

于 2017-07-28T16:12:49.400 回答
2

要添加到此讨论中,您还可以使用自定义基本架构定义覆盖 mongoose.Schema。为了代码兼容性,添加允许在没有new. 虽然这很方便,但在公共包中执行此操作之前请三思而后行。

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
});
于 2015-01-27T17:18:12.200 回答
2

您可以创建一个接受架构定义和可选架构选项的架构工厂函数,然后将传入的架构定义和选项与您想要跨架构共享的架构字段和选项合并。说明这一点的示例(假设您要共享或扩展启用了字段email和选项is_verified的模式timestamps):

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

SchemaFactory然后可以使用以下方法调用该函数:

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

现在UserSchemaandAdminSchema将包含emailandis_verified字段以及timestamps启用选项,以及您传递的架构字段和选项。

于 2019-01-20T19:06:17.187 回答
2

所有这些答案似乎都相当复杂,扩展辅助函数或扩展方法应用于模式或使用插件/鉴别器。我使用了以下解决方案,它简单、干净且易于使用。它为基本模式定义了一个蓝图,然后使用蓝图构建实际的模式:

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;

您可以按原样使用原始模式的蓝图,并且可以使用Object.assign您喜欢的任何方式扩展蓝图以用于其他模式,而无需修改相同的对象。

于 2017-10-06T09:29:59.423 回答
2

我刚刚发布了一个mongoose-super npm 模块。虽然我做了一些测试,但它仍处于实验阶段。我很想知道它是否适用于我的 SO 用户的应用程序!

该模块提供了一个 inherit() 便利函数,该函数基于父模型和子模式扩展返回子 Mongoose.js 模型。它还使用 super() 方法扩充模型以调用父模型方法。我添加了这个功能,因为它是我在其他扩展/继承库中错过的。

继承便利功能只是使用鉴别器方法

于 2016-04-18T14:39:25.897 回答
0

我不需要歧视,因为我试图扩展子文档模式,这些模式无论如何都存储为父文档的一部分。

我的解决方案是将“扩展”方法附加到作为基本模式的模式,以便您可以使用基本模式本身或基于它生成新模式。

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;

您现在可以按原样使用此架构,或根据需要“扩展”它:

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

在这种情况下,扩展创建了一个全新的模式定义。

于 2016-07-20T21:06:08.547 回答
0

我也尝试为我们的项目创建相同的东西,但是上面的答案和mongoose-extend-schema包无法容纳来自基本模式的钩子(方法、虚拟和查询助手)的副本。

为了解决这个问题,我做了以下

const BaseSchema = new mongoose.Schema({
  name: String,
  password: {type: String, required: true}
});
const AdminSchemaProperties = {
  name: String,
  password: {type: String, required: true}
};
function extendSchema(oldSchema,properties){
    let cloneSchema = oldSchema.clone()// deep copy the mongoose schema
    let schemaObj = {...cloneSchema.obj,...properties} //create new object with schema object and extended properties
    cloneSchema.obj = schemaObj 
    return cloneSchema
}
const AdminSchema = extendSchema(BaseSchema,AdminSchemaProperties)
export default AdminSchema
于 2021-11-08T08:17:25.413 回答