Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB update validator

I have a collection that looks like this:

{
  "_id": "5c90e8736441c96cc8e5c703",
  "meta": {
    "schemaVersion": 1,
    "locHint": {
      "region": "150",
      "country": "GBR"
    }
  },
  "name": "My test study"
}

And the following validator:

{
  "$jsonSchema": {
    "bsonType": "object",
    "required": [
      "meta.schemaVersion"
      "meta.locHint.region",
      "meta.locHint.country",
      "name"
    ],
    "properties": {
      "meta.schemaVersion": {
        "bsonType": "int",
        "description": "Version counter used schema changes"
      },
      "meta.locHint.region": {
        "bsonType": "string",
        "pattern": "^[0-9]{3}$",
        "description": "meta.locHint.region is required to be a valid iso3166 region  (3 digits)"
      },
      "meta.locHint.country": {
        "bsonType": "string",
        "pattern": "^[A-Z]{3}$",
        "description": "meta.locHint.country is required to be a valid iso3166 alpha-3  (3 upper case letters)"
      },
      "name": {
        "bsonType": "string",
        "description": "name is required and must be a string"
      }
    }
  }
}

What do I want to achieve is: rename (update) region to m46Region, country to iso3166Country and change the pattern for meta.locHint.country using a MongoBee migration script.

When I try to do that, mongo says that my document is not valid and it's not allowing me to rename the fields.

How do I update the validators as well? or remove and recreate them?

like image 943
frunza Avatar asked Mar 19 '19 15:03

frunza


1 Answers

I managed to do it, not sure if it is the right way, but it did the job.

First you have to turn off validationLevel and suppress the errors in validationAction

private def disableValidators(db: DB, collectionName: String): Unit = {
  val command = new BasicDBObject("collMod", collectionName)
    .append("validationLevel", "off")
    .append("validationAction", "warn")

  db.command(command)
}

Add the new field and rename the other fields.

private def addRenameLocationHintFields(collection: DBCollection): Unit = {
  val findQuery = new BasicDBObject()
  val addFieldIso3166Subdivision = new BasicDBObject("$set", new BasicDBObject("meta.locHint.iso3166Subdivision", null))
  collection.updateMulti(findQuery, addFieldIso3166Subdivision)

  val renameRegion = new BasicDBObject("$rename", new BasicDBObject("meta.locHint.region", "meta.locHint.m49Region"))
  collection.updateMulti(findQuery, renameRegion)

  val renameCountry = new BasicDBObject("$rename", new BasicDBObject("meta.locHint.country", "meta.locHint.iso3166CountryA2"))
  collection.updateMulti(findQuery, renameCountry)
}

If you had indexed your fields, you'll need to drop and recreate them.

collection.dropIndex("locHint")
  collection.createIndex(
    BasicDBObjectBuilder
      .start()
      .add("meta.locHint.m49Region", 1)
      .add("meta.locHint.iso3166CountryA2", 1)
      .add("meta.locHint.iso3166Subdivision", 1)
      .add("_id", 1)
      .get()
  )

Update the validators, the old collection validators will be replaced by the new ones.

private def updateValidators(db: DB, collectionName: String): Unit = {
  val command = BasicDBObjectBuilder
    .start()
    .add("collMod", collectionName)
    .add(
      "validator",
      BasicDBObjectBuilder
      .start()
      .add(
        "$jsonSchema",
        BasicDBObjectBuilder
          .start()
          .add("bsonType", "object")
          .add(
            "required",
            Array(
              "meta.schemaVersion",
              "meta.locHint.m49Region",
              "meta.locHint.iso3166CountryA2",
              "name"
            )
          )
          .add(
            "properties",
            BasicDBObjectBuilder
              .start()
              .add(
                "meta.schemaVersion",
                BasicDBObjectBuilder
                  .start()
                  .add("bsonType", "int")
                  .add("description", "Version counter used schema changes")
                  .get()
              )
              .add(
                "meta.locHint.m49Region",
                BasicDBObjectBuilder
                  .start()
                  .add("bsonType", "string")
                  .add("pattern", "^[0-9]{3}$")
                  .add("description", "meta.locHint.m49Region is required to be a valid iso3166 region  (3 digits)")
                  .get()
              )
              .add(
                "meta.locHint.iso3166CountryA2",
                BasicDBObjectBuilder
                  .start()
                  .add("bsonType", "string")
                  .add("pattern", "^[A-Z]{2}$")
                  .add("description", "meta.locHint.iso3166CountryA2 is required to be a valid iso3166 alpha-2  (2 upper case letters)")
                  .get()
             )
              .add(
                "name",
                BasicDBObjectBuilder
                  .start()
                  .add("bsonType", "int")
                  .add("description", "Version of the terms of service that the user has accepted")
                  .get()
              )
              .get()
          )
        .get()
      )
      .get()
  )

  db.command(command.get())
}

Enable back the validators.

private def enableValidators(db: DB, collectionName: String): Unit = {
  val command = new BasicDBObject("collMod", collectionName)
    .append("validationLevel", "strict")
    .append("validationAction", "error")

  db.command(command)
}

That should do the job.

like image 75
frunza Avatar answered Dec 01 '22 00:12

frunza