MongoDB基础学习

来源:互联网 发布:flexpaper flash.js 编辑:程序博客网 时间:2024/05/24 06:36

一. 基本概念

  1. 文档
    文档是有序集合里面的一个键值对的有序集合。文档的键是字符串(除了少数情况,键可以用任何的UTF-8)。相当于关系型数据库当中的表table。
  2. 集合
    集合就是一组文档,相当于关系型数据库当中的一行数据。

二. 数据类型

  1. null
  2. 布尔型true or false
  3. 数值型
        默认为64位的浮点型数值,如果要使用整形值,可以使用NumberInt类(表示4字节带符号整数)或NumberLong类(表示8字符带符号整数),如
         {"x":NumberInt("3")}
        {"x":NumberLong("3")}
  4. 字符串
  5. 日期
        如{“x”:new Date()}
  6. 正则表达式,语法于JavaScript的正则表达式语法相同。
  7. 数组
         数组列表或数据集可以表示为数组:
        {"x":["a","b","c"]}
  8. 内嵌文档
        文档可以嵌套其他文档,被嵌套的文档作为父文档的值。如:
        {"x":{"y":NumberInt("3")}}
  9. 对象id
        对象id是一个12字节的ID,是文档的唯一标识。
         {"x",ObjectId()}
  10. 二进制数据
  11. 代码
         查询和文档可以包含任意的JavaScript代码:
         {"x":function(){/* ... */}}

三. 创建、更新和删除文档

1. 操作

  • 插入文档
       插入一个文档
    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有两个参数,一个是查询文档,用于定位需要更新的目标文档;另一个是修改器文档,用于说明要对找到的文档进行那些修改。
        文档的修改可以分为两种类型,一种是文档的替换,就是将文档内容整个地替换掉。另外一种是文档的部分字段的更新。在进行文档的部分字段的更新时,必须使用修改器,否则文档会被全部替换。

db.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。

2.常用的修改器

  • $set 用于设置文档中某一个键对应的值,如果对应的键不存在,则会新增加一个键值对。

  • $inc 用于增加已有键的值,如果对应的键不存在那就创建一个。$set只能用于整形,长整形或者双精度浮点型的值。

  • $push 用于操作数组。如果数组已经存在,$push会向已有的数组末尾加入一个元素,要是没有则会创建一个数组。

db.stocker.update({"_id":"GOOD"},{"$push":{"comment":"123"}})

    如果_id为GOOD的文档中不存在comment键,则新创建一个,如果存在的话,则将123添加到comment键的值当中,成为一个数组。
    也可以和$each一起使用:

db.stocker.update({"_id":"GOOD"},{"$push":{"comment":{"$each":["123","456","789"]}}})

    这样,三个字符串就都被添加到comment键对应的数组当中了。

  • $ne $andToSet 当我们将数组座位数据集使用的时候,需要保证数组内的元素不会重复,可以使用$ne $andToSet。

  • $pull 用于删除数组当中的元素。

四. 查询

1. find函数

    MongoDB数据查询使用find函数,其功能与SQL中的select函数相同,可提供与关系型数据库类似的许多功能,包含映射、排序等。

    find函数语法

db.COLLECTION_NAME.find(query,fields,limit,skip)

    find() 方法以非结构化的方式来显示所有文档。
    如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:

db.COLLECTION_NAME.find().pretty()

    pretty() 方法以格式化的方式来显示所有文档。
参数说明

  • 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语法:
     db.account.find().limit(5).skip(10)

注意事项:

  • 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}

当我们使用查询语句:
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" ] }   

    我们发现只要包含苹果的数组都能被查询出来。如果要通过多个元素来匹配数组,就需要条件操作符”$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" ] }  

    我们看,使用“$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" ] }   

    如果是精确匹配的方式,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" ] }    

    数组索引从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" ] }  

    但条件操作符”$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 }  

    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" }  

    “$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" }  

    如果第二个参数中有个键使用了条件操作符”$slice”,则默认查询会返回所有的键,如果此时你要忽略哪些键,可以手动指明!如:

db.fruitshop.find({}, {"fruits":{"$slice":[3,6]}, "name":0, "_id":0});  { "fruits" : [ "strawberry", "banana" ] }  { "fruits" : [ ] }  { "fruits" : [ "banana" ] }  

3.4 查询内嵌文档

    查询文档有两种方式,一种是完全匹查询,另一种是针对键值对查询!内嵌文档的完全匹配查询和数组的完全匹配查询一样,内嵌文档内键值对的数量,顺序都必须一致才会匹配,如下例:

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 }  

    针对内嵌文档特定键值对的查询是最常用的!通过点表示法来精确表示内嵌文档的键:

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 }  

    我们看,这样查询,所有有效文档均被查询到了!通过点表示法,可以表示深入到内嵌文档内部的键!利用“点表示法”来查询内嵌文档,这也约束了在插入文档时,任何键都不能包含“.” !!
    当内嵌文档变得复杂后,如键的值为内嵌文档的数组,这种内嵌文档的匹配需要一些技巧,如下例:

> 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!" } ] }

    我们想要查询评论中有叫“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”可以组合一组条件,并且还能达到的“点表示法”的模糊查询的效果!

0 0