search.png
关于我
menu.png
mongoose自定义schema type之路

前言

mongoose是nodejs使用非常方便的一个操作mongodb的一个库,目前star已经过万。
但是其支持的类型很少,只有:

  • String
  • Number
  • Date
  • Buffer
  • Boolean
  • Mixed
  • ObjectId
  • Array
  • Decimal128

这明显是不方便使用的,所以官方也提供了自定义type的接口。并且github上也有别人写好的插件可以使用,如long、Int32等

为了以后少写代码,提高代码复用,所以我阅读了Int32源码,学习怎么自定义schema类型。


Int32

源码:

'use strict';

const mongoose = require('mongoose');

const INT32_MAX = 0x7FFFFFFF;    // 设定int32的范围
const INT32_MIN = -0x80000000;

class Int32 extends mongoose.SchemaType {    // 继承mongoose.SchemaType
  constructor(key, options) {
    super(key, options, 'Int32');    // 调用父类构造器
  }

  /**
   * Cast the given value to something that MongoDB will store as int32
   * 将给定的值转换为MongoDB存储的int 32。
   * 
   * @param {any} val
   * @return {Number}
   */

  cast(val) {
    var _val = Number(val);
    if (isNaN(_val)) {    // NaN时,表示传入的val不是Number类型
      throw new mongoose.SchemaType.CastError('Int32',    //抛出错误
        val + ' is not a number');
    }
    _val = Math.round(_val);    // 四舍五入
    if (_val < INT32_MIN || _val > INT32_MAX) {    // 判断范围
      throw new mongoose.SchemaType.CastError('Int32', val +
        ' is outside of the range of valid BSON int32s: ' + INT32_MAX + ' - ' +
        INT32_MIN);
    }
    return _val;
  }
}

Int32.prototype.$conditionalHandlers =
  mongoose.Schema.Types.Number.prototype.$conditionalHandlers;
// 这句应该是继承了Number类型的$conditionalHandlers

Int32.INT32_BSON_TYPE = 16;
Int32.INT32_MAX = INT32_MAX;
Int32.INT32_MIN = INT32_MIN;

Int32.instance = 'Int32';    // 设置类型
mongoose.Schema.Types.Int32 = Int32;    // 把Int32挂载到Schema的Types上去
module.exports = Int32;    // export

大概懂了

  1. 实现一个类继承mongoose.SchemaType
  2. 实现构造器调用父类构造器
  3. 实现cast(val)函数,这个函数用来判断类型是否正确,并对数据进行处理,数据处理错误应该抛出父类的错误CastError
  4. 挂载父类的prototype.$conditionalHandlers
  5. 设置一些参数(可选)
  6. 设置instance
  7. 挂载到mongoose.Schema.Types
  8. export出去

实现一个自定义的Int类型

这里结合了eggjs定制了:

'use strict';

module.exports = app => {
  const { mongoose } = app;

  class Int extends mongoose.SchemaType {    // 继承mongoose.SchemaType
    constructor(key, options) {
      super(key, options, 'Int');    // 调用父类构造器
    }

    cast(val) {
      var _val = Number(val);
      if (isNaN(_val)) {
        throw new mongoose.SchemaType.CastError('Int', val + ' is not a number');
      }

      if (! Number.isInteger(_val)) {
        throw new mongoose.SchemaType.CastError('Int', val + ' is not int');
      }
      return _val;
    }
  }

  Int.prototype.$conditionalHandlers =
    mongoose.Schema.Types.Number.prototype.$conditionalHandlers;

  Int.instance = 'Int';
  mongoose.Schema.Types.Int = Int;
};

输了个float类型,成功报错:

在这里插入图片描述
在这里插入图片描述

搞定。

什么时候自定义类型?

个人觉得如果这个类型不满足原子性就没必要自定义类型,因为哪怕你定义了也用不到多少次。
如果一个正则表达式就能解决,那么推荐使用match来匹配正则表达式,也别用自定义类型。
match: /http:\/\/.*/
如果写两三行validate能搞定,那也没必要自定义类型。

validate: (data)=>{ // 自定义数据校验方法
     return data.length >= 10;
}

如果滥用自定义类型会导致代码太过复杂,不易于维护。


有什么问题欢迎留言评论,看到会尽量回复的

版权声明

知识共享许可协议 本文章由作者“衡于墨”创作,转载请注明出处,未经允许禁止用于商业用途

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
发布时间:2019年07月06日 14:31:18

评论区#

还没有评论哦,期待您的评论!

关闭特效