MongoDB has at times been excluded because its fields are too flexible and its data structure is unstable.

For example, the user table, for the most part, is in a fixed format:

| id | name | age | province |
Copy the code

Types are:

| ID | CHAR | INT | VARTXT |
Copy the code

If you use MongoDB, because the document type is flexible, this will cause a problem if someone accidentally inserts a data:

{
  name: "Jerry",
  age: "18",
  province: "Guangdong"
}
Copy the code

Look at the age: “18”, especially in the current NodeJS environment, WEB submitted tables but all string, can appear. Of course, there are solutions:

  • Application-level validation — that is, write your own validation, such as Koa/ ExpressJS intermediate, or KOA-validate;
  • Validation/conversion using Mongoose Schema;

Using 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}});Copy the code

The advantage of using Mongoose is that it can not only validate, but also convert types. In addition, it can also use data in a more object-oriented way, so it is highly recommended to introduce mongoose library.

So the question is, what if the same database is used in a non-NodeJS environment? Everyone knows that in microservices, it is possible to use Java for all kinds of data processing. So the best way to do this is to validate in a database like MYSQL does.

Validate MongoDB fields using jsonSchema

After MongoDB3.6, data validation of jsonSchema will be supported, which can be configured and modified as follows.

// Table creation time configuration
db.createCollection( <collection>, { validator: { $jsonSchema: <schema> } } )
// Change it at any time
db.runCommand( { collMod: <collection>, validator:{ $jsonSchema: <schema> } } )
Copy the code

The configuration is done with the two statements above, which are very simple and use the standard JSON Schema specification.

JSON Schema specification content, here not to elaborate, you can search for their own. Of course, mongoDB also has some field types of its own, which is a little different from Javascript. For int, then 10 is legal, but 10.1 is illegal. There are also some details, such as year below.

year: {
    bsonType: "int".minimum: 2017.maximum: 3017.description: "must be an integer in [ 2017, 3017 ] and is required"
},
Copy the code

Here is only an introduction, specific can go to the official website to see, not complex.

The effect

After setting up validation, an error will be reported whenever validation fails:

WriteResult({
	"nInserted" : 0,
	"writeError" : {
		"code" : 121,
		"errmsg" : "Document failed validation"
	}
})
Copy the code

In NodeJS, this can be set up in code, but using Mongoose requires a bit of manipulation as it does not provide mongodb Connector built-in object exposure.

const MongoClient = require("mongodb").MongoClient;
const URL = 'mongo: / / @ 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()
  // Write jsonSchema here
  await db.createCollection(<collection>, { validator: { $jsonSchema: <schema> } })
  // Modify jsonSchema
  await db.runCommand( { collMod: <collection>, validator:{ $jsonSchema: <schema> } } )
}
init().then(() = > {})
Copy the code

Let’s write some test cases:

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

const URL = 'mongo: / / @ 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 Will fail if it is not an integer
      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()
    // If name is required, it will fail
    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')})})Copy the code

In this way, we implement mysql-like data validation.

So don’t say MongoDB data structures are unstable again.

The official documentation portal: docs.mongodb.com/manual/refe…