文章目录

  • 一、什么修改器:
  • 二、修改器可分类两大类:
  • 字段修改器:
  • 数组修改器:
  • 三、字段修改器
  • 3.1、`$set` 修改某个键值
  • 3.2、`$set`用来修改内嵌文档
  • 3.3、`$unset` 将键删除
  • 3.4、`$inc` 对数字增加或减少
  • 四、数组修改器
  • 4.1 ` $push` 向数组末尾添加一个元素
  • 4.2、`$addToSet` 向数据中添加数据,并去重
  • 4.3、` $each` 一次添加多个不同的值
  • 4.4、`$pop` 从数组的头部或尾部删除一个元素
  • 4.5、`$pull` 删除数组中所有匹配的元素
  • 4.6、数组的定位修改器 `$`
  • 4.6.1、通过下标位置:
  • 4.6.2、定位操作符 `$` :



在mongodb中,通常文档只会有一部分数据要更新。例如要把"foo"的值修改为"bar",常见的错误做法如下:

db.coll.update(criteria,{"foo":"bar"})

这个语句是不对的。
会把整个文档被替换为{"foo":"bar"},一定要使用以$开头的修改器来修改键/值对。

一、什么修改器:

修改器是种特殊的键,用来指定复杂的更新操作,比如调整、增加、或者删除键,还可以操作数组和内嵌文档。
可以做到只更新文档的一部分键值,而且更新极为高效。

二、修改器可分类两大类:

字段修改器:

$set$unset$inc$rename

数组修改器:

$push$pullAll$addToSet$each$pop$pull$

三、字段修改器

3.1、$set 修改某个键值

$set 修改器用来指定一个键值。如果这个键不存在,则创建他,他对更新模式或者用户定义键来说非常方便。

> db.users.findOne()
{
        "_id" : ObjectId("56fe7df8b322e3ff1dabf834"),
        "name" : "joe",
        "age" : 30,
        "sex" : "male",
        "location" : "Wisconsin",
        "favorite book" : "war and pace"
}

> db.users.update({"name":"joe"},{"$set":{"favorite book":["cat's cardle","foundation trilogy","ender's game"]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("56fe7df8b322e3ff1dabf834"),
        "name" : "joe",
        "age" : 30,
        "sex" : "male",
        "location" : "Wisconsin",
        "favorite book" : [
                "cat's cardle",
                "foundation trilogy",
                "ender's game"
        ]
}

3.2、$set用来修改内嵌文档

> db.blog.insert({"title":"a blog post","author":{"name":"joe","email":"joe@example.com"}})
WriteResult({ "nInserted" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "author" : {
                "name" : "joe",
                "email" : "joe@example.com"
        }
}

> db.blog.update({"author.name":"joe"},{"$set":{"author.name":"joe schmoe"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "author" : {
                "name" : "joe schmoe",
                "email" : "joe@example.com"
        }
}

> db.blog.update({"title":"a blog post"},{"$set":{"author.name":"joe schmoe op"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "author" : {
                "name" : "joe schmoe op",
                "email" : "joe@example.com"
        }
}
>

3.3、$unset 将键删除

$unset 用于将键和对应的数据全部删除

格式:
{$unset:{filed:1}} :
field 后面对应的可以填0,也可以填1或其他数值,但效果都是将该键从这条数据中删除。

> db.users.findOne()
{
        "_id" : ObjectId("56fe7df8b322e3ff1dabf834"),
        "name" : "joe",
        "age" : 30,
        "sex" : "male",
        "location" : "Wisconsin",
        "favorite book" : [
                "cat's cardle",
                "foundation trilogy",
                "ender's game"
        ]
}

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

3.4、$inc 对数字增加或减少

$inc 其用来增加或减少已有的键的键值,或者在键不存在的时候创建一个键。

格式:
{$inc:{filed:value}},为指定的键对应的数值进行加减

> db.games.insert({"game":"pinball","user":"joe"})
WriteResult({ "nInserted" : 1 })
> db.games.findOne()
{
        "_id" : ObjectId("5770a1394f533aa7535d46d4"),
        "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("5770a1394f533aa7535d46d4"),
        "game" : "pinball",
        "user" : "joe",
        "score" : 50
}

为"score"键增加 50 再减少 20

> db.games.update({"game":"pinball","user":"joe"},{"$inc":{"score":50}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne()
{
        "_id" : ObjectId("5770a1394f533aa7535d46d4"),
        "game" : "pinball",
        "user" : "joe",
        "score" : 100
}

> db.games.update({"game":"pinball","user":"joe"},{"$inc":{"score":-20}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne()
{
        "_id" : ObjectId("5770a1394f533aa7535d46d4"),
        "game" : "pinball",
        "user" : "joe",
        "score" : 80
}

以上 $inc$set 的用法类似,就是专门用来增加或减少数字的。
$inc 只能用于整数、长整数或双精度浮点数,要是在其他类型的数据上就会导致操作失败,其中包括很多语言会自动转换成数字的类型,例如 null,布尔类型,或数字构成的字符串。
$inc 键的值必须为数字,不能使用字符串、数组和其他非数字的值,否则会报错,要修改其他类型,只能使用$set

> db.foo.insert({"count":"1"})
WriteResult({ "nInserted" : 1 })
> db.foo.find()
{ "_id" : ObjectId("5770befc4f533aa7535d46d5"), "count" : "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('5770befc4f533aa7535d46d5')} has the field 'count' of non-numeric type String"
        }
})

> db.foo.update({},{$set:{count:2}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.foo.find()
{ "_id" : ObjectId("5770befc4f533aa7535d46d5"), "count" : 2 }
>

四、数组修改器

数组修改器,顾名思义它只可以用于操作数组,只能用在值为数组的键上。

4.1 $push 向数组末尾添加一个元素

$push 向数组末尾添加一个元素。
如果没有数组,就会创建一个新的数组,并添加数据。
不过滤重复的数据,数据可以重复。

格式:
{$push:{filed:value}} 中的field应为数组类型的,
如果field不是数组类型的,就会出错,
如果filed不存在,则创建该数组类型并插入数据。

> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "author" : {
                "name" : "joe schmoe op",
                "email" : "joe@example.com"
        }
}

> db.blog.update(
	{ "title" : "a blog post" }
	,{ "$unset" : { "author":1 }  }
)
 ## 删除了key是author的所有数据

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{ "_id" : ObjectId("57709da84f533aa7535d46d3"), "title" : "a blog post" } ## 删除了key是author的所有数据


> db.blog.update(
 { "title" : "a blog post" }
 ,{ "$push" : 
 		{  "comments" : {"name":"joe","email":"joe@example.com","content":"nice post"}   }
 	}
)  ## 添加key是comments的数组类型的数据

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post"
                }
        ]
}

为blog集合"a blog post"文档再增加一条评论:

> db.blog.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.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post"
                },
                {
                        "name" : "bob",
                        "email" : "bob@example.com",
                        "content" : "good post"
                }
        ]
}

4.2、$addToSet 向数据中添加数据,并去重

addToSet 可以理解为 add to Set ,添加元素到set集合中,set集合可以去重。所以,$addToSet 还有去重功能。

格式:
{$addToSet:{filed:value}} 向 field 目标 数组 中添加数据,
如果目标数组中已存在value,则什么都不操作,(避免重复) 。
如不存在,则添加进去。

实现的功能与 $ne 修改器相同,且比 $ne 更为方便。

$addToSet 不会对集合中已有的数据进行去重操作,而只对要添加的元素,在添加前先要查询value值是否存在。

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com"
        ]
}
> db.users.update(
	{ "_id" : ObjectId("5770ca42e90c1adc80040a08" )  }
	, { $addToSet : {"emails":"joe@gmail.com" }
})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })   
--原文档里已有"joe@gmail.com",修改完也没有产生重复值

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com"
        ]
}
> db.users.update(
	{"_id" : ObjectId("5770ca42e90c1adc80040a08") }
	,{ $addToSet : { "emails":"joe@163.com" }
})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com"
        ]
}

4.3、$each 一次添加多个不同的值

$each 数组修改器要和 $addToSet 修改结合起来用,可以一次添加多个不同的值。

例如下面的例子中,我们一次添加多个 email 值, 如下:

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com"
        ]
}
> db.users.update(
	{"_id":ObjectId("5770ca42e90c1adc80040a08")}
	,{ $addToSet:{ $each:["joe@example.com","joe@python.com","joe@php.com"]  }
})

WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 52,
                "errmsg" : "The dollar ($) prefixed field '$each' in '$each' is not valid for storage."
        }
})

## 上面报的是语法错误,必须指定key值,比如emails,来储存数据。

> db.users.update(
	{ "_id" : ObjectId("5770ca42e90c1adc80040a08")  }
	,{ $addToSet:{ "emails" : { $each:["joe@example.com","joe@python.com","joe@php.com"]  } }
})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com",
                "joe@python.com",
                "joe@php.com"
        ]
}
>

4.4、$pop 从数组的头部或尾部删除一个元素

$pop 通过位置(下标)删除数组元素。只能从数组的头部或尾部删除一个元素 。

格式:
{$pop:{filed:value}} ,field 为数组类型,value 为 1 或者 -1 ,
value 为 1 时,删除指定数组的最后一个值;
value 为 -1 删除指定数组第一个值。

{$pop:{key:1}} 从数组末尾删除一个元素
{$pop:{key:-1}} 从数组头部删除一个元素

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com",
                "joe@python.com",
                "joe@php.com"
        ]
}
> db.users.update( { "name":"joe" } ,  { $pop:{"emails":1} }  )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@example.com",
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com",
                "joe@python.com"
        ]
}
> db.users.update( { "name" : "joe" } , { $pop : { "emails" : -1 } }   )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com",
                "joe@python.com"
        ]
}

有时我们需要基于特定条件来删除元素,而不仅仅依据位置,就可以使用 $pull 修改器

4.5、$pull 删除数组中所有匹配的元素

$pull 删除数组元素,将所有匹配的元素删除。
$pop 修改类似,但是比 $pop强大的多。

例如我们想删除 emails 数组中的 joe@163.comjoe@itpub.com 两个元素:

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com",
                "joe@python.com"
        ]
}
> db.users.update( 
 	{"name":"joe"} 
	, { $pull : { "emails" :  [ "joe@163.com","joe@itpub.com"] } 
  })   

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })  --好像不能一次删除多个,没有起作用

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@itpub.com",
                "joe@163.com",
                "joe@python.com"
        ]
}
> db.users.update( {"name" : "joe" } , { $pull : { "emails" : "joe@163.com" } }  )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.users.update( {"name":"joe"} ,  {  $pull : {"emails":"joe@itpub.com"}  })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.users.findOne()
{
        "_id" : ObjectId("5770ca42e90c1adc80040a08"),
        "name" : "joe",
        "emails" : [
                "joe@gmail.com",
                "joe@yahoo.com",
                "joe@python.com"
        ]
}

4.6、数组的定位修改器 $

若是数组中有多个值,而我们只想对其中一部分进行操作,有两种方法可以实现这种操作。

两种方法操作数组中的值:

  • 通过下标位置
  • 定位操作符 $

4.6.1、通过下标位置:

数组都是以 0 开头的,可以将下标直接作为键来选择元素。

> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post"
                },
                {
                        "name" : "bob",
                        "email" : "bob@example.com",
                        "content" : "good post"
                }
        ]
}
> db.blog.update(
	{"title":"a blog post"}
	,{ $set : { "comments.1.name":"livan" } }
)

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post"
                },
                {
                        "name" : "livan",
                        "email" : "bob@example.com",
                        "content" : "good post"
                }
        ]
}
> db.blog.update(  {"title":"a blog post"} , { $set : { "comments.1.email":"livan@example.com"} }  )

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post"
                },
                {
                        "name" : "livan",
                        "email" : "livan@example.com",
                        "content" : "good post"
                }
        ]
}

4.6.2、定位操作符 $

在很多情况下,不预先查询文档就不能知道要修改数组的下标,为了克服这种困难,mongodb 提供了定位操作符 $,
用来定位查询文档已经匹配的元素,并进行更新,定位符只更新第一个匹配的元素。

例如:用户 john 把名字改成了 jim,就可以用定位符来替换评论中的名字:
db.blog.update({"comments.author":"john"},{$set:{"comments.$.author:"john"}})

可以理解为{"comments.author":"john"}查询条件定位到第一个元素,就执行{ $se t: {"comments.$.author:"john"} }$ 定位符就表示找到的第一个元素

> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post"
                },
                {
                        "name" : "livan",
                        "email" : "livan@example.com",
                        "content" : "good post"
                }
        ]
}

> db.blog.update({"comments.name":"livan"},{$set:{"comments.$.email":"bob@example.com"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.update({"comments.name":"livan"},{$set:{"comments.$.name":"bob"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("57709da84f533aa7535d46d3"),
        "title" : "a blog post",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@example.com",
                        "content" : "nice post"
                },
                {
                        "name" : "bob",
                        "email" : "bob@example.com",
                        "content" : "good post"
                }
        ]
}