MongoDB 数组
MongoDB是文档型数据库,每个文档(doc)表示数据的一项记录。相比关系型DB的row只能使用简单的数据类型,doc能够使用复杂的数据类型:内嵌doc,数组。MongoDB的数组是一系列元素的集合,使用中括号 [] 表示数组,例如:[1,2,3]的元素是整数值,[{name:"t5"}, {name:"t7"}],[ {name:"t5", age:21}, {name:"t7", age:22} ]的元素是doc。
在MongoDB中,数组元素允许重复,元素的位置是固定的。如果两个数组相等,那么这两个数组的元素和及其位置都相同。
创建示例collection,使用db.collection.insert()函数和数组参数,一次性向集合中插入3个doc。
user1={ name:"t1", age:21}
user2={ name:"t2", age:22}
user3={ name:"t3", age:23}
db.users.insert([user1,user2,user3])
一,使用dot标记法(dot notation)访问数组元素
MongoDB使用 “.” 方法访问数组的元素,或内嵌doc字段。
users=
{
name:"t1",
age:21,
address: {phone:123,email:"xxx@163.com"},
followers:[{name:"a"},{name:"b"},{name:"c"}]
}
address 字段是内嵌文档,查找phone是123的doc:
db.users.find({"addr.phone":123})
followers 字段是数组,查询followers中存在name=“b”的doc:
db.users.find({"followers.name":"b"})
二,修改字段的值
在MongoDB中,修改操作主要使用两个修改器:set和inc,这两个修改器具有upsert特性:如果doc中存在相应的字段,那么修改该字段的值;如果doc中不存在相应的字段,那么在doc中创建新的字段。$inc用于整数型字段,增加或减少字段的值,$set用于任意数据类型,替换字段的值。
1,使用$set修改器,增加followers数组,使用空过滤器:{},表示更新集合中的所有doc
db.users.updateMany(
{},
{$set:
{
followers:[ {name:"t5"},{name:"t7"} ]
}
}
)
2,使用$inc修改器,增加age字段的值,每个doc的age字段的都增加1:
db.users.updateMany(
{},
{$inc:{age:1}}
)
3,set和inc的不同之处在于:set是替换现有字段的值,而inc是在现有字段值的基础上,增加或减少指定的数值
示例,使用$set修改age的值,更新结束后,每个doc的age字段都是1
db.users.updateMany(
{},
{$set:{age:1}}
)
修改数组
如果要向数组中增加或删除一个元素,set和set和inc 都不能很好的满足这种需求,MongoDB有专用的数组操作方法,用于修改数组字段。
1,使用$push向doc中增加数组,或插入新的元素
$push:如果doc中存在相应的数组字段,那么向数组的尾部插入一个元素;如果doc中不存在相应的数组字段,那么向doc中创建一个数组字段,并初始化。
示例,第一次调用$push,由于doc中不存在comments字段,因此MongoDB向doc中新建comments 数组字段,并初始化数组:
db.users.updateMany(
{},
{$push:{comments:{msg:"c1"}}}
)
后续再次调用$push,向已有的数组字段的尾部追加一个元素:
db.users.updateMany(
{},
{$push:{comments:{msg:"c2"}}}
)
图示,左边是数组的第一个元素,右边是数组的最后一个元素,使用$push每次向数组的尾部追加一个元素:
2,向数组字段插入多个元素
push修改器每次只能向数组字段的尾部插入一个元素,搭配使用each 修改器,每次能向数组字段中插入多个元素
db.users.updateMany(
{},
{$push:
{
comments:{ $each:[ {msg:"c3"}, {msg:"c4"} ] }
}
}
)
复制代码
图示,使用$each,一次将多个元素插入到数组的尾部:
3,从数组字段的特定位置处开始插入元素
使用push修改器只能将元素插入到数组字段的尾部,搭配使用position 修改器,能够指定元素插入的开始位置,position必须和each搭配使用。数组的下标是从0开始的。
db.users.updateMany
(
{},
{$push:
{comments:
{
$each:[ { msg:"c5"}, {msg:"c6"} ],
$position:2
}
}
}
)
图示,使用$position 指定元素插入的开始位置,将c5,c6 依次插入都数组的2,3位置处,由于数组的小标是从0开始的,c5,c6 是数组的第3,4个元素:
如果不使用position修改器,那么push 每次都向数组的末尾写入元素;使用position修改器,可以指定push插入元素的开始位置。
4,限制数组中元素的数量
在push元素时,使用slice=MaxNum限制数组元素的最大数量。只要没有达到最大数量,就会向数组中插入新的元素,直到达到最大值。slice必须和each搭配使用,如果数组字段的元素的数量已经达到最大值,根据MaxNum值的不同,会有不同的行为:
- 如果MaxNum=0,表示将数组清空;
- 如果MaxNum是正整数,表示数组只保留前面的MaxNum个元素;
- 如果MaxNum是负整数,表示数组只保留后面的MaxNum个元素;
示例,保留每个comments的最后5个元素:
db.users.updateMany(
{},
{$push:
{comments:
{
$each:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}],
$slice:-5
}
}
}
)
图示,$slice:-5,MongoDB先将新的元素插入到数组中,将保留数组末尾的5个元素,将数组的其他元素删除:
5,对数组字段的元素进行排序
在限制数组字段的元素数量之前,使用sort操作符对元素进行排序,使数组元素有序排列。在sort操作之后使用 $slice:MaxNum 修改器,由于数组元素是有序的,能够只保留序列前面或后面的特定数量的元素。
db.users.updateMany(
{},
{$push:
{comments:
{
$each:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}],
$sort:{msg:-1},
$slice:-5
}
}
}
)
图示,each向现有的数组的尾部中插入三个元素:c7,c8,c9,sort:{msg:-1} 对数组按照msg的降序排序,$slice:-5 操作符限制数组的元素数量,只保留数组尾部的5个元素:
如果数组是[1,2,3]这种类型,那么:
- sort:1,按照1,2,3升序排列
- sort:-1,安装3,2,1 降序排列
6,使用$addToSet向数组插入无重复的元素
通过push插入元素,有可能插入重复的元素,MongoDB允许数组中的元素重复;如果一个数组不能插入重复值,可以使用addToSet修改器, addToSet在向数组插入元素时,首先检查元素是否已经存在于数组中,如果不存在,那么addToSet将元素插入到数组中;如果存在,那么addToSet不会插入任何元素。addToSet只会保证不会插入重复的元素,不应影响数组中已经存在的重复元素。
$addToSet 能够一次性向数组中插入多个元素。
示例,向comments 数组中插入三个messge:
db.users.updateMany(
{},
{$addToSet:
{comments:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}] }
}
)
7,使用$pop删除数组的第一个或最后一个元素
把数组看作是队列,下标为0的元素是在队列头部,是数组的第一个元素,小标最大的元素是数组的最后一个元素。使用$pop删除元素时,{$pop:{array:1}} 表示删除数组的最后一个元素,{$pop:{array:-1}} 表示删除数组的第一个元素。
db.users.updateMany(
{},
{$pop:{comments:1}}
)
图示,删除数组的最后一个元素:
8,根据查询过滤器删除数组元素
db.users.updateMany(
{},
{$pull:{comments:{msg:"c7"}}}
)
图示,删除数组中msg字段是"c7"的所有元素
9,根据数组的下标修改元素,数组下标是从0开始的
对于javaScript的数组 arr,包含两个element,修改第一个元素的like 字段,将其值设置为2.
var arr=[{name:"t1",like:1},{name:"t2",like:2}]
arr[0].like=2
print(tojoson(arr))
在MongoDB中,如果要修改doc中的数组,可以使用 dot notation,使用 arrary.index.field 对数组中特定位置的元素进行修改。
db.users.updateMany(
{},
{$inc:{"comments.0.likes":1}}
)
图示,向数组的第一个元素中增加likes字段,并初始化为1:
如果不知道数组元素的下标,MongoDB提供占位符 ,用于表示从数组中查找满足queryfilter的第一个元素。占位符,需要对数组进行查找,查找的query filter必须显式提供,如果存在数据元素满足query filter,那么$ 占位符表示第一个匹配的数组元素的position,如果没有数组元素满足query filter,那么MongoDB不会对数据任何作用。
$占位符的使用格式,跟数组的元素类型有关:
- 如果数组元素是doc,那么使用 $ 占位符的格式是:arrary.$.field
- 如果数组的元素类型是原子类型,例如,[1,2,3]等,那么使用那么使用 $ 占用符的格式是:arrary.$
示例1,使用empty filter作为query filter:
db.users.updateMany(
{},
{$inc:{"comments.$.unlikes":1}}
)
MongoDB抛出错误消息:
"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: comments.$.unlikes"
说明 query filter 不能使用empty filter,必须显式提供query filter。
示例2,对数组元素进行查询,只要存在任何一个元素的msg字段的值c4,就在该元素中增加一个unlikes字段,并初始化为1:
db.users.updateMany(
{comments:
{$elemMatch:{msg:"c4"}}
},
{$inc:
{"comments.$.unlikes":1}
}
)
图示,$ 占位符表示匹配query filter的第一个元素:
数组的查询
1,元素匹配符 $elemMatch,使用数组元素进行条件匹配
$elemMatch 是对数组元素的字段进行匹配,如果元素或元素的字段满足查询条件,那么返回该元素所在的doc。
格式是:{array:{$elemMatch:{field_query_filter,,,,}}}
db.users.find({comments:{$elemMatch:{like:1}}})
示例1,数组元素是整数类型(原子类型):
{ _id: 1, results: [ 82, 85, 88 ] }
{ _id: 2, results: [ 75, 88, 89 ] }
db.scores.find(
{ results: { $elemMatch: { $gte: 80, $lt: 85 } } }
)
查询结果是:只有_id为1的doc的数组元素82满足query filter:
{ "_id" : 1, "results" : [ 82, 85, 88 ] }
示例2,数组元素是doc:
{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }
db.survey.find(
{ results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } }
)
查询结果是:
{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "xyz", "score" : 8 } ] }
2,数组的比较,使用数组进行条件匹配
使用数组进行比较时,只要数组中的任何元素满足query filter,就匹配成功。
如果有以下三个doc,每个doc中都有一个grades 数组:
{ "_id" : 1, "grades" : [ 80, 85, 90 ] }
{ "_id" : 2, "grades" : [ 88, 90, 92 ] }
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
示例1,对于query filter:{grades:{gt:85,gt:85,lt:100}},分析这3个数组是否满足:
- 第1个数组:元素 90 满足大于 85,所有的元素都小于100
- 第2个数组:所有的元素满足条件
- 第3个数组:元素90,100 满足大于85的条件,元素85,90满足小于100的条件
因此,只要数组中有任何一个元素满足qeury filter,就算满足qeury filter,这3个数组都满足query filter。
示例2,query filter:{grades:90}
只要数组中有一个元素的值是90,就满足query filter,因此,这3个数组都满足条件。
3,查询数组元素的数量
size操作符,如果doc中存在数组,并且数组的元素满足size操作符,如果doc中存在数组,并且数组的元素满足size指定的条件,那么返回该doc。
db.collection.find( { array: { $size: n } } );
4,数组包含指定的多个元素
{ array : { $all: [ <value1> , <value2> ... ] } }
$all 表示集合的包含关系,全集包含子集的所有元素。如果数组A包含数组B,那么A是B的全集,B是A的子集。子集中的所有元素,都存在于全集;全集中的元素,不一定存在于子集。
如果array包含指定的数组,那么满足$all条件,返回doc,表示指定数组的元素都存在于array。
示例1,如果两个数组相等,那么这两个数组的元素和及其位置都相同,即在数组的相同位置上,其元素相同:
这三个数组互不相同,arr1 和 arr2 元素数量相同,但是存在相同位置上(下标是:1,2)的元素不同;
arr1=[1,2,3]
arr2=[1,3,2]
arr3=[1,2]
示例2,包含关系(数组元素是int,字符串等原子类型)
$all表示的是包含关系,对于arr=[1,2,3],满足条件{arr:{$all:[2,1]}},arr存在元素1,2。
示例,查询数组中同时存在2,3的doc
{ "_id" : 1, "g" : [ 1, 2, 3 ] }
{ "_id" : 2, "g" : [ 4, 2, 3 ] }
{ "_id" : 3, "g" : [ 4, 2, 5 ] }
db.foo.find({g:{$all:[2,3]}})
查询结果是:
{ "_id" : 1, "g" : [ 1, 2, 3 ] }
{ "_id" : 2, "g" : [ 4, 2, 3 ] }
示例3,包含关系(数组元素是doc)
示例,对于以下集合,每个doc中都有一个数组字段qty,每个数组中包含三个元素,每个元素都是内嵌doc:
{_id:1,
qty: [
{ size: "S", num: 10, color: "yellow" },
{ size: "M", num: 45, color: "blue" },
{ size: "L", num: 100, color: "green" }
]
}
{_id:2,
qty: [
{ size: "S", num: 10, color: "blue" },
{ size: "M", num: 100, color: "red" },
{ size: "L", num: 100, color: "green" }
]
}
查询数组元素中color字段同时存在blue 和 green的doc,这两个doc都满足条件:
db.foo.find({"qty.color":{$all:["blue","green"]}})
手机扫码阅读本文
本文来自互联网,本网站转载的目的在于传递更多信息以供访问者学习参考,所属内容只代表原作者的个人观点,不代表本网站的立场和价值判断,版权归原作者所有。如有侵犯您的版权,请联系我们,我们收到后会尽快核实并第一时间改正。