像MYSQL同样验证MongoDB字段

MongoDB在某些时候被人诟病,说字段过于灵活,数据结构不稳定,从而被排除在外。mongodb

例如,用户表,大部分状况下,都是固定格式:数据库

| id | name | age | province |
复制代码

类型是:express

| ID | CHAR | INT | VARTXT |
复制代码

若是使用MongoDB,由于是文档类型,字段增长修改灵活,这就会有一个问题,若是不当心被人insert了一个数据:json

{
  name: "Jerry",
  age: "18",
  province: "Guangdong"
}
复制代码

你们看这个 age: "18",特别在当前NodeJS流行的环境下,WEB提交的表但都是string,就有可能出现。固然解决办法也是有的:markdown

  • 应用层面验证——就是本身写验证,例如Koa/expressjs中间,或koa-validate;
  • 使用Mongoose schema验证/转换;

使用Mongoose Schema:

const mongoose = require('mongoose')

var UserSchema = mongoose.Schema({
  _id: false,
  name: {
    type: String,
    required: [true, "user name is required"]
  },
  age: {
    type: Number
  },
  province: {
    type: String
  }
});
复制代码

使用mongoose的好处是,不但能够验证,还能够转换类型,除此以外,还能够更贴合面向对象的方式使用数据,因此十分推荐引入Mongoose库。数据结构

那么问题来了,若是同一个数据库的使用这,不是NodeJS环境呢?你们都知道在微服务中,有可能使用Java来实现各类数据的处理。因此最好的方式就是像MYSQL那样在数据库里验证。koa

使用jsonSchema验证MongoDB字段

在MongoDB3.6后,支持jsonSchema的数据验证,经过以下方式能够配置,也能够修改。async

//建表的时候配置
db.createCollection( <collection>, { validator: { $jsonSchema: <schema> } } )
//随时修改
db.runCommand( { collMod: <collection>, validator:{ $jsonSchema: <schema> } } )
复制代码

配置的方式就是上面的2个语句,很是简单,使用的是标准JSON Schema规范。mongoose

JSON Schema规范内容,这里不铺开阐述,你们能够本身搜索。固然mongoDB也有本身的一些字段类型,这一点和Javascript有点不同。例如int,那么10是合法的,但10.1是不合法的。还有一些细则,例以下面的year。微服务

year: {
    bsonType: "int",
    minimum: 2017,
    maximum: 3017,
    description: "must be an integer in [ 2017, 3017 ] and is required"
},
复制代码

这里仅作介绍,具体能够到官网去看,也不复杂。

效果

在设置好验证后,只要验证不经过,就会报错:

WriteResult({
	"nInserted" : 0,
	"writeError" : {
		"code" : 121,
		"errmsg" : "Document failed validation"
	}
})
复制代码

在NodeJS里,能够经过代码来设置,只是若是使用mongoose,由于不提供mongodb connector内置对象暴露,须要咱们稍稍处理一下便可。

const MongoClient = require("mongodb").MongoClient;
const URL = 'mongodb://@127.0.0.1:27017/mydb'

function connect () {
  return new Promise((resolve, reject) => {
    MongoClient.connect(URL, { useNewUrlParser: true }, (err, database) => {
      if (err) {
        console.log(`Unable to connect to the databse: ${err}`);
        reject(err)
      } else {
        console.log('Connected to the database');
        resolve(database)
      }
    });
  })
}
async function init () {
  let connection = await connect()
  let db = connection.db()
  //这里写jsonSchema
  await db.createCollection(<collection>, { validator: { $jsonSchema: <schema> } })
  //这里修改jsonSchema
  await db.runCommand( { collMod: <collection>, validator:{ $jsonSchema: <schema> } } )
}
init().then(() => {})
复制代码

咱们写一些测试用例:

const chai = require('chai')
const { expect } = chai

const URL = 'mongodb://@127.0.0.1:27017/mydb'
const MongoClient = require("mongodb").MongoClient;

function connect () {
  return new Promise((resolve, reject) => {
    MongoClient.connect(URL, { useNewUrlParser: true }, (err, database) => {
      if (err) {
        console.log(`Unable to connect to the databse: ${err}`);
        reject(err)
      } else {
        console.log('Connected to the database');
        resolve(database)
      }
    });
  })
}

describe('mongoose', function () {
  it('createCollection', async () => {
    let connection = await connect()
    let db = connection.db()
    await db.createCollection('students', {
      validator: {
        $jsonSchema: {
          required: ["name"],
          properties: {
            name: {
              bsonType: "string",
              description: "must be a string and is required"
            },
            age: {
              bsonType: "int"
            },
            score: {
              bsonType: "number"
            },
            height: {
              bsonType: "double"
            },
            address: {
              bsonType: "object",
              required: ["zipcode"],
              properties: {
                "street": { bsonType: "string" },
                "zipcode": { bsonType: "string" }
              }
            }
          }
        }
      }
    })

  })
  it('jsonSchemaMod', async () => {
    let connection = await connect()
    let db = connection.db()
    let rs = await db.command({
      collMod: 'students',
      validator: {
        $jsonSchema: {
          required: ["name"],
          properties: {
            name: {
              bsonType: "string",
              description: "must be a string and is required"
            },
            age: {
              bsonType: "int"
            },
            score: {
              bsonType: "number"
            },
            height: {
              bsonType: "double"
            },
            address: {
              bsonType: "object",
              required: ["zipcode"],
              properties: {
                "street": { bsonType: "string" },
                "zipcode": { bsonType: "string" }
              }
            }
          }
        }
      }
    })
    expect(rs).eql({ ok: 1 })
    // console.log('rs:', rs)
  })
  it('jsonSchemaModLess', async () => {
    let connection = await connect()
    let db = connection.db()
    let rs = await db.command({
      collMod: 'students',
      validator: {
        $jsonSchema: {
          properties: {
            name: {
              bsonType: "string",
              description: "must be a string and is required"
            },
            address: {
              bsonType: "object",
              properties: {
                "street": { bsonType: "string" },
                "zipcode": { bsonType: "string" }
              }
            }
          }
        }
      }
    })
    expect(rs).eql({ ok: 1 })
  })
  it('insert', async () => {
    let connection = await connect()
    let db = connection.db()

    let rs = await db.collection('students').insertOne({
      name: 'tom',
      age: 10,// 10.1 若是非整型,会失败
      score: 100, //ok
      height: 180, //ok
      address: {
        zipcode: 'code' //zipcode empty fail
      },
      otherField: 'an other field'
    })

    expect(rs.result).include({ ok: 1 })
  })
  it('emptyName', async () => {
    let connection = await connect()
    let db = connection.db()
    // 若是name是必须,则会失败
    let rs = await db.collection('students').insertOne({
      age: 10
    })
    // console.log(rs)
    await expect(rs.result).include({ ok: 1 })
  })
  it('found', async () => {
    let connection = await connect()
    let db = connection.db()

    let found = await db.collection('students').find().toArray()
    console.log('found:', found)

    expect(found).to.be.an('array')
  })
})
复制代码

这样,咱们就实现了相似MYSQL的数据验证。

因此呢,之后在也不要说MongoDB数据结构不稳定啦。

官方文档传送门:docs.mongodb.com/manual/refe…

相关文章
相关标签/搜索