Search code examples
mongodbcapped-collections

Add a field into capped collection in MongoDB


I created a capped collection to store my log data with few fields. Due to some requirement I wanted to add an additional field named "createAt" in this collection.

db.myLogs.update({},{$set: {"createAt":new Date()}})

This is throwing below error:

WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 10003,
                "errmsg" : "Cannot change the size of a document in a capped collection: 39 != 57"
        }
})

How can I add a few field into capped collection?


Solution

  • Simple answer

    As mongod tells you, you can't. As does the documentation:

    If the update operation causes a document to grow beyond the document’s original size, the update operation will fail.

    Slightly more sophisticated answer

    If the field isn't mandatory, simply add the new documents with the field and leave the old documents as they are, using a sensible default value for the documents which do not have the field.

    If you really need to do it

    1. Stop reading from and writing to the capped collection
    2. Copy the documents from the capped collection to a temporary collection
    3. Change the documents as needed in the temporary collection
    4. Drop and recreate the capped collection
    5. Read the documents from the temporary collection in the desired order and insert them into the recreated capped collection.

    After you did "1.", you can use something like this for "2." on the shell:

    var bulk = db.temp.initializeOrderedBulkOp();
    var counter = 0;
    
    db.capped.find().forEach(
    
      function(doc){
        bulk.insert(doc);
    
        // In case you have a lot of documents in
        // your capped collection, it makes sense
        // to do an intermediate execute
        if( ++counter % 10000 == 0){
          bulk.execute();
          bulk = db.temp.initializeOrderedBulkOp();
        }
    
      }
    );
    // execute the remainder
    bulk.execute() 
    

    This should be easily adaptable for "5."