MongoDB学习笔记

MongoDB笔记

一   mongodb主要组成部分:

什么是文档?文档是mongodb中的最基本数据单元,类似于关系数据库中的行。

  在mongodb中,文档的键通常都是字符串。

     Mongodb对文档的键是区分大小写的,对键值是区分数据类型的。

     *同一文档中键值是不能重复的

什么是集合:集合是文档组成的,在关系型数据库中就是表。

  在mongodb中,对集合中存储的文档是非常开放的,一个集合中可以存放各式各样的文档。

  组织子集合的一种惯例是使用“.”字符分开的按命名空间划分的子集合。在MongoDB中使用子集合来组织数据是很好的方法。

 例如:用户与用户详细信息包含两个集合,分别是user.user和user。userinfo。这样做的的目的只是为了使组织结构更好些,也就是说user这个集合(这里根本就不需要存在)及其子集合没有任何关系。把数据库的名字放到集合名前面,得到的就是集合的完全限定名,成为命名空间。

数据库:MongoDB中多个集合可以组成数据库。

  (1)admin  从权限的角度来看,这是“root”数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或关闭服务器。

  (2)local 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合

  (3)config 当mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

命名规则:不能是“”空字符串;不可以含有空格,在mongodb中空格代表结束字符;

  文档的键不可以有“.”和“$”符号,集合名中不可以包含“$”;

  集合不能以“system.”开头,mongodb中system是系统保留前缀;

  集合名长度不得超于121字节,在实际使用当中应该小于100字节,数据库名最多不超过64个字节(mongodb最终是以文件方式存储的,操作系统中的非法文件名,在mongodb中就是非法的,这样便于理解与记忆)

数据类型:mongodb的文件存储格式为BSON,同JSON一样,支持往其他文档对象和数组中再插入文 档对象和数组,同时扩展了JSON的数据类型与数据库打交道的那些应用。例如:JSON没有日期类型,这会使得处理本来间的日期问题变得非常繁琐,也没有办法表示其他常用类型,如正则表达式或函数。

mongodb的支持的数据类型:

 null:null用于表示空值或者不存在的字段。{"x":null}

 布尔:布尔类型有两个值“true”和“false”{"x":true}

 浮点数:都是这种类型。例如:{"pi":3.1415926} {"pi":3}

 符号:符号类型转换成了字符串

 对象id:对象id是文档12字节的唯一ID {"x":Objectid()}

 日期:日期类型存储的是从标准纪元开始的毫秒数。{“x”:new Date()}

 正则表达式:文档中可以包含正则表达式,采用javascript的正则表达式语法:{“x”:/test/i}

 代码:文档中还可以包含javascript代码。{“x”:function(){/*.....*/}}

 二进制数据:二进制数据可以由任意自己的串组成。

 未定义:文档中也可以使用未定义类型:{“x”:undefined}

 数组:值的集合或列表可以表示数组:{“x”:["a","b","c"]}

 内嵌文档:文档可以包含别的文档,也可以作为值嵌入到付文档中,数据可以组织的更自然些{"x":{"sex":"woman"}}

二、mongodb shell:是一个功能完备的javas的解释器,能够运行任意的javas。同时他是一个独立的mongodb客户端,可以执行管理操作,检查运行实例等。

 运行shell:

    windows:mongo.exe

    linux:./mongo

 shell默认连接test数据库,使用其他数据库实例,需要使用use[数据库名]命令。

使用shell窍门:由于mongo是一个javascript shell,可以查看JavaScript文档获取很多帮助,同时shell本身也内置了帮助文档,可以通过help命令查看。

数据库级帮助:db.help()

集合级帮助:db.[集合名].help()

简单查询:mongodb中查询包含find()和findOne();

find():返回值是文档集合;

findOne():返回值是一个为先匹配到的文档对象;

     find和findOne函数的第一个参数决定了需要返回的文档,其形式也是一个文档,实际上可以称之为查询条件。

    空的查询文档{},是匹配所有集合下的文档,不指定查询文档的情况下,mongodb默认值也是{}。比如:db.mydb.user.find()等价于db.mydb.user.find({})

findOne()也是一样。

当我们往文档里添加键/值对时,,意味着我们易筋经限定了查询条件,使用类型匹配;

例如:

查询mydb.user集合中name值为”admin“的文档集合db.mydb.user.find({name:"admin"})

查询mydb.user集合中password值为123的文档集合db.mydb.user.find({password:123})

区别于:db.mydb.user.find({password:"123"})

有时并不需要文档所有的键/值对都返回,find和findOne函数的第二个参数可以设定想要返回的键,这样做不仅可以节省传输数据量,而且可以节省客户端文档解析时间与内存消耗。 

    例如:db.mydb.user,find({},{name:1})

               db.mydb.user.findOne({},{name:1})

创建、删除文档:就是mongodb数据的插入于删除操作。

  插入并保存文档:db.mydb.user.insert({name:"jack",password:222}),在不指定”_id“值时,mongodb会自动帮我们生成”_id“键,值为Object(....),然后保存到mydb.user集合中。db.mydb.user.insert({_id:"s001",name:"jack",password:223}).mongodb则不会帮我们创建_id键。

 删除制定文档:db.mydb.user.remove([需要指定条件]),例如:db.mydb.user.remove({name:"jack"})

  删除整个文档集合:db.mydb.user.drop()

Mongodb插入文档(insert):插入是向MongoDB中添加数据的最基本的方法。向目标集合使用insert方法。

db.mydb.user.insert({name:"test1"})这个操作时向mydb.user集合中增加”_id“键,并添加到MongoDB中。

原理与作用:当执行插入时,使用驱动程序会将数据转换成BSON格式。数据库会解析BSON,并检验是否含有”_id“键和文档不超过16M,除之,不做任何数据验证。缺点:插入无效数据;优点:数据库远离注入式攻击。

Mongodb保存文档(save):save也是向MongoDB中添加数据的方法。

db.mydb.user.save({name:"test2"})这个操作也时向mydb.user集合中增加”_id“键,并添加到MongoDB中。

insert与save区别:save函数实际上就是根据参数条件,调用了insert或update函数。如果像插入的数据对象存在,insert函数会报错,而save函数时改变原来的对象,;如果像插入的对象不存在,那么他们执行相同的插入操作,这里可以用几个字来概括他们两个的区别,”有则改之,无则加之“。

例如:db.mydb.user.insert({name:"test1"})等价于db.mydb.user.save({name:"test1"})

db.mydb.user.insert({_id:1,name:"test3"})不等于db.mydb.user.save({_id:1,name:"test4"})

文档替换:更新文档最简单的情形就是一个新文档替换匹配的文档,适用于模式发生发生较大改变的时候。例如:{name:"test1"}修改为{name:"test1",age:20}

执行操作:db.mydb.user.update({name:"test1"},{name:"test1",age:20})

使用修改器:通常文档只需要部分更新,可使用原子的更新修改器,使得文档更新起来更高效。更新修改器是种特殊的键,用来指定复杂的操作,比如增加、删除或者调整键,还可能是操作数组或者内嵌文档。

$inc使用:“$inc”修改器是用来修改已有键的值,或者初始化某个键值(当键不存在的时候),该键值必须为数字类型,并对其进行增减操作。用法:db.集合.update({...},{$inc:{键:数字类型}})例如:db.mydb.user.update({name:"test1"},{$inc:{age:1}})

$set与 $unset:$set一般用于指定键值修改,如果指定键不存在,mongoDB会创建它,这对于文档的更新或增加键非常方便。

用法:db.集合.update({...},{$set:{键:值}})例如:为test1增加一个爱好键值db.mydb.user.update({name:"test1"},{$set:{hobby:"swimming"}})

"$set"修改器也可以内嵌文档。例如:test1的体态特征身高“175cm”   db.mydb.user.update({name:"test1"},{$set:{"posture.height":"175cm"}})操作内嵌文档时,需要注意键名的书写格式:采用“:”连接方式。

“$unset”修改器用于删除键。例如test1经过人生洗礼发现自己没有爱好了

 db.mydb.user.update({name:"test1"},{$unset:{hobby:175cm}})

数组修改器:是一类操作数组的特殊键,该键仅可以数组类型值。

数组修改器$push:"$push"修改器时会往键已有的数组中追加值,如果数组不存在,则创建新的数组并更新键值。

用法:db.集合.update({...},{$push:{键:值}})  例如:db.mydb.user.update({name:"test1"},{$push:{hobby:"drink"}})

数组修改器:

$push + $ne:$push + $ne组合键使用于如果追加值不咋数组中则追加进去。

用法:db.集合.update({键:{$ne:值}},{$push:{键:值}}) 例如:db.mydb.user.update({name:"test1",bobby:{$ne:“drink”}},{$push:{hobby:"drink"}})

$addToSet:$push + $ne可以用$addToSet来实现,因为有些情况下$push + $ne组合时实现不了的。  用法:db.集合.update({...},{$addToSet:{键:值}})  例如:db.mydb.user.update({name:"test1"},{$addToSet:{hobby:"drink"}})

$addToSet+$each:当我们一次需要追加多个值的时候,而且需要判断数组中是否存在,这时$push + $ne组合就不能实现,需要用到$addToSet+$each组合键。

用法:db.集合.update({...},{$addToSet:{键:{$each:[....]}}})

例如:db.mydb.user.update({name:"test1"},{$addToSet:{hobby:{$each:["drink","sing"]}}})

$pop:"$pop"修改器用于删除数组里面值,如果吧数组看成一个队列或者栈的话,可以从数组任何一端删除元素。

用法:{$pop:{键:1}}   从数组尾部删除一个元素

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

例如:db.mydb.user.update({name:"test1"},{$pop:{hobby:1}})

$pull:"$pull"修改器也是删除数组里面的值,他可以删除数组中任意位置的值,并且删除所有匹配到的值。

用法:{$pull:{键:值}}

例如:db.mydb.user.update({name:"test1"},{$pull:{hobby:“drink”}})

数组修改器 定位修改器$:“$”定位修改器也是用于数组里面的数据类型为内嵌文档,可以使用$实行定位修改

例如:posture:[{height:"175cm",age:20},{height:"180cm",age:26}]

(1)数组都是0开头的,可以通过下标直接选择要修改的元素:db.mydb.user.update({name:"test1"},{$inc:{“posture.0.age”:1}})

(2)使用定位修改器修改:db.mydb.user.update({"posture.age":21},{"$set":{“poster.$.height”:"178cm"}})

upsert可以理解为update与insert的缩写,他是update函数的第三个参数,其实际含义就是更新插入。

update参数upsert默认值是false,他是一种特殊的更新,根据这个参数值,update函数判断对匹配不到的文档,将更新文档基于匹配文档之上以一个新的文档是否增加到集合中。

用法:db.集合.update({匹配文档},{更新文档},true)

例如:db.mydb.user.update({age:24},{$inc:{age:1}},true)

mutli:他是update函数的第四个参数,其实际含义就是是否实现更多操作。update函数的mutli默认值是false,代表只对第一个匹配到的文档进行更新操作;需要更新多个符号匹配条件的文档,则需要设置mutli值为true。

用法:db.集合.update({...},{....},false,true)

例如:所有名称为test3的年龄加2

db.mydb.user.update({name:"test3"},{$inc:{age:2}},false,true)

想要知道多少文档进行了更新,在执行完update操作之后,可以运行getLastError命令,返回值中键“n”的值就是代表更新数。

例如:db.mydb.user.update({name:"test3"},{$inc:{age:2}},false,true)

          db.runCommand({getLastError:1})

返回已更新的文档findAndModify:findAndModify跟我们平时update不同,而且还有点慢,因为它需要等待数据库的回应。但对于其他原子性的查询,取值或赋值操作要方便的多。

用法:db.集合名.findAndModify({

                  query:{匹配文档},

                  update:{更新文档}

                 [,remove:布尔值,sort:{排序文档},new:布尔值]

                               })

query:匹配文档,也就是查询条件。

sort:排序匹配文档的条件

update:修改器文档,执行文档更新操作。

remove:true/false。是否对匹配到的文档进行删除。

new:true/false..表示返回的是更新前还是更新后的文档,默认值是false。

注意:update与remove必须而且只能存在一个。他一次只能操作一个文档,不能进行upsert操作,智能更新已存在的文档。findAndModify执行效率有点低,不代表避免使用,mongno每个函数的存在,都有其实际存在的价值。它的执行效率相当于一次查询、更新外加getLastError顺序执行的时间。

瞬间完成:增、删、改操作都是瞬间完成的,因为他们不需要数据库的响应,可以把他想象成客户端发出请求后就不用操心操作。我们一般称之为“离玄之箭”方式。

优点:执行效率高,它只会受客户端网络速度制约,一般情况下会工作的很好,但有时也会出意外。比如:服务器崩溃,网线被老鼠咬断,数据中心短路。。。。,对于有些应用如用户点击量或分析型数据采样,丢失几秒的数据无关紧要,但对于购物付费类的,就不好玩了

安全操作:是在执行玩操作后立即执行getLastError命令,来检查是否成功执行。然后适当的处理数据库返回的错误,一般情况下数据库会抛出一个可捕获得错误,我们可以采用自己的开发语言来捕获和处理。如果执行成功,getLastError会给出额外的信息作为响应(比如:更新或删除操作给出的更新数)

捕获“常规”错误:安全操作也是一种调试数据库“奇怪”行为的好方法,建议开发过程大量使用,这样可以避免常规的使用数据库错误。例如:“_id”值重复,mongoDB中不允许在一个集合中出现重复“_id”值的文档。

{_id:123,name:"test1"}和{_id:123,name:"test2"}

请求与链接:数据库会为每一个mongoDB链接创建一个独立的队列,来存放连接的请求,当客户端发送请求,会被存放在该连接队列的末尾,当队列的请求都执行完毕以后,才会执行后续的请求。对于实际应用的交错插入,查询,会产生秒级的延迟。

查询 find:查询就是返回一个集合中文档的子集,子集合的范围从0个文档到整个集合。

例如:db.mydb.user.find({name:"test1"})

指定返回键:有时并不需要返回文档中的所有键和值,遇到这样的情况,使用find的第二个参数来制定返回键,这样做既可以节省传输的数据量,也可以节省客户端解码文档的时间和内存开销。

例如:db.mydb.user.find({},{age:1})

限制:查询还是有些限制的,数据库所关心的查询文档的值必须是常量,也就是不可以使用文档的其他键值。

查询条件:< :$lt(小于)   例如:db.mydb.user.find({age:{$lt:35}})

                 <= :$lte(小于等于)  例如:db.mydb.user.find({age:{$lt3:35}})

                 > :$gt(大于)   例如:db.mydb.user.find({age:{$gt:31}})

                 >= :$gte(大于等于)   例如:db.mydb.user.find({age:{$gte:31}})

                 ≠ :$ne(不等于)   例如:db.mydb.user.find({age:{$ne:"test3"}})

                模糊查询:/^.../   例如:db.mydb.user.find({name:{$lt:/^test/})

or查询:or查询在mongodb中有两种方式:$in($nin)与$or

           $in:是用来查询一个键的多个值。

                例如:(1)db.mydb.user.find({name:{$in:["test1","test2"]}})

                          (2)db.mydb.user.find({_id:{$in:["123",1]}})

           与之相对的则是$nin:用来查询条件不匹配的所有文档。

           $or:比$in更通用一些,是用来查询多个键的任意给定键值,其值是一个数组形式。

                例如:db.mydb.user.find({$or:[{name:"test1"},{age:25}]})

           $or也可以与$in或$nin结合起来使用 db.mydb.user.find({$or:[{name:{$in:["test1","test2"]}},{age:25}]})

           $not:是用来查询与特定模式相符的文档。

                  例如:db.mydb.user.find({age:{$not:{$mod:[5,0]}}})

           $not与正则表达式是联合使用的时候极为有用。

条件句规则:在查询中,类似$lt的键外在内层文档,如修改器$inc则处在外层文档。如:{age:{$lt:30}}与{$inc:{age:1}}而且一个键可以含有多个条件,但不可以含有多个修改器:正确:{age:{$lt:30,$gt:20}}  错误 {$inc:{age:1},$set:{age:40}}

键值为数组的查询:在实际应用中,对于一个键值为数组的查询时非常常见的,如下:db.mydb.insert({shop:"A",fruit:["apple","banana","orange"],size:3}) 采用查询:db.mydb.find({fruit:"apple"})

$all :对于数组查询中,多个数组的匹配时,需要用到关键键$all。如下:

       db.mydb.insert({shop:"B",fruit:["apple","peach","orange"],size:3})

       db.mydb.insert({shop:"C",fruit:["mango","banana","orange"],size:3})

查询既有“apple”又有“banana”的文档:db.mydb.find({fruit:{$all:["apple","banana"]}})

注意:$all查询不考虑键值的先后顺序问题。

精确匹配:使用[]精确匹配数组元素,既要考虑键值的个数,也要考虑元素的位置。

               db.mydb.find({fruit:["apple","banana","orange"]})

下标查询:Key.index   key:代表键   index:数组下标   

数组键值下标是从0开始。如下:db.mydb.find({“fruit.1”:"banana"})查询数组中第二的元素为“banana”的文档。

$size:在数组查询中,该键就是查询指定长度的数组。如:db.mydb.find({fruit:{$size:3}}),但他不可以与其他查询子句组合使用(“$lt”,"$gt");如:db.mydb.find({fruit:{$size:{$gt:3}}})是非法的。但是这种查询可以通过在文档中增加一个size键来实现的。如:db.mydb.update({shop:"A"},{"$push":{fruit:"watermelon"},$inc:{size:1}})

db.mydb.find({size:{"$lt":4}})

$slice是用于自己查询,其返回值是一个子集合,如:db.mydb.find({shop:"A"},{fruit:{$slice:2}}),查询表示查询数组的前2个子集。

也可以返回后2个子集:db.mydb.find({shop:"A"},{fruit:{$slice:-2}}),它也可以接受偏移量[m,n]:db.mydb.find({shop:"A"},{fruit:{$slice:[1,2]}})表示跳过第一个查询第2个子集

键值为文档的查询:对于内嵌文档的查询有两种查询方式,一种是全文档匹配,另一种是键/值匹配,如:{study:"java",person:{name:"zhangsan",age:25}}

采用全文档匹配查询:db.mydb.km.find({person:{name:"zhangsan",age:25}})

另一种查询方式:使用.连接符的键值匹配。如下:db.mydb.mk.find({"person.name":"zhangsan","person.age":25})这种点的查询方式就是区别于其他普通个文档的主要特点,而且为什么对于键名不能使用.这个符号做出最好的诠释。

注意:键值匹配查询,对于内嵌文档值多个或者顺序未知的情况下,是有效的。

$elemMatch:该键是用来匹配数组中单个内嵌文档使用的。db.mydb.km.find({person:{$elemMatch:{name:"zhangsan",age:27}}})

$where查询:当键、值查询不能满足需求的时候,我们需要用到$where查询子句。是借助javascript来完成的

查询apple和芭娜娜值相等的文件:$where表达式来执行:

 db.mydb.fruit.find({"$where":function(){

     if(this.apple==this.banana) return true;

     return false;       

}})等价于db.mydb.fruit.find({"$where":"this.apple==this.banana"})

注意:$where查询其返回值是表达式返回true的当前文档

$where的优缺点:

优点:由于它可以使用JavaScript表达式,查询灵活多样。

缺点:使用$where查询,查询速度慢,因为其查询原理是将被查询文档转化为JavaScript对象,然后通过$where表达式去比较,而且还不能使用索引

limit:是限制返回结果集的数量,在find函数后使用该函数。

limit(n):返回匹配n个文档。这里的n是上限,不是下限。

例如:db.mydb.fruit.find().limit(3)//返回匹配到的3个文档,如果结果少于3个,则全部返回。

skip(n):略过匹配到的前n个文档,返回剩下的文档。

例如:db.mydb.fruit.find().skip(3)//略过匹配到的前3个文档,返回余下的所有文档。如果结果少于3个,则返回空。

sort:是一个排序函数,他的参数一个文档对象(键、值),键是集合中的键,值是1或-1;1代表升序,-1代表降序。

例如:db.mydb.fruit.find().sort({apple:1,banana:-1})由于文档型数据库的数据类型是不规则的,但mongo预定义了数据类型的排序规则。如:{a:[1,2]}、{a:true}

排序规则:从小到大

1.最小值     2.null      3.数字    4.字符串    5.文档     6.数组    7.二进制    8.对象ID   9.布尔型    10.日期   11.时间戳   12.正则表达式   13.最大值

随机抽取文档:在实际应用中,随机抽取一个或多个文档是常见的需求,最简单的实现方法:先计算出一个从0到记录总数之间的随机数,然后采用skip(随机数)方法

var total=db.mydb.fruit.count()

var random=Math.floor(Math.random()*total);

db.mydb.fruit.find().skip(random).limit(1)

当数据量很大时,skip操作会变得很慢,可以采用另外一种查询方式:为每一条记录增设random字段,并赋值为Math.random(),查询时采用$gte和$lte.

db.mydb.fruit.update({banana:1},{$set:{random:Math.random}})

var rand=Math.random();

db.mydb.fruit.find({random:{$gte:rand}});

借助mongodb对地理空间索引的支持,从而可以在上一个方法的基础上来实现随机记录的获取。

创建地理空间索引:

db.mydb.fruit.ensureindex({random:"2d"})

db.mydb.fruit.update({banana:1},{random:[Math.random(),0]})

db.mydb.fruit.find({random:{$near:[Math.random(),0]}})

null:在查询中,null有点奇怪,它不仅可以匹配键值为null的文档,而且还可以匹配“不存在”的键。如果仅仅想要匹配键值为null的文档,既要判断该键值是否为null,还要通过“$exists”判断该键是否存在。

例如:db.mydb.test.find({z:{$in:[null],$exists:true}})

正值表达式:正则表达式能更灵活有效的匹配字符串。mongoDB使用perl兼容的正值表达式(PCRE)库来匹配正值表达式,PCRE所支持的正值表达式都能被MongoDB接受,正值表达式也可以匹配自身。

例如:{name:/jack/}   {name:/jack/i}   {name:/jac?/i}   {name:/^ja/}

聚合查询:

count:它的查询方式跟find一样,但返回值不一样。find返回值是文档集,count返回值是文档数。

例如:db.mydb.fruit.count()    查询文档总数

          db.mydb.fruit.count({banana:1})  查询限定文档总数

distinct是键值去重,其返回值是一个数组。

用法:db.mydb.fruit.distinct(“键”[,查询条件])

如:db.mydb.fruit.distinct("apple")

       db.mydb.fruit.distinct("apple".{banana:1})

group:在mongodb里面做group操作有点小复杂,利用group可以将集合中的记录按照一个或多个键分组,然后可以聚合每个分组内的文档产生新的结果。

用法:db.mydb.fruit.group({"key":{},"$reduce":function(doc,prev){},"initial":{}})

key分组的键

initial:累加器初始值,分组之后,每一组的reduce函数会调用这个初始文档,并在组内传递累加器;

$reduce:分组处理函数,分组之后会对每组元素遍历处理,该函数两个参数,第一个参数是当前的文档对象和第二个参数是上一次function操作的累计对象。

group返回值:结果返回值为文档数组:[{分组建,累加器}]

例如:db.mydb.fruit.db.group({"key":{apple:1},"$reduce":function(doc,prev){pre.bananaArr.push(doc.banana)},"initial":{bananaArr:[]}})

 

 

 

 

 

 

 

 

 

 

  

 

posted @ 2018-07-08 16:49  我要当吃霸  阅读(367)  评论(0编辑  收藏  举报