MongoDb学习
基本概念
- 文档
文档是有序集合里面的一个键值对的有序集合。文档的键是字符串(除了少数情况,键可以用任何的UTF-8)。相当于关系型数据库当中的表table。 - 集合
集合就是一组文档,相当于关系型数据库当中的一行数据。
数据类型
- null
- 布尔型true or false
- 数值型
默认为64位的浮点型数值,如果要使用整形值,可以使用NumberInt类(表示4字节带符号整数)或NumberLong类(表示8字符带符号整数),如
{"x":NumberInt("3")}
{"x":NumberLong("3")} - 字符串
- 日期
如{“x”:new Date()} - 正则表达式,语法于JavaScript的正则表达式语法相同。
- 数组
数组列表或数据集可以表示为数组:
{"x":["a","b","c"]} - 内嵌文档
文档可以嵌套其他文档,被嵌套的文档作为父文档的值。如:
{"x":{"y":NumberInt("3")}} - 对象id
对象id是一个12字节的ID,是文档的唯一标识。
{"x",ObjectId()} - 二进制数据
- 代码
查询和文档可以包含任意的JavaScript代码:
{"x":function(){/* ... */}}
创建、更新和删除文档
操作
插入文档
插入一个文档db.foo.insert({"bar":"baz"})
批量插入db.foo.batchInsert({"_id":0},{"_id":1},{"_id":2})删除文档
删除文档db.foo.remove()db.foo.remove({"opt_out":true})
如果要删除全部的文档,使用drop直接删除集合会更快。更新文档
文档存入数据库后,就可以使用update方法来更新文档。update有两个参数,一个是查询文档,用于定位需要更新的目标文档;另一个是修改器文档,用于说明要对找到的文档进行那些修改。
文档的修改可以分为两种类型,一种是文档的替换,就是将文档内容整个地替换掉。另外一种是文档的部分字段的更新。在进行文档的部分字段的更新时,必须使用修改器,否则文档会被全部替换。1
2db.user.update({"name":"joe"},{"address":"12345"})
db.user.update({"name":"joe"},{"$set":{"address":"12345"}})第一个更新操作会将name为Joe的文档替换掉,第二个更新操作会将name为joe的文档的address内容替换为12345。因此,在修改文档的字段内容的时候,必须使用修改器。
update函数的第三个参数为一个布尔型的值,指示update函数是否为upset。当为true(即upset)时,要是没有找到符合更新条件的文档,就会以这个条件和更新文档为基础创建一个新的文档。如果找到了匹配的文档,则进行正常的更新。
update函数的第四个参数为一个布尔型的值,用于指示是否更新所有匹配的文档。update默认情况下只更新第一个匹配到的文档。因此,如果要更新所有匹配的文档,则需要将改参数设置为true。
常用的修改器
$set 用于设置文档中某一个键对应的值,如果对应的键不存在,则会新增加一个键值对。
$inc 用于增加已有键的值,如果对应的键不存在那就创建一个。$set只能用于整形,长整形或者双精度浮点型的值。
$push 用于操作数组。如果数组已经存在,$push会向已有的数组末尾加入一个元素,要是没有则会创建一个数组。
1
db.stocker.update({"_id":"GOOD"},{"$push":{"comment":"123"}})
如果_id为GOOD的文档中不存在comment键,则新创建一个,如果存在的话,则将123添加到comment键的值当中,成为一个数组。
也可以和$each一起使用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67db.stocker.update({"_id":"GOOD"},{"$push":{"comment":{"$each":["123","456","789"]}}})
```
    这样,三个字符串就都被添加到comment键对应的数组当中了。
- $ne $andToSet 当我们将数组座位数据集使用的时候,需要保证数组内的元素不会重复,可以使用$ne $andToSet。
- $pull 用于删除数组当中的元素。
#四. 查询
## find函数
    MongoDB数据查询使用find函数,其功能与SQL中的select函数相同,可提供与关系型数据库类似的许多功能,包含映射、排序等。
    find函数语法
>db.COLLECTION_NAME.find(query,fields,limit,skip)
    find() 方法以非结构化的方式来显示所有文档。
    如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:
>db.COLLECTION_NAME.find().pretty()
    pretty() 方法以格式化的方式来显示所有文档。
<font color='red'>**参数说明**</font>:
- query:指明查询条件,相当于SQL中的where语句。
`db.account.find({name:"lewesyang",age:{$it:22}})`
- fields:用于字段映射,指定是否返回该字段,0代表不返回,1代表返回
`db.account.find({name:"lewesyang",age:{$it:22}},{"age":0})`
- limit:限制查询结果集的文档数量,指定查询返回结果数量的上限
`db.account.find({name:"lewesyang",age:{$it:22}},{"age":0},5) `
- skip:跳过一定数据量的结果,设置第一条返回文档的偏移量
`db.account.find({name:"lewesyang",age:{$it:22}},{"age":0},5,10) `
    单独使用limit和skip语法:<br>
     `db.account.find().limit(5).skip(10)`
<font color='red'>**注意事项**</font>:
- MongoDB不支持多集合间的连接查询,find函数一次查询只能针对一个集合。
- find参数为空或者查询条件为空文档时,会返回集合中的所有文档。
- 除了将limit和skip作为find函数的参数外,还可以单独使用limit和skip函数来修饰查询结果。
- 返回的查询结果集默认情况下是无序的,如果需要对结果进行排序,可以使用sort函数:
`db.account.find().sort({age:-1})`
- db.collection.findOne()只会返回第一条数据。
- 当查询的集合文档数量很大时,为了加快数据的查询速度可以创建索引。
- 除了使用find函数实现基本查询外,MongoDB还提供了聚合框架,用于复杂查询。
##2. 查询条件
- 比较操作符
$gte,$gt,$lte,$lt,$ne,特别对应>=、>、<=、<。可以将其组合起来以方便查找一个范围的值。
`db.user.find("age":{"$gte":18,"$lte":25,"$ne":22})`
- OR查询
MongoDB当中有两种方式进行OR查询,$in可以用来查询一个键的多个值,$or更通用一些,可以在多个键当中查询任意的给定值,$nin的作用域$in相反。
`db.raffle.find({"age":{"$in":[1,2,3]}})`
`db.raffle.find({"$or":[{"age":12},{"sex":"man"}]})`
- $not
$not时原条件句,即可以用在任何其他条件上。
`db.raffle.find({"age":{"not":{"$in":[1,2,3]}}})`
作用和
`db.raffle.find({"age":{"$nin":[1,2,3]}})`
相同
- 条件语义
我们会发现以$开头的键位于不同的位置。在查询当中,"$lte"在内层文档,在更新当中,"$inc"位于外层文档。基本可以肯定:条件语句是内层文档的键,而修改器则是外层文档的键。有一些“元操作符”也位于外层文档中,比如“$and”,“$or”,“$nor”等。一个键可以有任意多个条件语句,但是一个健不能对应多个更新修改器。
##3. 特定类型的查询
###3.1 null
    null类型有些奇怪,它不仅能够匹配自身,也能够匹配不包含自己的键的值。例如:{“_id”:ObjectId(“123”),”y”:null}
{“_id”:ObjectId(“123”),”y”:1}
{“_id”:ObjectId(“123”),”y”:2}1
2
3
4
5
6
7
8
9
10
11
12
13
当我们使用查询语句:
`db.c.find({"y":null})`
时,不仅能够查询到第一条数据,也能够查询到第二条数据。因此,为了解决该问题,应该使用查询语句:
`db.c.find({"y":{"$in":[null],"$exists":true}})`
来进行查询。
###3.2 正则表达式
可以使用
`db.user.find({"name":/joe/i})`
来匹配name键值包含joe字串的文档。在使用正则表达式时,需要注意的是,正则表达式不区分大小写。MongDB使用Perl兼容的正则表达式(PCRE)库来匹配正则表达式,任何PCRE支持的正则表达式语法都能被Perl接受。
###3.3 查询数组
    查询数组很容易,对于数组,我们可以这样理解:数组中每一个元素都是这个键值对键的一个有效值,如下面的例子:我们要查询出售apple的水果店:db.fruitshop.find();
{ “_id” : ObjectId(“5022518d09248743250688e0”), “name” : “big fruit”, “fruits” : [ “apple”, “pear”, “orange” ] }
{ “_id” : ObjectId(“502251a309248743250688e1”), “name” : “good fruit”, “fruits” : [ “banana”, “pear”, “orange” ] }
{ “_id” : ObjectId(“502251c109248743250688e2”), “name” : “good fruit”, “fruits” : [ “banana”, “apple”, “tomato” ] }db.fruitshop.find({“fruits”:”apple”});
{ “_id” : ObjectId(“5022518d09248743250688e0”), “name” : “big fruit”, “fruits” : [ “apple”, “pear”, “orange” ] }
{ “_id” : ObjectId(“502251c109248743250688e2”), “name” : “good fruit”, “fruits” : [ “banana”, “apple”, “tomato” ] }1
    我们发现只要包含苹果的数组都能被查询出来。如果要通过多个元素来匹配数组,就需要条件操作符"$all",比如我们要查询既卖apple又卖banana的水果店:
db.fruitshop.find();
{ “_id” : ObjectId(“5022518d09248743250688e0”), “name” : “big fruit”, “fruits” : [ “apple”, “pear”, “orange” ] }
{ “_id” : ObjectId(“502251a309248743250688e1”), “name” : “good fruit”, “fruits” : [ “banana”, “pear”, “orange” ] }
{ “_id” : ObjectId(“502251c109248743250688e2”), “name” : “good fruit”, “fruits” : [ “banana”, “apple”, “tomato” ] }db.fruitshop.find({“fruits”:{“$all”:[“apple”,”banana”]}});
{ “_id” : ObjectId(“502251c109248743250688e2”), “name” : “good fruit”, “fruits” : [ “banana”, “apple”, “tomato” ] }1
    我们看,使用“$all”对数组内元素的顺序没有要求,只要全部包含的数组都能查询出来。数组查询也可以使用精确匹配的方式,即查询条件文档中键值对的值也是数组,如:
{ “_id” : ObjectId(“5022518d09248743250688e0”), “name” : “big fruit”, “fruits” : [ “apple”, “pear”, “orange” ] }
{ “_id” : ObjectId(“5022535109248743250688e4”), “name” : “fruit king”, “fruits” : [ “apple”, “orange”, “pear” ] }
{ “_id” : ObjectId(“502253c109248743250688e5”), “name” : “good fruit”, “fruits” : [ “apple”, “orange”, “pear”, “banana” ] }db.fruitshop.find({“fruits”:[“apple”,”orange”,”pear”]});
{ “_id” : ObjectId(“5022535109248743250688e4”), “name” : “fruit king”, “fruits” : [ “apple”, “orange”, “pear” ] }1
2
3    如果是精确匹配的方式,MongoDB的处理方式是完全相同的匹配,即顺序与数量都要一致,上述中第一条文档和查询条件的顺序不一致,第三条文档比查询条件文档多一个元素,都没有被匹配成功!
    对于数组的匹配,还有一种形式是精确指定数组中某个位置的元素匹配,我们前面提到,数组中的索引可以作为键使用,如我们要匹配水果店售第二种水果是orange 的水果店:db.fruitshop.find();
{ “_id” : ObjectId(“5022518d09248743250688e0”), “name” : “big fruit”, “fruits” : [ “apple”, “pear”, “orange” ] }
{ “_id” : ObjectId(“5022535109248743250688e4”), “name” : “fruit king”, “fruits” : [ “apple”, “orange”, “pear” ] }
{ “_id” : ObjectId(“502253c109248743250688e5”), “name” : “good fruit”, “fruits” : [ “apple”, “orange”, “pear”, “banana” ] }db.fruitshop.find({“fruits.1”:”orange”});
{ “_id” : ObjectId(“5022535109248743250688e4”), “name” : “fruit king”, “fruits” : [ “apple”, “orange”, “pear” ] }
{ “_id” : ObjectId(“502253c109248743250688e5”), “name” : “good fruit”, “fruits” : [ “apple”, “orange”, “pear”, “banana” ] }1
2
3    数组索引从0开始,我们匹配第二种水果就用furits.1作为键。
    $size条件操作符,可以用来查询特定长度的数组的,如我们要查询卖3种水果的水果店:db.fruitshop.find();
{ “_id” : ObjectId(“5022518d09248743250688e0”), “name” : “big fruit”, “fruits” : [ “apple”, “pear”, “orange” ] }
{ “_id” : ObjectId(“5022535109248743250688e4”), “name” : “fruit king”, “fruits” : [ “apple”, “orange”, “pear” ] }
{ “_id” : ObjectId(“502253c109248743250688e5”), “name” : “good fruit”, “fruits” : [ “apple”, “orange”, “pear”, “banana” ] }db.fruitshop.find({“fruits”:{“$size”:3}});
{ “_id” : ObjectId(“5022518d09248743250688e0”), “name” : “big fruit”, “fruits” : [ “apple”, “pear”, “orange” ] }
{ “_id” : ObjectId(“5022535109248743250688e4”), “name” : “fruit king”, “fruits” : [ “apple”, “orange”, “pear” ] }
1 |     但条件操作符"$size"不能和其他操作符连用如“$gt”等,这是这个操作符的一个缺陷。使用这个操作符我们只能精确查询某个长度的数组。如果实际中,在查询某个数组时,需要按其长度范围进行查询,这里推荐的做法是:在这个文档中额外增加一个“size”键,专门记录其中数组的大小,在对数组进行"$push"操作同时,将这个“size”键值加1。如下所示: |
db.fruitshop.find({“name”:”big fruit”});
{ “_id” : ObjectId(“5022518d09248743250688e0”), “fruits” : [ “apple”, “pear”, “orange”, “strawberry” ], “name” : “big fruit”, “size” : 4 }
db.fruitshop.update({“name”:”big fruit”},
… {“$push”:{“fruits”:”banana”}, “$inc”:{“size”:1}}, false, false);
db.fruitshop.find({“name”:”big fruit”});
{ “_id” : ObjectId(“5022518d09248743250688e0”), “fruits” : [ “apple”, “pear”, “orange”, “strawberry”, “banana” ], “name” : “big fruit”, “size” : 5 }
1 |     find函数的第二个参数用于查询返回哪些键,他还可以控制查询返回数组的一个子数组,如下例:我只想查询水果店售卖说过数组的前两个: |
db.fruitshop.find();
{ “_id” : ObjectId(“5022518d09248743250688e0”), “fruits” : [ “apple”, “pear”, “orange”, “strawberry”, “banana” ], “name” : “big fruit” }
{ “_id” : ObjectId(“5022535109248743250688e4”), “fruits” : [ “apple”, “orange”, “pear” ], “name” : “fruit king” }
{ “_id” : ObjectId(“502253c109248743250688e5”), “fruits” : [ “apple”, “orange”, “pear”, “banana” ], “name” : “good fruit” }
db.fruitshop.find({}, {“fruits”:{“$slice”:2}});
{ “_id” : ObjectId(“5022518d09248743250688e0”), “fruits” : [ “apple”, “pear” ], “name” : “big fruit” }
{ “_id” : ObjectId(“5022535109248743250688e4”), “fruits” : [ “apple”, “orange” ], “name” : “fruit king” }
{ “_id” : ObjectId(“502253c109248743250688e5”), “fruits” : [ “apple”, “orange” ], “name” : “good fruit” }
1 |     “$slice”也可以从后面截取,用复数即可,如-1表明截取最后一个;还可以截取中间部分,如[2,3],即跳过前两个,截取3个,如果剩余不足3个,就全部返回! |
db.fruitshop.find();
{ “_id” : ObjectId(“5022518d09248743250688e0”), “fruits” : [ “apple”, “pear”, “orange”, “strawberry”, “banana” ], “name” : “big fruit” }
{ “_id” : ObjectId(“5022535109248743250688e4”), “fruits” : [ “apple”, “orange”, “pear” ], “name” : “fruit king” }
{ “_id” : ObjectId(“502253c109248743250688e5”), “fruits” : [ “apple”, “orange”, “pear”, “banana” ], “name” : “good fruit” }
db.fruitshop.find({}, {“fruits”:{“$slice”:-1}});
{ “_id” : ObjectId(“5022518d09248743250688e0”), “fruits” : [ “banana” ], “name” : “big fruit” }
{ “_id” : ObjectId(“5022535109248743250688e4”), “fruits” : [ “pear” ], “name” : “fruit king” }
{ “_id” : ObjectId(“502253c109248743250688e5”), “fruits” : [ “banana” ], “name” : “good fruit” }
db.fruitshop.find({}, {“fruits”:{“$slice”:[3,6]}});
{ “_id” : ObjectId(“5022518d09248743250688e0”), “fruits” : [ “strawberry”, “banana” ], “name” : “big fruit” }
{ “_id” : ObjectId(“5022535109248743250688e4”), “fruits” : [ ], “name” : “fruit king” }
{ “_id” : ObjectId(“502253c109248743250688e5”), “fruits” : [ “banana” ], “name” : “good fruit” }
1 |     如果第二个参数中有个键使用了条件操作符"$slice",则默认查询会返回所有的键,如果此时你要忽略哪些键,可以手动指明!如: |
db.fruitshop.find({}, {“fruits”:{“$slice”:[3,6]}, “name”:0, “_id”:0});
{ “fruits” : [ “strawberry”, “banana” ] }
{ “fruits” : [ ] }
{ “fruits” : [ “banana” ] }
1 |
|
db.staff.find();
{ “_id” : ObjectId(“50225fc909248743250688e6”), “name” : { “first” : “joe”, “middle” : “bush”, “last” : “Schmoe” }, “age” : 45 }
{ “_id” : ObjectId(“50225fe209248743250688e7”), “name” : { “first” : “joe”, “middle” : “bush” }, “age” : 35 }
{ “_id” : ObjectId(“50225fff09248743250688e8”), “name” : { “middle” : “bush”, “first” : “joe” }, “age” : 25 }
db.staff.find({“name”:{“first”:”joe”,”middle”:”bush”}});
{ “_id” : ObjectId(“50225fe209248743250688e7”), “name” : { “first” : “joe”, “middle” : “bush” }, “age” : 35 }
1 |     针对内嵌文档特定键值对的查询是最常用的!通过点表示法来精确表示内嵌文档的键: |
db.staff.find();
{ “_id” : ObjectId(“50225fc909248743250688e6”), “name” : { “first” : “joe”, “middle” : “bush”, “last” : “Schmoe” }, “age” : 45 }
{ “_id” : ObjectId(“50225fe209248743250688e7”), “name” : { “first” : “joe”, “middle” : “bush” }, “age” : 35 }
{ “_id” : ObjectId(“50225fff09248743250688e8”), “name” : { “middle” : “bush”, “first” : “joe” }, “age” : 25 }
db.staff.find({“name.first”:”joe”, “name.middle”:”bush”});
{ “_id” : ObjectId(“50225fc909248743250688e6”), “name” : { “first” : “joe”, “middle” : “bush”, “last” : “Schmoe” }, “age” : 45 }
{ “_id” : ObjectId(“50225fe209248743250688e7”), “name” : { “first” : “joe”, “middle” : “bush” }, “age” : 35 }
{ “_id” : ObjectId(“50225fff09248743250688e8”), “name” : { “middle” : “bush”, “first” : “joe” }, “age” : 25 }
1 |     我们看,这样查询,所有有效文档均被查询到了!通过点表示法,可以表示深入到内嵌文档内部的键!利用“点表示法”来查询内嵌文档,这也约束了在插入文档时,任何键都不能包含“.” !! |
db.blogs.findOne();
{
“_id” : ObjectId(“502262ab09248743250688ea”),
“content” : “…..”,
“comment” : [
{
“author” : “joe”,
“score” : 3,
“comment” : “just so so!”
},
{
“author” : “jimmy”,
“score” : 5,
“comment” : “cool! good!”
}
]
}
db.blogs.find({“comment.author”:”joe”, “comment.score”:{“$gte”:5}});
{ “_id” : ObjectId(“502262ab09248743250688ea”), “content” : “…..”, “comment” : [ { “author” : “joe”, “score” : 3, “comment” : “j
ust so so!” }, { “author” : “jimmy”, “score” : 5, “comment” : “cool! good!” } ] }
1 |     我们想要查询评论中有叫“joe”并且其给出的分数超过5分的blog文档,但我们利用“点表示法”直接写是有问题的,因为这条文档有两条评论,一条的作者名字叫“joe”但分数只有3,一条作者名字叫“jimmy”,分数却给了5!也就是这条查询条件和数组中不同的文档进行了匹配!这不是我们想要的,我们这里是要使用一组条件而不是单个指明每个键,使用条件操作符“$elemMatch”即可!他能将一组条件限定到数组中单条文档的匹配上: |
db.blogs.findOne();
{
“_id” : ObjectId(“502262ab09248743250688ea”),
“content” : “…..”,
“comment” : [
{
“author” : “joe”,
“score” : 3,
“comment” : “just so so!”
},
{
“author” : “jimmy”,
“score” : 5,
“comment” : “cool! good!”
}
]
}
db.blogs.find({“comment”:{“$elemMatch”:{“author”:”joe”, “score”:{“$gte”:5}}}});
db.blogs.find({“comment”:{“$elemMatch”:{“author”:”joe”, “score”:{“$gte”:3}}}});
{ “_id” : ObjectId(“502262ab09248743250688ea”), “content” : “…..”, “comment” : [ { “author” : “joe”, “score” : 3, “comment” : “j
ust so so!” }, { “author” : “jimmy”, “score” : 5, “comment” : “cool! good!” } ] }
    这样做,结果是正确的!利用条件操作符“$elemMatch”可以组合一组条件,并且还能达到的“点表示法”的模糊查询的效果!
欢迎关注个人公众号:
