Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a validator to an existing collection via node.js mongodb driver?

Here is a code where I'm trying to add a validator to an existing collection.

const { MongoClient } = require("mongodb")

const schema = {
  $jsonSchema: {
    bsonType: "object",
    additionalProperties: false,
    required: ["name"],
    properties: {
      _id: {
        bsonType: "objectId"
      },
      name: {
        bsonType: "string"
      }
    }
  }
}

const main = async () => {
  const client = await MongoClient.connect(
    "mongodb://localhost",
    { useNewUrlParser: true }
  )
  const db = client.db("t12")

  // await db.createCollection("test", { validator: schema })
  await db.createCollection("test")
  await db.admin().command({ collMod: "test", validator: schema })

  await db.collection("test").createIndex({ name: 1 }, { unique: true })

  await db.collection("test").insertOne({ name: "t1" })
  await db.collection("test").insertOne({ value: "t2" }) // should fail

  const all = await db
    .collection("test")
    .find({})
    .toArray()
  console.log(all)

  await client.close()
}

main().catch(err => console.error(err))

It fails:

max7z@mbp t12__npm__mongodb (master)*$ node test/1.js
{ MongoError: ns does not exist
    at /Users/max7z/projects/t/t12__npm__mongodb/node_modules/mongodb-core/lib/connection/pool.js:581:63
    at authenticateStragglers (/Users/max7z/projects/t/t12__npm__mongodb/node_modules/mongodb-core/lib/connection/pool.js:504:16)
    at Connection.messageHandler (/Users/max7z/projects/t/t12__npm__mongodb/node_modules/mongodb-
  ok: 0,
  errmsg: 'ns does not exist',
  code: 26,
  codeName: 'NamespaceNotFound',
  name: 'MongoError',
  [Symbol(mongoErrorContextSymbol)]: {} }
^C

If I create the collection with that schema it works, but when I'm trying to add a vatidator via collMod, it fails.

How to add a validator to an existing collection via collMod command?

like image 275
Max Block Avatar asked Sep 12 '25 17:09

Max Block


1 Answers

I created a function like

const updateValidator = async (collectionName, newValidator) => {
    return db.command({
      collMod: collectionName,
      validator: newValidator,
      validationLevel: "moderate",
      validationAction: "warn"
   });
}

The problem with db.command is that is replaces the whole validation schema. Therefore you need access to the current schema of the collection. As I did not found the function db.getCollectionInfos in the nodejs library I added the posibility of passing it as a parameter.

In my case I get it from other migration module with a require. E.g.

const currentValidator = require("migration-file-where-I-defined-the-previous-schema").schema.validator;

In the required file I have some initial schema like:

module.exports.schema = {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name"],
      properties: {
        name: {
          bsonType: "string",
          maxLength: 300,
          minLength: 3,
          description: "Must be a string and is required"
        },
        created: {
          bsonType: "date",
          description: "Date when it was created"
        },
        deleted: {
          bsonType: "date",
          description: "Date when it was deleted"
        }
      }
    },
  }
};

Then I create a merge of the new schema and that will be enough. E.g.

  const updatedValidator = Object.assign({}, currentValidator);
  updatedValidator.$jsonSchema.properties.new_attribX = {
    enum: ["type1", "type2"],
    description: "State of the tenant related to its life cycle"
  };
  updatedValidator.$jsonSchema.required.push('new_attribX');

  updateValidator("mycollection", updatedValidator)
    .then(next)
    .catch(console.error);

This will replace the previous schema with the new one which has the changes applied. For me, this was enough, but bear in mind that when you have existing data that needs to be updated with new data, then you need to update them with something like

collection.updateMany({'new_attribX': {$exists : false}}, {$set: {'new_attribX': 'type1'}});

Which for that data which has not that attribute (new_attribX) it should add them kind of this initial default value: type1.

I hope it helps.

like image 103
EliuX Avatar answered Sep 15 '25 09:09

EliuX