简体   繁体   中英

Schemas in external module not working in Node.js

I'm having a massive headache try to share some common schema definitions via a module to all the other modules in my code base.

I have a myproj_schemas module that contains these two schemas:

var mongoose = require('mongoose'),
    util = require("util"),
    Schema = mongoose.Schema;

var BaseProfileSchema = function() {
    Schema.apply(this, arguments);

    this.add({
        _user: {type: Schema.Types.ObjectId, ref: 'User', required: true},
        name: {type: String, required: true},
        bio: {type: String, required: true},
        pictureLink: String
    });

};
util.inherits(BaseProfileSchema, Schema);

module.exports = BaseProfileSchema;

And

var mongoose = require('mongoose'),
    BaseProfileSchema = require('./base_profile_schema.js'),
    Schema = mongoose.Schema;

var entSchemaAdditions = {
    mentors: {type: Schema.Types.ObjectId, ref: 'Mentor'}
};


var entrepreneurSchema = new BaseProfileSchema(entSchemaAdditions);

module.exports = entrepreneurSchema;

Mentors is also defined in another file.

My unit tests for these both work in the schemas module.

When I npm install this module and try to create using

Entrepreneur = db.model('Entrepreneur', entrepreneurSchema),

I get the following error:

TypeError: Undefined type at paths.mentors Did you try nesting Schemas? You can only nest using refs or arrays.

If I use the same code in my local module, then no problem. If I reference the schema file directly in the require (eg require('../node_modules/myproj_schemas/models/ent_schema') then I get the error.

I'm fairly sure it wasn't breaking like this earlier, but I've backed out all the changes and it is still not working.

I'm drawing a complete blank, and any suggestions would be gratefully received.

EDIT:

I've created a new Schemas module. It has one Schema:

var mongoose = require('mongoose');

var userSchema = new mongoose.Schema({
    email: String
});

module.exports = userSchema;

This also fails when packaged up in a module and npm install'd to other modules.

Running on OS X

Personally I created separate "common" project with an init method to register all of the models with mongodb, and call the init method in the app.js file of any apps that need access to the models.

  1. Create a shared project - Create a new node project, following the standard process.
  2. package.json - In the shared project, set your package.json file to contain the following entry:

     "main": "index.js" 
  3. Add a model - Create a new models folder within your shared project, to contain all of your mongoose schemas and plugins. Add your userSchema file to the models folder and name it user.js .

     var mongoose = require('mongoose'); var userSchema = new mongoose.Schema({ email: String }); module.exports = mongoose.model('User', userSchema); 
  4. index.js - Then in the project's root index.js file create a shared object that can be used by your apps, exposing the models and an init method. There's many ways to code this, but here's how I'm doing it:

     function Common() { //empty array to hold mongoose Schemas this.models = {}; } Common.prototype.init = function(mongoose) { mongoose.connect('your mongodb connection string goes here'); require('./models/user'); //add more model references here //This is just to make referencing the models easier within your apps, so you don't have to use strings. The model name you use here must match the name you used in the schema file this.models = { user: mongoose.model('User') } } var common = new Common(); module.exports = common; 
  5. Reference your common project - However you want to reference your shared project, add the reference to the shared project in the package.json file within your app and give it a name of common . Personally I used GitHub to store the project and referenced the repository path. Since my repository was private I had to use a key in the path, which is covered on the GitHub support site.
  6. Init the models in your app - In the start script for your app (let's assume it's app.js for this example) add the reference to your common project and call the init method to connect to the mongodb server and register the models.

     //at the top, near your other module dependencies var mongoose = require('mongoose') , common = require('common'); common.init(mongoose); 
  7. Use the model anywhere in your app - Now that mongoose has the connection pool established and the models have been registered, you can use the models is any of the classes within your app. For example, say you have a page that displays information about a user you could do it like this (untested code, just wrote this for an example):

     var common = require('common'); app.get('/user-profile/:id', function(req, res) { common.models.user.findById(req.params.id, function(err, user) { if (err) console.log(err.message); //do something else to handle the error else res.render('user-profile', {model: {user: user}}); }); }); 

EDIT Sorry, I didn't see the line where you were inheriting one schema from another. As one of the other answers stated, mongoose already offers the concept of a plugin . In your example above, you would do this:

In your common module, under '/models/base-profile-plugin.js'

module.exports = exports = function baseProfilePlugin(schema, options){

    //These paths will be added to any schema that uses this plugin
    schema.add({
        _user: {type: Schema.Types.ObjectId, ref: 'User', required: true},
        name: {type: String, required: true},
        bio: {type: String, required: true},
        pictureLink: String
    });

    //you can also add static or instance methods or shared getter/setter handling logic here. See the plugin documentation on the mongoose website.
}

In your common module, under '/models/entrepreneur.js

var mongoose    = require('mongoose')
  , basePlugin  = require('./base-profile-plugin.js');

var entrepreneurSchema   = new mongoose.Schema({
    mentors: {type: Schema.Types.ObjectId, ref: 'Mentor'}
});

entrepreneurSchema.plugin(basePlugin);

module.exports = mongoose.model('Entrepreneur', entrepreneurSchema);

你为什么不为这样的公共架构创建一个mongoose插件https://github.com/Keeguon/mongoose-behaviors/blob/master/lib/timestampable.js

what you are looking for basically is for schema inheritance, there's a project named mongoose extend that practically solves your issue, you can either decide if you want to implement it or take a look to the code and make your own.

just install it usng npm

$ npm install mongoose-schema-extend

this is how it works:

var mongoose = require('mongoose'),
    extend = require('mongoose-schema-extend');
var Schema = mongoose.Schema;

var PersonSchema = new Schema({
  name : String
}, { collection : 'users' });

var EmployeeSchema = PersonSchema.extend({
  department : String
});

var Person = mongoose.model('Person', PersonSchema),
    Employee = mongoose.model('Employee', EmployeeSchema);

var Brian = new Employee({
  name : 'Brian Kirchoff',
  department : 'Engineering'
});

regards

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM