node.js Mongoose:扩展模式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18317284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:21:48  来源:igfitidea点击:

Mongoose: extending schemas

node.jsschemamongoose

提问by Tom

Currently I have two almost identical schemas:

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

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

});

And

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

});

Their only difference is in validation: Users do not need a firstname, lastname or phone. Admins however must have these properties defined.

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

Unfortunately the above code is not very DRY, as they're almost identical. Therefore I am wondering if it is possible to build an adminSchemabased on the userSchema. E.g.:

不幸的是,上面的代码不是很 DRY,因为它们几乎相同。因此,我想知道是否可以adminSchema基于userSchema. 例如:

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

Obviously that's just pseudocode. Is something like this possible?

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

Another very similar question is if it is possible to create a new schema based on another, and add some more properties to it. For example:

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

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

采纳答案by Andreas Hultgren

Some people have in other places suggested using utils.inheritsto extend schemas. Another simple way would be to simply set up an object with settings and create Schemas from it, like so:

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

var settings = {
  one: Number
};

new Schema(settings);

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

It's a bit ugly though, since you're modifying the same object. Also I'd like to be able to extend plugins and methods etc. Thus my preferred method is the following:

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

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

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

  return schema;
}

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

Which happens to answer your second question that yes, you can add()fields. So to modify some properties of the Schema, a modified version of the above function would solve your problem:

这恰好回答了您的第二个问题,是的,您可以使用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;
}

回答by regretoverflow

Mongoose 3.8.1 now has support for Discriminators. A sample, from here: http://mongoosejs.com/docs/api.html#model_Model.discriminator

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

回答by Do Async

You can extend the original Schema#obj:

您可以扩展原始Schema#obj

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

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

Example:

例子:

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

Or use this npm module with the same approach:

或者以相同的方法使用这个 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}
});

Check the github repo https://github.com/doasync/mongoose-extend-schema

检查github repo https://github.com/doasync/mongoose-extend-schema

回答by nakhodkin

The simplest way for extending mongoose schema

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

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

回答by Adam Lockhart

To add to this discussion, you can also override mongoose.Schema with a custom base schema definition. For code compatibility, add the if statement that allows a Schema to be instantiated without new. While this can be convenient, think twice before doing this in a public package.

要添加到此讨论中,您还可以使用自定义基本架构定义覆盖 mongoose.Schema。为了代码兼容性,添加 if 语句允许在没有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
});

回答by Visionscaper

I just published a mongoose-super npm module. Although I did some testing, it is still in an experimental stage. I'm interested to know if it works well for the applications of my fellow SO users!

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

The module provides a inherit() convenience function that returns a child Mongoose.js model based on a parent model and a child schema extension. It also augments models with a super() method to call parent model methods. I added this functionality because it is something I missed in other extension/inheritance libraries.

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

The inherit convenience function simply uses the discriminator method.

继承便利函数仅使用鉴别器方法

回答by Adam Reis

All of these answers seem rather needlessly complicated, with extension helper functions or extend methods applied to the schema's or using plugins/discriminators. I've used the following solution instead which is simple, clean and easy to work with. It defines a blueprint for the base schema, and then the actual schema's are built using the blueprint:

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

foo.blueprint.js

foo.blueprint.js

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

foo.schema.js

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

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;

You can use the blueprint for the original schema as is, and using Object.assignyou can extend the blueprint in any way you like for other schema's, without modifying the same object.

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

回答by darthchudi

You can create a Schema Factory function that accepts a Schema definition and optionalschema options, which then merges the passed in Schema definition and options with the Schema fields and options which you want to share across schemas. Example illustrating this (assuming you want to share or extend a schema that has the fields emailand is_verifiedand the timestampsoption enabled):

您可以创建一个接受架构定义和可选架构选项的架构工厂函数,然后将传入的架构定义和选项与您想要跨架构共享的架构字段和选项合并。举例说明(假设你想分享或扩展有领域的模式emailis_verifiedtimestamps启用选项):

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

The SchemaFactoryfunction can then be called with:

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

Now the UserSchemaand AdminSchemawill contain both the emailand is_verifiedfield as well as have the timestampsoption enabled, along with the schema fields and options you pass along.

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

回答by Adam Reis

I didn't require discrimination, as I was trying to extend sub document schema's which are stored as a part of a parent document anyway.

我不需要歧视,因为我试图扩展作为父文档的一部分存储的子文档模式。

My solution was to append an "extend" method to the schema that is the base schema, so that you can either use the base schema itself or generate a new schema based on it.

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

ES6 code follows:

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;

You can now use this schema as is, or "extend" it as needed:

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

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

Extension in this case creates a brand new Schema definition.

在这种情况下,扩展会创建一个全新的架构定义。