Taste MongoDB: Operations (2)

Posted by jrschwartz on Fri, 10 May 2019 20:54:09 +0200

Catalog

4. Update data

4. Update data

(1)update()
In MongoDB, update() functions can be used to perform data update operations. The function takes three main parameters: criteria, objNew and option. The parameter criteria can be used to specify a query that selects the target record to be updated. Use the objNew parameter to specify the update information, or use the operator to complete it. The parameter option is used to specify the options when updating the document, and its optional values are upsert and multi. With the option upsert, you can specify whether the update is an upsert operation -- it tells MongoDB to update if the data exists, or create the data. Finally, the option multi specifies whether all matching documents should be updated, or only the first document (default behavior).

> db.media.update( { "Title" : "Matrix, The"}, {"Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action"}, { upsert: true} );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>

This example rewrites the document in the collection and saves the updated value for it. Note that the update() function without the $set operator removes any overlooked keys. After executing the above statement, the Cast key has been removed:

> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
>

The updateOne function can also be used to modify a single record. There are two main differences between the updateOne function and the update function:

  1. Do not remove overlooked keys.
  2. The $set operator must be used.
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
> db.media.updateOne( { "Title" : "Matrix, The"}, {"Type" : "DVD"}, { upsert: true} );
2018-09-27T13:31:54.772+0800 E QUERY    [js] Error: the update operation document must contain atomic operators :
DBCollection.prototype.updateOne@src/mongo/shell/crud_api.js:542:1
@(shell):1:1
> db.media.updateOne( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD"}}, { upsert: true} );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
> db.media.updateOne( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD1"}}, { upsert: true} );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD1", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
> db.media.updateOne( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD"}}, { upsert: true} );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
>

There are also two ways to modify multiple records:

db.media.updateMany( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD"}}, { upsert: true} );

Or:

db.media.update( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action"}}, { upsert: true,multi:true} );

(2)save()
Upert can be executed using the save() command. If the _id value is not specified, save() performs an insert operation, otherwise upsert operation is performed.

> db.products.save( { item: "book", qty: 40 } );
WriteResult({ "nInserted" : 1 })
> db.products.find();
{ "_id" : ObjectId("5bac6f41845a6b94a74d82f0"), "item" : "book", "qty" : 40 }
> db.products.save( { _id: 100, item: "water", qty: 30 } );
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 100 })
> db.products.find();
{ "_id" : ObjectId("5bac6f41845a6b94a74d82f0"), "item" : "book", "qty" : 40 }
{ "_id" : 100, "item" : "water", "qty" : 30 }
> db.products.save( { _id : 100, item : "juice" } );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.products.find();
{ "_id" : ObjectId("5bac6f41845a6b94a74d82f0"), "item" : "book", "qty" : 40 }
{ "_id" : 100, "item" : "juice" }
>

(3) Automatic update

  • $inc

The operator $inc can perform atomic updates for the specified key. If a field exists, the value is incremented by the specified increment, otherwise the key is created.

> manga = ( { "Type" : "Manga", "Title" : "One Piece", "Volumes" : 612, "Read" : 520 } );
{ "Type" : "Manga", "Title" : "One Piece", "Volumes" : 612, "Read" : 520 }
> db.media.insertOne(manga);
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5bac706d845a6b94a74d82f1")
}
> db.media.updateOne ( { "Title" : "One Piece"}, {$inc: {"Read" : 4} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find ( { "Title" : "One Piece" } );
{ "_id" : ObjectId("5bac706d845a6b94a74d82f1"), "Type" : "Manga", "Title" : "One Piece", "Volumes" : 612, "Read" : 524 }
>
  • $set

Some fields can be set to specified values using the $set operator. This operator can update any data.

> db.media.update ( { "Title" : "Matrix, The" }, {$set : { Genre : "Sci-Fi" , Type : "DVD"} } );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
  • $unset

The specified field can be deleted by the $unset operator.

> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Sci-Fi" }
{ "_id" : ObjectId("5bac6c6c845a6b94a74d82ee"), "Title" : "Matrix, The" }
> db.media.updateOne ( {"Title": "Matrix, The"}, {$unset : { "Genre" : 1 } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999 }
{ "_id" : ObjectId("5bac6c6c845a6b94a74d82ee"), "Title" : "Matrix, The" }
>
  • $push

The $push operator allows you to add a value to a specified field. If the field is an array, the value is added to the array. If the field does not exist, the value of the field will be set to an array. If the field exists, but not an array, an error will be thrown.

> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$push: { Author : "Griffin, Stewie"} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$push: { Title : "This isn't an array"} } );
2018-09-27T14:20:46.314+0800 E QUERY    [js] WriteError: The field 'Title' must be an array but is of type string in document {_id: ObjectId('5baae32464e6602b766d94c0')} :
WriteError({
    "index" : 0,
    "code" : 2,
    "errmsg" : "The field 'Title' must be an array but is of type string in document {_id: ObjectId('5baae32464e6602b766d94c0')}",
    "op" : {
        "q" : {
            "ISBN" : "978-1-4842-1183-0"
        },
        "u" : {
            "$push" : {
                "Title" : "This isn't an array"
            }
        },
        "multi" : false,
        "upsert" : false
    }
})
WriteError@src/mongo/shell/bulk_api.js:461:48
Bulk/mergeBatchResults@src/mongo/shell/bulk_api.js:841:49
Bulk/executeBatch@src/mongo/shell/bulk_api.js:906:13
Bulk/this.execute@src/mongo/shell/bulk_api.js:1150:21
DBCollection.prototype.updateOne@src/mongo/shell/crud_api.js:572:17
@(shell):1:1
> db.media.find ( { "ISBN" : "978-1-4842-1183-0" } );
{ "_id" : ObjectId("5baae32464e6602b766d94c0"), "Type" : "Book", "Title" : "Definitive Guide to MongoDB 3rd ed., The", "ISBN" : "978-1-4842-1183-0", "Publisher" : "Apress", "Author" : [ "Hows, David", "Plugge, Eelco", "Membrey, Peter", "Hawkins, Tim", "Griffin, Stewie" ] }
{ "_id" : ObjectId("5baae32864e6602b766d94c1"), "Type" : "Book", "Title" : "Definitive Guide to MongoDB 3rd ed., The", "ISBN" : "978-1-4842-1183-0", "Publisher" : "Apress", "Author" : [ "Hows, David", "Plugge, Eelco", "Membrey, Peter", "Hawkins, Tim" ] }
>
  • $each

Add several values to the array:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, { $push: { Author : { $each:
... ["Griffin, Peter", "Griffin, Brian"] } } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Hows, David",
        "Plugge, Eelco",
        "Membrey, Peter",
        "Hawkins, Tim",
        "Griffin, Stewie",
        "Griffin, Peter",
        "Griffin, Brian"
    ]
}
>

You can also use the $slice operator when using $each. In this way, you can limit the number of elements in the array in the $push operator. $slice accepts negative or zero. Using negative numbers ensures that the last n elements in an array are preserved, while using 0 means empty arrays. Note that the operator $slice must be the first modification operator in the $push operation:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, { $push: { Author : { $each:
... ["Griffin, Meg", "Griffin, Louis"], $slice: -2 } } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Meg",
        "Griffin, Louis"
    ]
}
>
  • $addToSet

The operator $addToSet is another command that can be used to add data to an array. However, only when the data does not exist can the operator add the data to the array. It works differently from $push.

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, {$addToSet : { Author : "Griffin, Brian" } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Meg",
        "Griffin, Louis",
        "Griffin, Brian"
    ]
}
>

(4) Delete elements from an array

  • $pop

Delete the first or last element in the array:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, {$pop : {Author : 1 } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Meg",
        "Griffin, Louis"
    ]
}
> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, {$pop : {Author : -1 } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Louis"
    ]
}
>
  • $pull

Delete all specified values:

> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$push: { Author : "Griffin, Stewie"} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Louis",
        "Griffin, Stewie"
    ]
}
> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$pull : { Author : "Griffin, Stewie" } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Louis"
    ]
}
>
  • $pullAll

Delete multiple elements in the array:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0"}, {$pullAll : { Author : ["Griffin, Louis","Griffin, Peter","Griffin, Brian"] } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [ ]
}
>

(5) Specify the location of the matching array
The $operator can be used in the query to specify the location of the matching array elements in the query. This operator can be used to perform data operations on an array element after it has been searched.

> db.media.updateOne( { "Artist" : "Nirvana" }, {$addToSet : { Tracklist : {"Track" : 2,"Title": "Been a Son", "Length":"2:23"} } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "Tracklist.Title" : "Been a Son"});
{
    "_id" : ObjectId("5baae67464e6602b766d94c3"),
    "Type" : "CD",
    "Artist" : "Nirvana",
    "Title" : "Nevermind",
    "Tracklist" : [
        {
            "Track" : 2,
            "Title" : "Been a Son",
            "Length" : "2:23"
        }
    ]
}
> db.media.updateOne( { "Tracklist.Title" : "Been a Son"}, {$inc:{"Tracklist.$.Track" : 1} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "Tracklist.Title" : "Been a Son"});
{
    "_id" : ObjectId("5baae67464e6602b766d94c3"),
    "Type" : "CD",
    "Artist" : "Nirvana",
    "Title" : "Nevermind",
    "Tracklist" : [
        {
            "Track" : 3,
            "Title" : "Been a Son",
            "Length" : "2:23"
        }
    ]
}
>

(6) Atomic operation
MongoDB supports atomic operations on a single document, and does not support atomic updates of multiple documents in a single operation. If a set of operations satisfy the following conditions, they can be called atomic operations:

  • Other processes cannot get the result of modification unless the entire rental operation has been completed.
  • If one of the operations fails, the entire atomic operation will fail and roll back, and the data will be restored to the state before the atomic operation is run.

The standard behavior for atomic operations is to lock data and not allow access to other queries, but MongoDB does not support locks or complex transactions. Several update operations included in MongoDB can update data in atomic mode:

  • set: set specific values.
  • unset: Delete a specific value.
  • inc: Increase a value by a specific amount.
  • $push: Add values to the array.
  • pull: Delete a single value from an existing array.
  • pullAll: Delete multiple values from an existing array.

Use the Update if Current method
Another strategy for updating data is to use the Update if Current (if the data has not changed yet) method. The method consists of three steps, all of which are accomplished in atomic mode.

  1. Get the object from the document.
  2. Modify objects locally.
  3. Send an update request to update the object value, assuming that the current value still matches the previous value.

This approach is essentially an implementation of optimistic locking. To avoid ABA problems in concurrent situations, the following methods can be used:

  • Use the complete object in the updated query expression, not just the _id and comments.by fields.
  • Use $set to update important fields. Even if other fields have changed, they will not be affected by that field.
  • Add a version variable to the object and add its value each time it is updated.
  • If possible, use the $operator instead of the Update-if-Current sequence.

You can also perform atomic operations on documents by executing the find AndModify command. This command modifies and returns the document. It accepts three main operators: < query > for specifying the target document, < sort > for sorting multiple matching documents, and < operations > for specifying the operations that you want to perform. For example:

> db.media.findAndModify( { "Title" : "One Piece",sort:{"Title": -1}, remove: true} );
{
    "_id" : ObjectId("5bab30521062c31f5bdf664b"),
    "Type" : "DVD",
    "Title" : "Toy Story 3",
    "Released" : 2010
}
>

This code returns the document that matches the search criteria and finds and deletes the first document with Title as'One Piece'. The following example will modify the document rather than delete it:

> db.media.findAndModify( { query: { "ISBN" : "978-1-4842-1183-0" }, sort: {"Title":-1},update: {$set: {"Title" : " Different Title"} } } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [ ]
}
>

If you want to return the updated document, you can add a new operator after the query:

> db.media.findAndModify( { query: { "ISBN" : "978-1-4842-1183-0" }, sort: {"Title":-1},update: {$set: {"Title" : " Different Title"} }, new:true } );
{
    "_id" : ObjectId("5baae32864e6602b766d94c1"),
    "Type" : "Book",
    "Title" : " Different Title",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Hows, David",
        "Plugge, Eelco",
        "Membrey, Peter",
        "Hawkins, Tim"
    ]
}
>

Any modification operator can be used in this command, not just $set.

Topics: MongoDB shell