Friday, 23 December 2016

MongoDB Basics IV - CRUD Operations

The so-called CRUD operations are what we use to Create, Read, Update, and Delete documents.

In MongoDB, write operations (Create, Update, Delete) target a single Collection.

All write operations in MongoDB are atomic on the level of a single Document, regardless of how many Documents they change. This means if the operation fails at some point, the writes it has already perfomed persist in the database.

Write operations generally accept a writeConcern option that controls the level of acknowledgement required from MongoDB before the operation is considered to be complete. We will look at writeConcern here.

Create Operations
Create operations insert new Documents to a Collection.

If the Collection does not currently exist, inserting a Document will implicitly create the Collection.

MongoDB 3.4 provides the following methods to insert Documents into a Collection:
  • db.collection.insert(document or array of documents,writeConcern, ordered)
  • db.collection.insertOne(document, writeConcern)
  • db.collection.insertMany(array of documents,writeConcern, ordered)
  • some types of updates, which we will cover under Update Operations

If we use {ordered: false} then Documents are inserted in an unordered format and may be reordered by MongoDB for performance benefits. MongoDB will continue to insert remaining Documents from the array even if an error is encountered. The default, {ordered: true} will insert the Documents in the order given and if an error is encountered it will return an error and cease to insert the remaining Documents.

To see these in action, with a mongod process running, run the mongo executable to start the mongo shell. We can then insert Documents into MongoDB

Here we first see how to display the Databases and Collections in MongoDB (the commands: show dbs, db and show collections). Note that by default the MongoDB shell connects to the test Database, which currently does not exist and has no Collections.

After we insert our first Document:
db.play.insert({field1 : 1})
the test Database and the play Collection are created. Note also the _id field is added by MongoDB.

db.collection.insert() returns a WriteResult object:
WriteResult({ "nInserted" : 1 })
telling us how many documents were inserted. It will only ever be one for an insert.

We can use db.collection.insert() to insert multiple Documents, by encasing them in square brackets to form an Array. (See this post for discussion of data types)
Just for test purposes, I first dropped the test Database, using the db.dropDatabase() command.
Two documents are inserted with:
db.play.insert([{field1 : 2},{field1 : 3, field2 : 1}])
MongoDB responds with a BulkWriteResult object, which has many more fields in addition to "nInserted". They relate to other types of write operation.

The other two insert methods db.collection.insertOne and db.collection.insertMany behave similarly to the two ways of invoking db.collection.insert(), but they return a Document providing an acknowledged field and the ObjectId of the inserted Documents. The db.collection.insertOne() method will also return an error if you pass it an array of Documents.



The db.collection.save()  function will perform an insert if the Document is supplied with no _id field, otherwise it will call db.collection.update() (see below).

Read Operations
We covered read operations in this post

Update Operations
Update operations modify existing Documents in a Collection. MongoDB provides the following methods to do this:
  • db.collection.update(query document,update document,options)
  • db.collection.updateOne(query document,update document,options)
  • db.collection.updateMany(query document,update document,options)
  • db.collection.replaceOne(query document,update document,options)
  • db.collection.findAndModify(query document,update document,sort,options)
  • db.collection.findOneAndReplace(query document,update document,options)
  • db.collection.findOneAndUpdate(query document,update document,options)
  • db.collection.save(document,writeConcern)

You can specify query criteria, or filters, that identify the documents to update. These filters use the same syntax as read operations.

There are also various options that may be applied:
OptionTypeDescription
upsertbooleantrue: will create a new Document if no existing Documents match the filter
false: will only update existing Documents [default]
multibooleantrue: will update any Document that matches the filter
false: will update only one Document [default]
writeConcerndocumentcontrol the level of acknowledgement of the write
collationdocumentlanguage specific rules for the operation

And various operators can be applied in the update document:
NameDescription
$incincrements the value of the field by the given value
$mulmultiplies the value of the field by the given value
$rename renames the field
$setOnInsert sets the field to the given value only if a new Document is inserted
$set sets the field to the given value
$unset removes the specified field
$min updates the field only if the given value is less than the current field value
$max updates the field only if the given value is greater than the current field value
$currentDate sets the field's value to current Date or Timestamp
$ placeholder to refer to the first element in an array that matches the query
$addToSet adds elements to an array if they do not exist
$pop removes the first or last element in an array
$pullAll removes all matching values from an array
$pullremoves all elements from an array that match the query
$pushAll adds multiple elements to an array. Deprecated
$push adds an element to an array
$each modifies $push and $addToSet to append multiple elements to an array
$slice modifies $push to limit the size of the updated array
$sort modifies $push to reorder documents stored in an array
$position modifies $push to specify the position in the array to add elements
$bit performs bitwise AND, OR and XOR integer updates
$isolatedWhen True prevents other clients from reading or writing changed Documents before the update operation is fully completed. Default False allows concurrent access to changed Documents

Let's run the following to experiment with some basic features of update()
// examples for db.collection.update(query document,update document,options)
db.test.drop()

// 1. no documents match the query, so no update
db.test.update({field1: 'a'},{field1:'a', field2: 1})
db.test.find()

// 2. no documents match the query, so new document is inserted with upsert
db.test.update({field1: 'a'},{field1:'a', field2: 2},{upsert:1})
db.test.find()

// 3. one document matches the query, so it is updated with upsert
db.test.update({field1: 'a'},{field1:'a', field2: 3},{upsert:1})
db.test.find()

// 4. insert a duplicate document
db.test.insert({field1:'a', field2: 4})
db.test.find()

// 5. two documents match the query, but only first is updated
db.test.update({field1: 'a'},{field1:'a', field2: 5})

// 6. try to use multi, but it makes the simple update document format invalid
db.test.update({field1: 'a'},{field1:'a', field2: 6},{multi:true})

// 7. any matching documents are updated with multi if we use update operators
db.test.update({field1: 'a'},{$set:{field1:'a', field2: 7}},{multi:true})
db.test.find()


This gives the following results when run through the shell:


The db.collection.updateOne() function is similar to db.collection.update(,,{multi:true})  while db.collection.updateMany() functions similarly to  db.collection.update(,,{multi:false})

The db.collection.replaceOne() functions similarly to insert() and will effectively delete the first matching Document and replace it with the update Document. In other words, fields not specified in the update document will be removed. The insert() function also does this unless it is called using only $ update operators. Let's check that with the following test script:
// examples for db.collection.replaceOne(query document,update document,options)
db.test.drop()

// 1. no documents match the query, so new document is inserted with upsert
db.test.replaceOne({field1: 'a'},{field1:'a', field2: 1, field3:1},{upsert:1})
db.test.find()

// 2. one document matches the query, so the document is replaced
db.test.replaceOne({field1: 'a'},{field1:'a', field3: 2})
db.test.find()

// 3. normal update works the same way and replaces the matching document
db.test.update({field1: 'a'},{field1:'a', field4: 3, field5: 3})
db.test.find()

// 4. update with update operators updates only specified fields
db.test.update({field1: 'a'},{$set:{field1:'4'}, $inc:{field4:1 }})
db.test.find()



The db.collection.save() function will call db.collection.update(,,{upsert:true}) if the Document is supplied with an _id, otherwise it will call db.collection.insert().
> db.collection.save
function (obj, opts) {
    if (obj == null)
        throw Error("can't save a null");

    if (typeof(obj) == "number" || typeof(obj) == "string")
        throw Error("can't save a number or string");

    if (typeof(obj._id) == "undefined") {
        obj._id = new ObjectId();
        return this.insert(obj, opts);
    } else {
        return this.update({_id: obj._id}, obj, Object.merge({upsert: true}, opts));
    }


The db.collection.find...()  functions return the before-version of the modified Document(s) in addition to performing an update

Delete Operations
Delete operations remove documents from a collection. MongoDB provides the following methods to do this
  • db.collection.remove(query, justOne, writeConcern, collation)
  • db.collection.deleteOne(query, writeConcern, collation)
  • db.collection.deleteMany(query, writeConcern, collation)
  • db.collection.findOneAndDelete(query document,options)

You can specify query criteria, or filters, that identify the documents to remove. These filters use the same syntax as read operations.

Bulk Write
MongoDB provides the ability to perform multiple write operations in bulk.

db.collection.bulkWrite(
   [ <operation 1>, <operation 2>, ... ],
   {
      writeConcern : <document>,
      ordered : <boolean>
   }
)


The valid operations can be:
  • db.collection.insertOne()
  • db.collection.updateOne()
  • db.collection.updateMany()
  • db.collection.deleteOne()
  • db.collection.replaceOne()

The ordered parameter controls whether the operations are executed in the order written or not. When false the results are unpredictable as the order of execution can change.






1 comment: