Keystone.js/mongoose虚域精益记录

Keystone.js / mongoose virtual fields lean record

我正在尝试为包含虚拟字段的 REST API 生成精简记录。

关于如何为Mongoose实现虚拟字段的官方文档:
http://mongoosejs.com/docs/guide.html

我的模型:

var keystone = require('keystone')
    , Types = keystone.Field.Types
    , list = new keystone.List('Vendors');

list.add({
    name : {
          first: {type : Types.Text}
        , last: {type : Types.Text}
    }
});

list.schema.virtual('name.full').get(function() {
    return this.name.first + ' ' + this.name.last;
});

list.register();

现在,让我们查询模型:

var keystone = require('keystone'),
    vendors = keystone.list('Vendors');

vendors.model.find()
    .exec(function(err, doc){
        console.log(doc)
    });

虚拟字段 name.full 不在此处:

[ { _id: 563acf280f2b2dfd4f59bcf3,
    __v: 0,
    name: { first: 'Walter', last: 'White' } }]

但是如果我们这样做:

vendors.model.find()
    .exec(function(err, doc){
        console.log(doc.name.full); // "Walter White"
    });

然后虚拟显示。

我想原因是当我执行 console.log(doc) 时,调用了 Mongoose document.toString() 方法,默认情况下不包括虚拟。很公平。可以理解。

要在您必须采用的任何转换方法中包含虚拟对象:

doc.toString({virtuals: true}) 
doc.toObject({virtuals: true}) 
doc.toJSON({virtuals: true}) 

但是,这包括我不希望我的 REST API 输出给我的用户的密钥:

{ _id: 563acf280f2b2dfd4f59bcf3,
  __v: 0,
  name: { first: 'Walter', last: 'White', full: 'Walter White' },
  _: { name: { last: [Object], first: [Object] } },
  list: 
   List {
     options: 
      { schema: [Object],
        noedit: false,
        nocreate: false,
        nodelete: false,
        autocreate: false,
        sortable: false,
        hidden: false,
        track: false,
        inherits: false,
        searchFields: '__name__',
        defaultSort: '__default__',
        defaultColumns: '__name__',
        label: 'Vendors' },
     key: 'Vendors',
     path: 'vendors',
     schema: 
      Schema {
        paths: [Object],
        subpaths: {},
        virtuals: [Object],
        nested: [Object],
        inherits: {},
        callQueue: [],
        _indexes: [],
        methods: [Object],
        statics: {},
        tree: [Object],
        _requiredpaths: [],
        discriminatorMapping: undefined,
        _indexedpaths: undefined,
        options: [Object] },
     schemaFields: [ [Object] ],
     uiElements: [ [Object], [Object] ],
     underscoreMethods: { name: [Object] },
     fields: { 'name.first': [Object], 'name.last': [Object] },
     fieldTypes: { text: true },
     relationships: {},
     mappings: 
      { name: null,
        createdBy: null,
        createdOn: null,
        modifiedBy: null,
        modifiedOn: null },
     model: 
      { [Function: model]
        base: [Object],
        modelName: 'Vendors',
        model: [Function: model],
        db: [Object],
        discriminators: undefined,
        schema: [Object],
        options: undefined,
        collection: [Object] } },
  id: '563acf280f2b2dfd4f59bcf3' }

我当然可以随时删除不需要的密钥,但这似乎不太正确:

vendors.model.findOne()
    .exec(function(err, doc){
        var c = doc.toObject({virtuals: true});
        delete c.list;
        delete c._;
        console.log(c)
    });

这产生了我需要的东西:

{ _id: 563acf280f2b2dfd4f59bcf3,
  __v: 0,
  name: { first: 'Walter', last: 'White', full: 'Walter White' },
  id: '563acf280f2b2dfd4f59bcf3' }

没有更好的方法来获得精益记录吗?

一个解决方案是使用像 Lodash (or Underscore) which allows you pick 这样的模块 属性 名称的白名单:

vendors.model.findOne()
    .exec(function(err, doc){
        var c = _.pick(doc, ['id', 'name.first', 'name.last', 'name.full']);
        console.log(c)
    });

鉴于您通过 REST API 提供此数据的用例,我认为明确定义 属性 名称的白名单更安全。您甚至可以在您的架构上定义一个虚拟 属性,其中 returns 预定义的白名单:

list.schema.virtual('whitelist').get(function() {
    return ['id', 'name.first', 'name.last', 'name.full'];
});

并在多个地方使用它,或者拥有不同版本的白名单,所有这些都在模型层进行管理。

我想你想要 select 方法..像这样:

vendors.model.findOne()
.select('_id __v name').
.exec(function(err, doc){
    console.log(c)
});

此外,我个人更喜欢在模式而不是文档上设置 virtuals: true,但我猜这取决于用例。