动态定义猫鼬模型方法

Defining mongoose Model Methods Dynamically

我的情况是一个函数可以根据情况按不同的字段进行搜索。

它 returns 相同的数据集,它只是按不同的字段搜索:userId 或 tagId。因此,在我的代码中我有这样的东西:

var findByMethod;

if (searchBy === 'userId') {
    findByMethod = UserArticleModel.findByUser;
}

else {
    findByMethod = UserArticleModel.findByTag;
}

findByMethod(idToSearch, function (err, articles) {…});

findByUserfindByTag 是在 UserArticleModel.js

中定义的静态方法

UserArticleModel.js

var mongoose = require('mongoose');

var userArticleSchema = new mongoose.Schema({
        …
    }
});

userArticleSchema.statics.findByUser = function (userId, callback) {
    this.find({userId: userId}, function () {…});
};

userArticleSchema.statics.findByTag = function (tagId, callback) {…};

module.exports = mongoose.model('UserArticle', userArticleSchema);

回到我的控制器,当我这样做时:

UserArticleModel.findByTag(idToSearch, function (err, articles) {…});

一切顺利,一切顺利。但是当我通过我的变量动态调用方法时:

findByMethod(idToSearch, function (err, articles) {…});

节点 returns 出现错误:

DOMAINE ERROR CAUGHT: TypeError: Object #<Object> has no method 'find'

我怀疑 this 没有被绑定到正确的范围,但我真的不明白为什么 findByMethod === UserArticleModel.findByUser // true

我认为你让这件事变得比它需要的更复杂了。尽管 "too literally" 遵循已记录的 API 示例并从本质上思考 "this is how I need to hardcode this, because the docs say this is how you do it".

很容易掉入陷阱

JavaScript 对象是 "Objects",因此仅分配 "named" 实际上只是对象属性的静态方法只是 "looping" 定义的基本过程"schema paths" 来自已经定义的 "Object" 并为您想要的 "findByFieldName" 方法设置属性。

它只是 "assigning named properties",没有比它更晦涩或复杂甚至 "terse" 的了。

如果 "sounds like a mouthful" 那么迭代对象属性的实际过程和 "setting other properties" 在整个对象结构中与之相关的过程实际上并不像你可能会想。

举个简单的例子:

var async = require('async'),
    pascal = require('to-pascal-case'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var testSchema = new Schema({
  fieldA: String,
  fieldB: Number
});

function setStatics(schema) {
  Object.keys(schema.paths).filter(function(key) {
    return key != '_id';
  }).forEach(function(key) {
    schema.statics['findBy' + pascal(key)] = function(arg,callback) {
      var query = {};
      query[key] = arg;
      this.findOne(query,callback);
    };
  });
};

// Set up findByFieldName other than _id
setStatics(testSchema);

var Test = mongoose.model( 'Test', testSchema, "test" );

mongoose.connect('mongodb://localhost/test');

async.series(
  [
    function(callback) {
      Test.remove({},callback);
    },
    function(callback) {
      Test.create([
        { "fieldA": "a", "fieldB": 1 },
        { "fieldA": "b", "fieldB": 2 }
      ],callback);
    },
    function(callback) {
      Test.findByFieldA("a",function(err,doc) {
        console.log(doc);
        callback(err);
      });
    },
    function(callback) {
      Test.findByFieldB(2,function(err,doc) {
        console.log(doc);
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

这证明它们通过 "testing them" 工作,输出:

{ _id: 55f2ae1b7d8315f40b1a2b77, fieldA: 'a', fieldB: 1, __v: 0 }
{ _id: 55f2ae1b7d8315f40b1a2b78, fieldA: 'b', fieldB: 2, __v: 0 }

仅此而已。

当然,对于像 "Arrays" 这样的领域,您想要更多地参与,但这是作为列表的基本前提,您可以自己尝试(或一般社区的自我)。

我还可以注意到已经有一些东西,例如 Bluebird 通过它自己的 .promisifyAll() 调用与对象交互以在对象上设置新的 "named methods"以类似的方式。或者至少原则上应该是相似的,因为我没有真正看过那个代码。