chapter3 创建、更新和删除文档

3.1 插入并保存文档

> db.foo.insert({"bar",:"baz"})

这个操作会给文档自动增加一个"_id"键(要是原来没有的话),然后将其保存到MongoDB中。

3.1.1 批量插入

> db.foo.insert([{"_id":0},{"_id":1},{"_id":2}])
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 3,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
> db.foo.find()
{ "_id" : 0 }
{ "_id" : 1 }
{ "_id" : 2 }

3.1.2 插入检验

    插入数据时,MongoDB只对数据进行最基本的检查:检查文档的基本结构,如果没有"_id"字段,就自动增加一个。检查大小就是其中一项基本结果检查:所有文档多必须小于16MB。

3.2 删除文档

>db.foo.remove()

remove函数可以接受一个查询文档为可选参数。例如要删除mailing.list集合中所有"opt-out"为true的人:

>db.mailing.list.remove({"opt-out":true})

删除数据时永久性的,不能撤销,也不能恢复。

删除速度

    删除文档通常很快,但是如果要清空整个集合,那么使用drop直接删除集合会更快(然后在这个空集合上重建各项索引)。

例如,使用如下方法插入一百万个测试数据:

> for(var i=0;i<1000000;i++){
... db.tester.insert({"foo":"bar","baz":i,"z":10 - i})
... }


3.3 更新文档

3.3.1 文档替换

    文档存入数据库之后,就可以使用update方法来更新它。update有两个参数,一个是查询文档,用于定位需要更新的目标文档;另一个是修改器(modifier)文档,用于说明要对找到的文档进行哪些修改。
> db.users.insert({"name":"joe","friends":32,"enemies":2})
WriteResult({ "nInserted" : 1 })
> db.users.find()
{ "_id" : ObjectId("5aafdbf9522bf5348fda1c21"), "name" : "joe", "friends" : 32, "enemies" : 2 }
> var joe=db.users.findOne({"name":"joe"});
> joe.relationships={"friends":joe.friends,"enemies":joe.enemies};
{ "friends" : 32, "enemies" : 2 }
> joe.username=joe.name;
joe
> delete joe.friends;
true
> delete joe.enemies;
true
> delete joe.name;
true
> db.users.update({"name":"joe"},joe);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aafdbf9522bf5348fda1c21"),
        "relationships" : {
                "friends" : 32,
                "enemies" : 2
        },
        "username" : "joe"
}

    一个常见的错误是查询条件匹配到了多个文档,然后更新时由于第二个参数的存在就产生重复的"_id"值。数据库会抛出错误,任何文档都不会更新。

    例如,有好几个文档都有相同的"name"值,但是我们没有意识到:

> db.people.insert([{"name":"joe","age":65},{"name":"joe","age":20},{"name":"joe","age":49}])
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 3,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
> db.people.find()
{ "_id" : ObjectId("5aafdf4c522bf5348fda1c22"), "name" : "joe", "age" : 65 }
{ "_id" : ObjectId("5aafdf4c522bf5348fda1c23"), "name" : "joe", "age" : 20 }
{ "_id" : ObjectId("5aafdf4c522bf5348fda1c24"), "name" : "joe", "age" : 49 }

    现在如果第二个Joe过生日,要增加"age"的值,我们可能会这样做:

> joe=db.people.findOne({"name":"joe","age":20});
{ "_id" : ObjectId("5aafdf4c522bf5348fda1c23"), "name" : "joe", "age" : 20 }
> joe.age++
20
> db.people.update({"name":"joe"},joe);
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 16837,
                "errmsg" : "The _id field cannot be changed from {_id: ObjectId('5aafdf4c522bf5348fda1c22')} to {_id: ObjectId('5aafdf4c522bf5348fda1c23')}."
        }
})

    为了避免这种情况,最好确保更新时总是指定一个唯一文档,例如使用"_id"这样的键来匹配。对于上面的例子,这才是正确的更新方法:

> db.people.update({"_id":ObjectId("5aafdf4c522bf5348fda1c23")},joe)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

    使用"_id"作为查询条件比使用随机字段速度更快,因为是通过"_id"建立的索引。

3.3.2 使用修改器

    通常文档只会有一部分更新。可以使用原子性的更新修改器(update modifier),指定对文档中的某些字段进行更新。更新修改器是种特殊的键,用来指定复杂的更新操作,比如修改、增加或者删除键,还可能是操作数组或者内嵌文档。

    假设要在一个集合中放置网站的分析数据,只要有人访问页面,就增加计数器。可以使用更新修改器原子性的完成这个增加。每个URL及对应的访问次数都以如下方式存储在文档中:

> db.analytics.insert({"url":"www.example.com","pageviews":52})
WriteResult({ "nInserted" : 1 })
> db.analytics.find()
{ "_id" : ObjectId("5aafe2e2522bf5348fda1c25"), "url" : "www.example.com", "pageviews" : 52 }

    每次有人访问页面,就通过URL找到该页面,并用"$inc"修改器增加"pageviews"的值。

> db.analytics.update({"url":"www.example.com"},{"$inc":{"pageviews":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.analytics.find()
{ "_id" : ObjectId("5aafe2e2522bf5348fda1c25"), "url" : "www.example.com", "pageviews" : 53 }
1."$set"修改器入门

    "$set"用来指定一个字段的值。如果这个字段不存在,则创建它。这对更新模式或者增加用户定义的键来说非常方便。例如,用户资料存储在下面这样的文档里:

> db.users.insert({"name":"joe","age":30,"sex":"male","location":"Wisconsin"})
WriteResult({ "nInserted" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aafe4d7522bf5348fda1c26"),
        "name" : "joe",
        "age" : 30,
        "sex" : "male",
        "location" : "Wisconsin"
}

    想要添加喜欢的书籍进去,可以使用"$set":

> db.users.update({"_id" : ObjectId("5aafe4d7522bf5348fda1c26")},{"$set":{"favorite book":"War and Peace"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aafe4d7522bf5348fda1c26"),
        "name" : "joe",
        "age" : 30,
        "sex" : "male",
        "location" : "Wisconsin",
        "favorite book" : "War and Peace"
}

    要是用户觉得喜欢的其实是另外一本书,"$set"又能帮上忙了:

> db.users.update({"name":"joe"},{"$set":{"favorite book":"Green Eggs and Ham"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5aafe4d7522bf5348fda1c26"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "Wisconsin", "favorite book" : "Green Eggs and Ham" }

    用"$set"甚至可以修改键的类型。例如,如果用户觉得喜欢很多本书,就可以将"favorite ebook"键的值变成一个数组:

> db.users.update({"name":"joe"},{"$set":{"favorite book":["Cat's Cradle","Foundation Triday","Ender's Game"]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aafe4d7522bf5348fda1c26"),
        "name" : "joe",
        "age" : 30,
        "sex" : "male",
        "location" : "Wisconsin",
        "favorite book" : [
                "Cat's Cradle",
                "Foundation Triday",
                "Ender's Game"
        ]
}

   如果用户突然发现自己其实不爱读书,可以用"$unset"将这个键完全删除:

> db.users.update({"name":"joe"},{"$unset":{"favorite book":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aafe4d7522bf5348fda1c26"),
        "name" : "joe",
        "age" : 30,
        "sex" : "male",
        "location" : "Wisconsin"
}

    也可以用"$set"修改内嵌文档:

> db.blog.posts.insert({"title":"A Blog Post","content":"...","author":{"name":"joe","email":"joe@example.com"}})
WriteResult({ "nInserted" : 1 })
> db.blog.posts.findOne()
{
        "_id" : ObjectId("5aafe8a0522bf5348fda1c27"),
        "title" : "A Blog Post",
        "content" : "...",
        "author" : {
                "name" : "joe",
                "email" : "joe@example.com"
        }
}
> db.blog.posts.update({"author.name":"joe"},{"$set":{"author.name":"joe schmoe"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.posts.findOne()
{
        "_id" : ObjectId("5aafe8a0522bf5348fda1c27"),
        "title" : "A Blog Post",
        "content" : "...",
        "author" : {
                "name" : "joe schmoe",
                "email" : "joe@example.com"
        }
}

    增加、修改或删除键时,应该使用$修改器。

2.增加和减少

    "$inc"修改器用来增加已有键的值,或者该键不存在那就创建一个。

> db.games.insert({"game":"pinball","user":"joe"})
WriteResult({ "nInserted" : 1 })
> db.games.findOne()
{
        "_id" : ObjectId("5aafea07522bf5348fda1c28"),
        "game" : "pinball",
        "user" : "joe"
}
> db.games.update({"game":"pinball","user":"joe"},{"$inc":{"score":50}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne()
{
        "_id" : ObjectId("5aafea07522bf5348fda1c28"),
        "game" : "pinball",
        "user" : "joe",
        "score" : 50
}
> db.games.update({"game":"pinball","user":"joe"},{"$inc":{"score":10000}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.find()
{ "_id" : ObjectId("5aafea07522bf5348fda1c28"), "game" : "pinball", "user" : "joe", "score" : 10050 }

    "$inc"只能用于整型、长整型或双精度浮点型的值。插入其他类型会出错

> db.foo.insert({"count":"1"})
WriteResult({ "nInserted" : 1 })
> db.foo.update({},{"$inc":{"count":1}})
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 16837,
                "errmsg" : "Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('5aafeb51522bf5348fda1c29')} has the field 'count' of non-numeric type string"
        }
})
3.数组修改器

    有一大类很重要的修改器可用于操作数组。数组是常用且非常有用的数据结构:它们不仅是可通过索引进行引用的列表,而且还可以作为数据集(set)来用。

4.添加元素

    如果数组已经存在,"$push"会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组。例如,假设要存储博客文章,要添加一个用于保存数组的"comments"(评论)键。可以向还不存在的"comments"数组添加一条评论,这个数组会被自动创建,并加入一条评论:

> db.blog.posts.findOne()
{
        "_id" : ObjectId("5aafed10522bf5348fda1c2a"),
        "title" : "A blog post",
        "content" : "..."
}
> db.blog.posts.update({"title":"A blog post"},
... {"$push":{"comments":
... {"name":"joe","email":"joe@example.com",
... "content":"nice post."}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.posts.findOne()
{
        "_id" : ObjectId("5aafed10522bf5348fda1c2a"),
        "title" : "A blog post",
        "content" : "...",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post."
                }
        ]
}
> db.blog.posts.update({"title":"A blog post"},
... {"$push":{"comments":
... {"name":"bob","email":"bob@example.com",
... "content":"good post."}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.posts.findOne()
{
        "_id" : ObjectId("5aafed10522bf5348fda1c2a"),
        "title" : "A blog post",
        "content" : "...",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post."
                },
                {
                        "name" : "bob",
                        "email" : "bob@example.com",
                        "content" : "good post."
                }
        ]
}

    这是一种比较简单的"$push"使用形式,也可以将它应用在一些比较复杂的数组操作中。使用"$each"子操作符,可以通过一次"$push"操作添加多个值。

> db.stock.ticker.update({"_id":"GOOG"},
... {"$push":{"hourly":{"$each":[562.776,562.790,559.123]}}})

    这样就可以将三个新元素添加到数组中。如果指定的数组中只含有一个元素,那这个操作就等同于没有使用"$each"的普通"$push"操作。

    如果希望数组的最大长度是固定的,那么可以将"$slice"和"$push"组合在一起使用,这样就可以保证数组不会超出设定好的最大长度,这实际上就得到了一个最多包含N个元素的数组:

> db.movies.find({"genre":"horror"},
... {"$push":{"top10":{
... "$each":["Nightmare on Elm Street","Saw"],
... "$slice":-10}}})

    这个例子会限制数组只包含最后加入的10个元素。"$slice"的值必须是负整数。

    如果数组的元素数量小于10("$push"之后),那么所有元素都会保留。如果数组的元素数量大于10,那么只有最后10个元素会保留。因此,"$slice"可以用来在文档中创建一个队列。

    最后,可以在清理元素之前使用"$sort",只要向数组中添加子对象就需要清理:

> db.movies.find({"genre":"horror"},
... {"$push":{"top10"{
... "$each":[{"name":"Nightmare on Elm Street","rating":6.6},
... {"name":"Saw","rating":4.3}],
... "$slice":-10,
... "$sort":{"rating":-1}}}})

    这样会根据"rating"字段的值对数组中的所有对象进行排序,然后保留前10个。注意,不能将"$slice"或者"$sort"与"$push"配合使用,且必须使用"$each"。

5.将数组作为数据集使用

    将数组作为数据集使用,保证数组内的元素不会重复。可以在查询文档中用"$ne"来实现。例如,要是作者不在引文列表中,就添加进去,可以这么做:

> db.papers.update({"authors cited":{"$ne":"Richie"}},
... {$push:{"authors cited":"Richie"}})

    也可以用"$addToSet"来实现,要知道有些情况"$ne"根本行不通,有些时候更适合用"$addToSet"。

例如,有一个表示用户的文档,已经有了电子邮件地址的数据集:

> db.users.insert({"username":"joe","emails":["joe@example.com","joe@gmail.com","joe@yahoo.com"]})
WriteResult({ "nInserted" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aaff551522bf5348fda1c2b"),
        "username" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com"
        ]
}
> db.users.update({"_id" : ObjectId("5aaff551522bf5348fda1c2b")},
... {"$addToSet":{"emails":"joe@gmail.com"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.users.findOne()
{
        "_id" : ObjectId("5aaff551522bf5348fda1c2b"),
        "username" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com"
        ]
}
> db.users.update({"_id" : ObjectId("5aaff551522bf5348fda1c2b")},
... {"$addToSet":{"emails":"joe@hotmail.com"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aaff551522bf5348fda1c2b"),
        "username" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@hotmail.com"
        ]
}

    将"$addToSet"和"$each"组合起来,可以添加多个不同的值,而用"$ne"和"$push"组合就不能实现。例如,想一次添加多个邮件地址,就可以使用这些修改器:

> db.users.update({"_id" : ObjectId("5aaff551522bf5348fda1c2b")},{"$addToSet":
... {"emails":{"$each":
... ["joe@php.net","joe@example.com","joe@python.org"]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5aaff551522bf5348fda1c2b"),
        "username" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@hotmail.com",
                "joe@php.net",
                "joe@python.org"
        ]
}
6.删除元素

    若是把数组看成队列或者栈,可以用"$pop",这个修改器可以从数组任何一端删除元素。{"$pop":{"key":1}}从数组末端删除一个元素,{"$pop":{"key":-1}}则从头部删除。

    有时需要基于特定条件来删除元素,而不仅仅是依据元素位置,这时可以使用"$pull"。例如,有一个无序的待完成事项列表:

> db.lists.insert({"todo":["dishes","laundry","dry cleaning"]})
WriteResult({ "nInserted" : 1 })
> db.lists.update({},{"$pull":{"todo":"laundry"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.lists.find()
{ "_id" : ObjectId("5aaff8d5522bf5348fda1c2c"), "todo" : [ "dishes", "dry cleaning" ] }

    "$pull"会将所有匹配的文档删除,而不是只删除一个。对数组[1,1,2,1]执行pull 1,结果得到只有一个元素的数组[2]。

7.基于位置的数组修改器

    位置或者定位操作符("$")



    增加第一个评论的投票数量,可以这么做:


    要是用户John把名字改成了Jim,就可以使用定位符替换它在评论中的名字:


8.修改器速度
> db.coll.insert({"x":"a"})
WriteResult({ "nInserted" : 1 })
> db.coll.insert({"x":"b"})
WriteResult({ "nInserted" : 1 })
> db.coll.insert({"x":"c"})
WriteResult({ "nInserted" : 1 })
> db.coll.find()
{ "_id" : ObjectId("5aaffb8d522bf5348fda1c2d"), "x" : "a" }
{ "_id" : ObjectId("5aaffb9a522bf5348fda1c2e"), "x" : "b" }
{ "_id" : ObjectId("5aaffbab522bf5348fda1c2f"), "x" : "c" }
> db.coll.update({"x":"b"},{$set:{"x":"bbb"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.coll.find()
{ "_id" : ObjectId("5aaffb8d522bf5348fda1c2d"), "x" : "a" }
{ "_id" : ObjectId("5aaffb9a522bf5348fda1c2e"), "x" : "bbb" }
{ "_id" : ObjectId("5aaffbab522bf5348fda1c2f"), "x" : "c" }

    MongoDB不得不移动一个文档时,它会修改集合的填充因子(padding factor),填充因子是MongoDB为每个新文档预留的增长空间。可以运行db.collstats()查看填充因子。


    移动文档是非常慢的。MongoDB必须将文档原先所占的空间释放掉,然后将文档写入另一片空间。

3.3.3 upsert

    upsert是一种特殊的更新。要是没有找到符合更新条件的文档,就会以这个条件和更新文旦为基础创建一个新的文档。如果找到了匹配的文档,则正常更新。


    这就是说如果有人访问页面,我们得先对数据库进行查询,然后选择更新或者插入。要是多个进程同时运行这段代码,还会遇到同时对给定URL插入多个文档这样的竞争。


    这行代码和之前的代码作用完全一样,但它更高效,并且是原子性的!创建新文档会将条件文档作为基础,然后对它应用修改器文档。

     例如,要是执行一个匹配键并增加对应键值的upsert操作,会在匹配的文档上进行增加:


    upsert创建一个"rep"值为25的文档,随后将这个值加3,最后得到"rep"为28的文档。要是不指定upsert选项,{"rep":25}不会匹配任何文档,也就不会对集合进行任何更新。

    要是再次运行这个upsert(条件为{"rep":25}),还会创建一个新文档。这是因为没有文档满足匹配条件

    有时,需要在创建文档的同时创建字段并为它赋值,但是在之后的所有更新操作中,这个字段的值都不再改变。这就是"$setOnInsert"的作用。"$setOnInsert"只会在文档插入时设置字段的值。因此,实际使用中可以这么做:

> db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 1,
        "nModified" : 0,
        "_id" : ObjectId("5ab0176c030c32b899e4e97e")
})
> db.users.findOne()
{
        "_id" : ObjectId("5ab0176c030c32b899e4e97e"),
        "createdAt" : ISODate("2018-03-19T20:02:52.061Z")
}

    如果再次运行这个更新,会匹配到这个已存在的文档,所以不会在插入文档,因此"createdAt"字段的值也不会改变:

> db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.users.findOne()
{
        "_id" : ObjectId("5ab0176c030c32b899e4e97e"),
        "createdAt" : ISODate("2018-03-19T20:02:52.061Z")
}

    通常不需要保留"createdAt"这样的字段,因为ObjectIds里包含了一个用于标明文档创建时间的时间戳.但是,在预置或者初始化计数器时,或者是对于不使用ObjectIds的集合来说,"$setOnInsert"是非常有用的。

    save是一个shell函数,如果文档不存在,它会自动创建文档;如果文档存在,它就会更新这个文档。他只有一个参数:文档。要是这个文档含有"_id"键,save会调用upsert。否则,会调用insert。如果在Shell中使用这个函数,就能方便地对文档进行快速修改。

    要是不用save的话,最后一行代码看起来就会比较繁琐了,比如db.foo.update({"_id":x._id},x)。

3.3.4 更新多个文档

    默认情况下,更新只能对符合匹配条件的第一个文档进行操作。要是有多个文档符合条件,只有第一个文档会被更新,其他文档不会发生变化。要更新所有匹配的文档,可以将update的第4个参数设置成true。

> db.users.update({"birthday":"10/13/1978"},
... {"$set":{"gift":"Happy birthday!"}},false,true)

    这样就给生日为1978年10月13日的所有用户文档添加了"gift"键。

    可以运行getLastError命令。键"n"的值就是被更新文档的数量。


    这里"n"为5,说明有5个文档被更新了。"updatedExisting"为true,说明是对已有的文档进行更新。

3.3.5 返回被更新的文档

    findAndModify命令可以得到被更新的文档。

    假设有一个集合,其中包含以一定顺序运行的进程。其中每个进程都用如下形式的文档表示:

    "status"是一个字符串,它的值可以是"READY","RUNNING"或"DONE"。需要找到状态为"READY"具有最高优先级的任务,运行相应的进程函数,然后将其状态更新为"DONE"。也可能需要查询已经就绪的进程,按照优先级排序,然后将优先级最高的进程的状态更新为"RUNNING"。完成了以后,就把状态改为"DONE"。


    这个算法不是很好,可能会导致竞态条件。假设有两个线程正在进行。A线程读取了文档,B线程在A将文档状态改为"RUNNING"之前也读取了同一个文档,这样两个线程会运行相同的处理过程。



    这样有可能一个线程处理了所有任务,而另外一个就傻傻的呆在那里。A线程可能会一直占用着进程,B进程试着抢占失败后,就让A线程自己处理所有任务了。

    findAndModify能够在一个操作中返回匹配结果并且进行更新。


    注意,返回文档中的状态仍然为"READY",因为findAndModify返回的是修改之前的文档。要是再在集合上进行一次查询,会发现这个文档的"status"已经更新成了"RUNNING":


    这样的话,程序就变成了下面这样:

    例如,现在不用更新状态了,而是直接删掉,就可以像下面这样:


    findAndModify命令有很多可以使用的字段。

  • findAndModify

字符串,集合名

  • query

查询文档,用于检索文档的条件。

  • sort

排序结果的文件

  • update

修改器文档,用于对匹配的文档进行更新(update和remove必须指定一个)。

  • remove

布尔类型,表示是否删除文档(remove和update)必须指定一个

  • new

布尔类型,表示返回更新前的文档还是更新后的文档。默认是更新前的文档。

  • fields

文档中需要返回的字段(可选)

  • upsert

布尔类型,值为true时表示这是一个upsert。默认为false。

3.4 写入安全机制

    写入安全(Write Concern)是一种客户端设置,用于控制写入的安全级别。默认情况下,插入、删除和更新都会一直等待数据库响应(写入是否成功),然后才会继续执行。通常,遇到错误时,客户端会抛出一个异常。两种最基本的写入安全机制是应答式写入(acknowledged write)和非应答写入(unacknowledged write)。应答式写入是默认的方式:数据库会给出响应,告诉你写入操作是否成功执行。非应答式写入不返回任何响应,所以无法知道写入是否成功。









































































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值