MongoDB 数组元素增删改
Posted Leshami
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MongoDB 数组元素增删改相关的知识,希望对你有一定的参考价值。
与关系型数据库相比,MongoDB支持数组,将数组存储到文档之中。因此,与之对应的是数组的增删改查。对于有C语言基础的童鞋,数组应该不会陌生。数组的增删改查,在MongoDB中有相应的操作符来实现。本文主要描述数组的增删改相关操作符的使用。
有关数组的查询可以参考:MongoDB 数组查询
1、占位符$
占位符$的作用主要是用于返回数组中第一个匹配的数组元素值(子集),重点是第一个
在更新时未显示指定数组中元素位置的情形下,占位符$用于识别元素的位置
通过数组过滤条件找到的第一个匹配的元素值的文档将被更新
使用示例
> db.students.insertMany([
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] ,
"_id" : 2, "semester" : 1, "grades" : [ 90, 90, 92 ] ,
"_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] ,
"_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] ,
"_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] ,
"_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] ])
使用样式:
db.collection.find( <array>: <value> ... ,
"<array>.$": 1 )
db.collection.find( <array.field>: <value> ...,
"<array>.$": 1 )
更新元素值样式:
db.collection.update(
<array>: value ... ,
<update operator>: "<array>.$" : value
)
更新数组内嵌文档样式(使用.$.成员方式)
db.collection.update(
<query selector> ,
<update operator>: "array.$.field" : value
)
更新操作时的占位符$匹配查询文档的第一个元素
数组字段必须为查询的过滤条件
更新数组元素值
//下面查询semester值为1,grades为90的文档
//如下结果,所有包含90的文档都被返回
> db.students.find( semester: 1, grades: $eq: 90 )//等价:db.students.find(semester:1,grades:90)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ]
"_id" : 2, "semester" : 1, "grades" : [ 90, 90, 92 ]
"_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ]
//下面通过占位符$来过滤数组的结果,也即是只有第一个为90的值才会被显示,其余的元素值不被显示
> db.students.find( semester: 1, grades: $eq: 90 , "grades.$": 1 )
"_id" : 1, "grades" : [ 90 ]
"_id" : 2, "grades" : [ 90 ] //注意这里显示的是该文档的第一个90
"_id" : 3, "grades" : [ 90 ]
//下面使用占位符进行更新
> db.students.update(semester: 1, grades:90,$set:"grades.$":95)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 ) //提示1个文档被更新
//如下文档_id为1的数组值被更新为95
> db.students.find( semester: 1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ]
"_id" : 2, "semester" : 1, "grades" : [ 90, 90, 92 ]
"_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ]
//下面使用multi:true选项,如下可知,2个匹配文档被更新
//但是仅仅是第一个元素值被更新(文档_id为2数值值为90的第二个元素未更新)
> db.students.update(semester: 1, grades:90,$set:"grades.$":95,multi:true)
WriteResult( "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 )
> db.students.find( semester: 1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ]
"_id" : 2, "semester" : 1, "grades" : [ 95, 90, 92 ]
"_id" : 3, "semester" : 1, "grades" : [ 85, 100, 95 ]
更新数组内嵌文档
//向集合增加一个文档
> db.students.insert(
...
... _id:7,
... grades: [
... grade: 80, mean: 75, std: 8 ,
... grade: 85, mean: 90, std: 5 ,
... grade: 90, mean: 85, std: 3
... ]
... )
WriteResult( "nInserted" : 1 )
//如下所示,使用数组名.$.成员名的方式进行更新
//
> db.students.update(
... _id: 7, "grades.grade": 85 ,
... $set: "grades.$.std" : 6 )
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("grades.grade":85, "grades.$": 1 )
"_id" : 7, "grades" : [ "grade" : 85, "mean" : 90, "std" : 6 ]
更新数组内嵌文档(多条件匹配)
//对于多条件的匹配,使用$elemMatch
> db.students.update("_id":7,grades:$elemMatch:grade:$lte:90,mean:$gt:80,
... $set: "grades.$.std" : 6 )
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 ) //提示一条被更新
//验证结果,尽管有2个内嵌文档满足查询条件,但是只有第一个被更新
> db.students.find("grades.std":6).pretty()
"_id" : 7, // Author: Leshami
"grades" : [ // Blog : http://blog.csdn.net/leshami
"grade" : 80,
"mean" : 75,
"std" : 8
,
"grade" : 85,
"mean" : 90,
"std" : 6
,
"grade" : 90,
"mean" : 85,
"std" : 3
]
2、操作符$addToSet
当数组元素值不存在的时候,将该值添加到数组。否则,什么也不做。
样式:
$addToSet: <field1>: <value1>, ...
$addToSet确保没有重复的项添加到数组集合,对于已经存在的重复元素不受影响。
$addToSet不能保证添加时元素的顺序
如果<field1>为空,操作失败,如果添加的值是数组,那么整个值将作为一个单个元素被追加
> db.students.update("_id":6,$addToSet:grades:99)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("_id":6)
"_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96, 99 ]
//下面尝试再次添加99,如下提示有匹配,但是没有更新
> db.students.update("_id":6,$addToSet:grades:99)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 )
//下面的将grades:[100,101]作为一个整体元素添加到数组
> db.students.update("_id":6,$addToSet:grades:[100,101])
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
//验证,100,101作为了一个整体,而非单个元素
> db.students.find("_id":6)
"_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96, 99, [ 100, 101 ] ]
//结合$each操作符使用$addToSet
//$addToSet结合$each,允许$addToSet一次添加多个值到数组
//如下,我们需要将[96,99,100,101]这几个值添加到数组
>db.students.update("_id":6,$addToSet:grades:$each:[96,99,100,101])
//如下面的查询结果,96,99元素值由于之前已经存在,因此未被添加,而100,101则被添加
> db.students.update("_id":6,$addToSet:grades:$each:[96,99,100,101])
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("_id":6)
"_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96, 99, [ 100, 101 ], 100, 101 ]
3、操作符$push
$push添加一个指定的值到数组,如果是嵌套文档,使用.成员方式
样式:
$push: <field1>: <value1>, ...
行为:
如果被更新的文档该数组不存在,那么$push将添加数组字段和值
如果字段不是一个数组,操作失败
如果值是数组,那么整个数组作为一个单个元素添加到数组
$push的修改顺序(参考本小点后面的综合示例)
将添加的元素更新到数组
如果指定了排序,那么应用排序
如果指定了slice,那么切割数组
存储数组
示例:
//字段不存在时的情形
> db.students.update("_id":1,$push:score:80)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
//查看添加后的结果,score作为一个数组字段添加到文档
> db.students.find("_id":1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 80 ]
//再次对score字段进行$push
> db.students.update("_id":1,$push:score:90)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
//查看添加后的结果,由于score数组字段存在,因此为其追加数组元素90
> db.students.find("_id":1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 80, 90 ]
> //$push结合$each,一次向数组push多个元素值,相同的元素值并没有忽略
> //这里是$push与$addToSet最大的一个差别,一个滤重,一个不滤重
> db.students.update("_id":1,$push:score:$each:[80,100,120])
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("_id":1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 80, 90, 80, 100, 120 ]
//$push结合$each,$slice,$sort的综合示例
//首先添加文档到集合
> db.students.insert(
"_id" : 8,
"quizzes" : [
"wk": 1, "score" : 10 ,
"wk": 2, "score" : 8 ,
"wk": 3, "score" : 5 ,
"wk": 4, "score" : 6
]
)
//使用如下更新操作
> db.students.update(
_id: 8 ,
$push:
quizzes: //下面使用$each为数组添加3个元素
$each: [ wk: 5, score: 8 , wk: 6, score: 7 , wk: 7, score: 6 ],
$sort: score: -1 , //基于socre倒叙排列
$slice: 3 //仅保留3个元素
)
//更新后的结果如下,score,10,8,8被保留,其余的被删除
> db.students.find("_id":8).pretty()
"_id" : 8,
"quizzes" : [
"wk" : 1,
"score" : 10
,
"wk" : 2,
"score" : 8
,
"wk" : 5,
"score" : 8
]
4、操作符$pop
$pop操作符移除数组中的第一个或者最后一个元素(1为最后一个元素,-1为第一个元素)
如果是数组为嵌套文档,使用.成员方式
样式:
$pop: <field>: <-1 | 1>, ...
//查看"_id"值为1的文档
> db.students.find("_id":1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 80, 90, 80, 100, 120, 80, 100, 120 ]
//移除score最后一个元素
> db.students.update("_id":1,$pop:score:1)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("_id":1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 80, 90, 80, 100, 120, 80, 100 ]
//移除score第一个元素
> db.students.update("_id":1,$pop:score:-1)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("_id":1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 90, 80, 100, 120, 80, 100 ]
//移除嵌套数组的最后一个元素
> db.students.update("_id":8,$pop:quizzes:1)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("_id":8)
"_id" : 8, "quizzes" : [ "wk" : 1, "score" : 10 , "wk" : 2, "score" : 8 ]
5、操作符$pull
$pull操作符从现有数组中移除与指定条件匹配的值或值的所有实例
样式:
$pull: <field1>: <value|condition>, <field2>: <value|condition>, ...
说明:
如果指定的<condition>数组元素为内嵌文档时,$pull操作符应用<condition>,类似每个数组元素是集合中的文档一样
如果指定的<value>去移除数组,$pull仅仅移除满足指定条件的数组元素(精确匹配,包括顺序)
如果指定的<value>去移除一个文档,$pull仅仅移除字段和值精确匹配的数组元素素(顺序可以不同)
示例:
移除所有特定元素值
> db.students.find("_id":6)
"_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96, 99, [ 100, 101 ], 100, 101 ]
//移除单个数组元素值
> db.students.update("_id":6,$pull:grades:99)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
//移除嵌套数组元素值
> db.students.update("_id":6,$pull:grades:[100,101])
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
//验证结果
> db.students.find("_id":6)
"_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96, 100, 101 ]
//下面使用$in来移除所有匹配元素
> db.students.update("_id":6,$pull:grades:$in:[95,96])
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
//验证结果
> db.students.find("_id":6)
"_id" : 6, "semester" : 2, "grades" : [ 90, 100, 101 ]
移除所有匹配的特定条件的数字元素
//移除数组grades中大于等于100的元素
> db.students.update("_id":6,$pull:grades:$gte:100)
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
//如下查询100,101元素值已经不存在了
> db.students.find("_id":6)
"_id" : 6, "semester" : 2, "grades" : [ 90 ]
移除文档型数组项目
> db.survey.insert([
_id: 1,
results: [
item: "A", score: 5 ,
item: "B", score: 8, comment: "Strongly agree"
]
,
_id: 2,
results: [
item: "C", score: 8, comment: "Strongly agree" ,
item: "B", score: 4
]
])
//下面的update操作,移除results数组中嵌套文档score的值为8,item值为B的数值元素
> db.survey.update(
,
$pull: results: score: 8 , item: "B" ,
multi: true
)
WriteResult( "nMatched" : 2, "nUpserted" : 0, "nModified" : 1 ) //2个匹配,1个更新
> db.survey.find().pretty()
"_id" : 1, "results" : [ "item" : "A", "score" : 5 ]
"_id" : 2,
"results" : [
"item" : "C",
"score" : 8,
"comment" : "Strongly agree"
,
"item" : "B",
"score" : 4
]
6、操作符$pullAll
该操作符从一个存在的数组中移除指定值得所有实例(即一次删除多个值)。
$pull操作符是移除一个指定查询的元素,$pullAll移除匹配列出值得所有元素
样式:
$pullAll: <field1>: [ <value1>, <value2> ... ], ...
说明:
当指定的<field>是一个内嵌文档或数组时,使用.成员方式
如果<value>是一个文档或者数组,$pullAll仅仅精确移除匹配指定<value>数组的元素(顺序要求匹配)
示例
> db.students.find("_id":$in:[1,5])
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 90, 80, 100, 120, 80, 100 ]
> db.students.find("_id":1)
"_id" : 1, "semester" : 1, "grades" : [ 70, 87, 95 ], "score" : [ 90, 80, 80 ]
> db.students.find("_id":5).pretty()
"_id" : 5,
"semester" : 2,
"grades" : [
88,
88,
92
],
"quizzes" : [
"wk" : 5,
"score" : 8
,
"wk" : 6,
"score" : 7
,
"wk" : 7,
"score" : 6
]
> db.students.update("_id":5,$pullAll:"quizzes":["wk" : 5,"score" : 8])
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.update("_id":5,$pullAll:"quizzes":["wk" : 7,"score" : 6])
WriteResult( "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 )
> db.students.find("_id":5).pretty()
"_id" : 5,
"semester" : 2,
"grades" : [
88,
88,
92
],
"quizzes" : [
"wk" : 6,
"score" : 7
]
以上是关于MongoDB 数组元素增删改的主要内容,如果未能解决你的问题,请参考以下文章