Keystone.js / mongoose虚拟领域精益记录

时间:2015-11-05 03:59:16

标签: node.js mongoose keystonejs

我正在尝试为包含虚拟字段的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' }

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

2 个答案:

答案 0 :(得分:1)

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

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

另外,我个人更喜欢在架构而不是文档上设置virtuals: true,但是我想这取决于用例。

答案 1 :(得分:0)

一种解决方案是使用像Lodash(或Underscore)这样的模块,它允许pick属性列表的白名单:

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

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

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

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