设为首页 收藏本站
查看: 1100|回复: 0

[经验分享] MongoDB常用操作整理

[复制链接]

尚未签到

发表于 2017-12-14 20:41:46 | 显示全部楼层 |阅读模式
  Mongodb:是一种NoSQL数据库,NoSQL:Not Only SQL
  SQL: 数据表->JDBC读取->POJO(VO、PO)->控制层转化为JSON数据->客户端
  这种转换太麻烦了,如果有直接数据库存放要显示的内容,就能够省略所有需要进行转换的过程。
  所以在实际开发中,往往除了关系型数据库之外还要提供一个NoSql数据库,其中NoSql数据库负责数据的读取,因为直接保存的就是JSON(前提:MongoDB中的数据是排列好的组合数据)。
  例如:现在要求显示出每个雇员的编号、姓名、部门名称、部门位置、工资等级。传统的关系型数据库之中一定要存放大量的冗余数据,不合理。而又nosql数据库,可以直接在业务层里面将数据交给nosql数据库保存。按照指定的结构进行存储。
  MongoDB数据库之中与Oracle数据库有如下的概念对应:
  oracle:表  nosql:集合
  oracle:行    nosql:文档
  oracle:列         nosql:   成员

  oracle: 主键:    nosql:object>  Node.js中一定要使用MongoDB,而node.js(基于JavaScript操作)在国内最成功的应用-taobao。
  Mongodb:面向集合的存储过程,模式自由(无模式)。支持索引、支持短暂数据保留、填充、具备完整的数据库状态监控、基于BSON的应用。
  Mongodb与mysql是一种互补的关系。
  命令:
  不设置端口号启动MongoDB服务:
  mongod  --dbpath D:\MongoDB\db  表示要再这个目录保存所有的数据库文件的操作
  设置端口号启动:
  mongod  --dbpath D:\MongoDB\db --port=2700
  如果通过程序访问数据库的话,一定要设置端口号。
  使用mongo命令连接数据库(重新启动一个cmd)
  mongo
  范例:查询所有的数据库
  show databases
  此时,只存在一个local的本地数据库,不过这个数据库不使用。
  在实际的开发中,在Mongodb中设置一些相应参数:端口号、是否启用用户验证、数据文件的位置等等。
  在D:\MongoDB目录下,建立一个文件"mongodb.conf",
  同时,建立一个保存日志信息的文件" mongodb.log"
  重新启动mongodb的命令:
  mongod -f d:\MongoDB\mongodb.conf
  在Mongo数据库的基础操作:
  1、使用mldn数据库:
  use mldn
  实际上这个时候并不会创建数据库,只有在数据库里面保存集合数据之后才能真正创建数据库。
  2、创建一个集合
  db.createCollection("emp")  创建一个emp集合
  此时,利用show databases;可以看到创建的数据库即mldn数据库才会真正的存在。
  3、但是很多时候如果按照以上代码的形式进行会觉得你不正常,正常是直接保存一个数据。
  db.dept.insert({"deptno":10,"dname":"财务部","loc":"北京"});
  4、查看所有集合的命令:show collections
  可以看出dept的集合自动创建
  5、查看emp标的数据
  db.集合名称.find({若干条件})
  从传统的数据表来看(集合就相当于表的结构),表的结构一旦定义就要按照其定义的内容来编写,但是MongoDB不一样,可以随意扩展数据。
  6、可以随意增加数据
  var deptData = {
  "deptno":20,
  "dname":"研发部",
  "loc":"深圳",
  "count":20,
  "avg":8000.0
  };
  db.dept.insert(deptData)
  此时dept集合的内容可以由用户随意去定义,完全不用考虑其它的结构,那么实际上就必须明确一点,在MongoDB数据库中是绝对不可能存在有查看集合结构的操作。
  7、关于ID的问题
  在MongoDB集合中的每一行记录都会自动的生成一个"_id“:" 数据组成是:时间戳+机器码+PID+计数器",这个ID的信息是MongoDB自己为用户服务的。
  范例:查看单独的一个文档信息
  db.dept.findOne();
  8、范例:删除数据
  db.dept.remove({"_id" : ObjectId("5624fc5bfddcd6e2428e9ed0")})
  10、更新数据
  var timData = {
  "account":"tim",
  "passwd":"123",
  "studentid":525
  };
  db.dept.update({"_id" : ObjectId("5624fe1ffddcd6e2428e9ed1")},timData);
  11、删除集合
  语法:db.集合名称.drop()
  db.dept.drop()
  12、删除数据库(删除当前所在的数据库信息)
  db.dropDatbase()
  数据操作
  CRUD:除了增加之外,其它的都很麻烦。
  1、数据增加
  使用:db.集合.insert() 可以实现数据的增加操作。
  范例:增加一个简单的数据
  db.infos.insert({"url":"www.mldn.cn"});
  切换到mldn数据库:use mldn
  范例:保存数组
  db.infos.insert([
  {"url":"www.mldn.cn"},
  {"url1":"www.mldnjava.cn"}
  ]);
  范例:保存10000个数据
  for(var x = 0;x<10000;x++){
  db.infos.insert({"url":"mldn-"+x});
  }
  如果数据保存很多的情况下,列表不会全部列出,只会列出部分内容。
  2、数据查询
  任何数据库中,数据的查询操作是最为麻烦的,而在MongoDB的数据库中有关系运算、逻辑运算、正则运算等等。
  语法:db.集合名称.find({查询条件}[,{设置显示的字段}])
  范例:最简单的用法是用find()函数完成查询:db.infos.find();
  希望查询出url为:www.mldn.cn
  db.infos.find({"url":"www.mldn.cn"});
  对于设置的显示字段严格来说称为数据的投影操作,如果不需要显示的字段设置为”0“,而需要显示的字段设置为”1“。
  范例:不显示ID
  db.infos.find({"url":"www.mldn.cn"},{"_id":0,"url":1});
  大部分的情况下,这种投影意义的查询意义不大,数据的查询可以使用”pretty()“函数进行漂亮显示。
  范例:漂亮显示:
  db.infos.find({"url":"www.mldn.cn"},{"_id":0,"url":1}).pretty();
  数据列多的时候可以看出华丽的显示。
  关系查询:
  在MongoDB里面支持的关系查询操作:大于($gt)、小于($lt)、大于等于($gte)、小于等于($lte)、不等于($ne)、等于(key:value,$eq)。但是要想让这些操作可以正常使用,那么需要准备出一个数据集合。
  范例:定义一个学生信息集合:
  db.students.drop();
  db.students.insert({"name":"张三","sex":"男","age":19,"score":89,"address":"北京"});
  db.students.insert({"name":"李四","sex":"女","age":21,"score":59,"address":"广州"});
  db.students.insert({"name":"王五","sex":"男","age":20,"score":99,"address":"天津"});
  db.students.insert({"name":"赵六","sex":"女","age":19,"score":100,"address":"上海"});
  db.students.insert({"name":"陈七","sex":"男","age":25,"score":20,"address":"深圳"});
  db.students.insert({"name":"王八","sex":"男","age":19,"score":78,"address":"福州"});
  范例:查询名字为张三的信息:
  db.students.find({"name":"张三"}).pretty();
  范例:查询性别是男的信息:
  db.students.find({"sex":"男"}).pretty();
  范例:查询年龄大于20岁:
  db.students.find({"age":{"$gt":20}}).pretty();
  范例:查询成绩大于等于60分:
  db.students.find({"score":{"$gte":60}}).pretty();
  范例:查询姓名不是王五的信息:
  db.students.find({"name":{"$ne":"王五"}}).pretty();
  此时与之前最大的区别就在于,在一个JSON结构里面需要定义其他的JSON结构,这种风格通过程序进行操作依然如此。
  逻辑运算主要就是三种:与($and)、或($or)、非($not、$nor)。
  范例:查询年龄在19~20岁的学生信息
  db.student.find({"age":{"$gte":19,"$lte":20}}).pretty();
  在进行逻辑运算的时候,"and”的连接是最容易的,因为只需要利用","分割若干个条件就可以了。
  范例:查询年龄不是19岁。
  db.student.find({"age":{"$ne":{"$gte":19}}});
  范例:查询年龄大于19岁、或者成绩大于90分的学生信息:
  db.students.find({"$or":[
  {"age":{"$gt":19}},
  {"score":{"$gt":90}};
  ] }).pretty();
  针对于或的运算,可以实现一个求反的功能。
  在这几个逻辑运算之中,与的连接最简单,而或的连接需要为数据设置数据的过滤条件。
  求模
  模的运算使用:“$mod” 语法:“{$mod:[数字,余数]}”
  db.students.find({"age":{"$mod",[20,0]}}).pretty()
  利用求模运算可以编写一些数学公式。
  范围查询:
  只要是数据库,必须存在有"$in"(在范围之中)、"$nin"(不在范围之中)
  范例:查询姓名是"张三"、”李四"、“王五"的信息。
  db.students.find({"name":{"$in":["张三","李四","王五"]}}).pretty();
  在实际的工作之中,范围的操作很重要。
  数组查询:
  在mongodb是支持数组保存的,就需要对数组的数据进行匹配。
  范例:保存一部分数组内容:
  db.student.insert({"name":大神,"course":["语文","英语","数学"]});
  此时的数据包含有数组的内容,而后需要针对数组数据进行判断,可以使用几个运算符:$alll、$size、$slice、$elemMatch
  范例:查询同时参加语文和数学课程的学生:
  现在两个数组内容都需要保存,所以使用"{"$all",[内容1,内容2,...]}"
  db.students.find({"course":{"$all":["语文","数学"]}}).pretty();
  现在显示所有信息包含语文和数学的内容,而如果差一个内容的不会显示。
  虽然,$all 计算可以用于数组上,但是也可以用于一个数据的匹配上。
  范例:查询学生地址是“海淀区"的信息
  db.students.find({"address":{"$all":[海淀区]}}).pretty();
  既然在集合里面保存的是数组信息,那么数组就可以利用索引操作,使用"key.index”的方式来定义索引。
  范例:查询数组中第二个内容(index=1,索引下标从0开始)为数学的信息。
  db.students.find({"course.1":"数学"}).pretty();
  范例:要求查询出只参加两门课程的学生:(使用$size 来进行数量的控制)
  db.students.find({"course":{"$size":2}})
  在进行数据查询的时候,只要是内容符合条件,数组的内容就全部显示出来,但是现在希望可以控制数组返回的数量,那么可以使用$slice 进行控制。
  范例:返回年龄为19岁,所有学生的信息,但是只显示两门参加课程。
  db.students.find({"age":19},{"$course":{"$slice":2}}).pretty()
  现在只取得前两门的信息,也可以设置取得后两门的信息,把上面的2设置为-2.
  db.students.find({"age":19},{"$course":{"$slice":-2}}).pretty()
  或者只是取得中间部分的信息:
  db.students.find({"age":19},{"$course":{"$slice":[1,3]}}).pretty()
  在此时设置的两个数据里面,第一个数据表示跳过的数据量,而第二个表示返回的数据量。
  嵌套集合运算:
  在MongoDB数据库里面每一个集合数据可以继续保存其它的集合数据,例如:有些学生需要保存家长信息。
  范例:
  此时给出的内容是嵌套的集合,而这种集合的数据的判断只能够通过"$elemMatch"完成
  范例:查询出父母有人是局长的信息。
  db.students.find("$and":[{"age":{"$gte":19}},{"parents":{"$elemMatch":{"job":"局长"}}}]).pretty();
  由于这种查询的条件比较麻烦,所以尽可能不要搞这么复杂的数据结构组成。
  判断某个字段是否存在:
  使用“$exists”可以判断某个字段是否存在,如果设置为true表示存在,如果设置为false表示不存在.
  范例:查询具有parents成员的数据
  db.students.find({"parents":{"$exists":true}}).pretty();
  范例:查询不具有course成员的数据:
  db.students.find({"course":{"$exists":false}}).pretty();
  可以利用此类查询来进行一些不需要的数据过滤,但是建议数据组成一致。
  条件过滤
  实际上习惯于传统关系型数据库开发的我们对于数据的筛选,可能首先想到的一定是where子句,所以在mogo里面也提供有"$where"。
  范例:使用where进行数据查询
  db.students.find({"$where":"this.age>20"}).pretty();
  可以直接用:db.students.find("this.age>20").pretty();
  对于“$where”是可以简化的,但是这类的操作是属于进行每一行的信息判断,实际上对于数据量较大的情况是不方便使用。实际上,以上的代码严格来讲是属于编写一个操作的函数。
  db.students.find(function(){ return this.age>20; }).pretty();
  以上只是查询了一个判断,如果要想实现多个条件的判断,那么就需要使用and连接。
  db.students.find({"$and":[
  {"$where":"this.age>19"}.
  {"$where":"this.age<19"}
  ]}).pretty();
  虽然这种形式的操作可以实现数据查询,但是最大的缺点是将MongDB里面保存的BSON数据变为JavaSript的语法结构,这样的方式不方便使用数据库的索引机制。
  正则运算
  如果要想实现模糊查询,那么必须使用正则表达式,而且正则表达式使用的语言Perl兼容的正则表达式的形式。如果要想实现正则使用,则按照如下的定义格式:
  基础语法:{key:正则标记}
  完整语法:{key:{"$regex":正则标记,"$options":选项}}
  其中,对于options主要是设置正则的信息查询的标记。
  "i":忽略字幕大小写
  "m":多行查找
  "x":空白字符串除了被转义的或在字符中意外的完全被忽略:
  "s":匹配所有的字符(圆点、“.”),包括换行内容
  需要注意的是,如果是直接使用(javascript)那么只能够使用i和m,而"x"和"s"必须使用"$regex"
  范例:查询以"谷"开头的姓名
  db.students.find({"name":/谷/}).pretty();
  范例:查询姓名有字母a
  db.students.find({"name":/a/i}).pretty(); 其中,i表示忽略大小写
  上面的写法可以为:db.students.find("name":{"$regex":/a/i}).pretty();
  如果要执行模糊查询的操作,严格来说只需要编写一个关键字。
  正则操作之中,除了可以查询单个字段的内容之外,也可以查询数组数据。
  范例:查询数组数据。
  db.students.find("course":{"$regex":/语/}).pretty();
  mongodB中的正则符号和之前java正则是有一些小小差别。不建议使用以前的一些标记,正则就用在模糊数据的查询上。
  数据排序:
  在mongoDB里面数据排序操作使用"sort()"函数,在进行排序的时候可以有两个顺序:升序(1)、降序(-1)
  范例:数据排序
  db.students.find().sort({"sort":-1}).pretty();
  但是在进行排序的过程中,有一种方式称为自然排序,按照数据保存的先后顺序排序,使用"$natural"表示
  范例:自然排序
  db.students.find().sort({"$natural":-1}).pretty();
  在MongoDB数据库里面排序的操作相比传统关系型数据库的设置要简单。
  数据的分页显示:
  在MongoDB里面的数据分页显示也是符合大数据要求的操作函数:
  skip():表示跨过多少数据行。
  limit(n):取出的数据行的个数限制。
  范例:分页显示(第一页,skip(0),limit(5))
  db.students.find().skip(0).limit(5).sort({"age":-1}).spretty();
  范例:分页显示(第二页,skip(5),limit(5))
  db.students.find().skip(5).limit(5).sort({"age":-1}).spretty();
  这两个分页的控制操作,就是在以后只要有存在大数据的信息情况下都会使用它。
  数据更新操作
  对于MongoDB而言,数据的更新基本上是一件很麻烦的事情,如果在实际的工作中,真的具有此类的操作支持,那么最好的做法,在MongoDB里面对于数据的更新操作提供了两类函数:save()、update()。
  函数的基本使用
  如果要修改数据最直接的使用函数就是update()函数,但是这个函数的语法要求很麻烦。
  语法:db.集合.update(更新条件,新的对象数据(更新操作符),upsert,multi)
  -update:如果要更新的数据不存在,则增加一条新的内容(ture为增加、false为不增加)
  -multi:表示是否只更新满足条件的第一行记录,如果设置为false,则只更新第一行记录,如果是true全更新。
  以下是更新存在的数据:
  范例:将年龄是19岁的人的成绩更新为100分(此时会返回多条数据)
  db.students.update({"age":19},{"$set":{"score":100}},false,false); 选择只更新更新的第一条数据
  db.students.update({"age":19},{"$set":{"score":100}},false,true); 所有满足条件的数据都更新
  范例:更新不存在的数据
  db.students.update({"age":30},{"$set":{"name":"不存在"}},true,false)
  由于没有年龄是30岁的学生信息,所以此时相当于进行数据的创建。
  那么除了update()函数之外,还提供有一个save()函数,这个函数的功能与更新不存在的内容相似。
  范例:使用save()操作
  db.students.save({"_id":ObjectId("5123145d2fde")},{"age":50})
  save操作不好用,可以用update函数来使用。
  修改器:
  对于MongDB数据库而言,数据的修改会牵扯到内容的变更、结构的变更(包含有数组),所以在进行MongoDB设计的时候,就提供有一系列修改器的应用,那么之前使用的"$set"就是一个修改器的使用
  1、$inc:主要针对于一个数字字段,增加某个数字字段的内容;
  语法:{"$inc":{成员:内容}}
  范例:将所有年龄为19岁的学生成绩一律减少30分,年龄加一岁
  db.students.update({"age":19},{"$inc":{"score":-30,"age":1}},false,true);//全部执行,false,true
  2、$set:进行内容的重新设置
  语法:{"$set":{"成员":"新内容"}}
  范例:将年龄是20岁的人的成绩修改为89分
  db.students.update({"age":20},{"$set":{"score":19}})
  3、$unset:删除某个成员的内容
  语法:{"$unset":{"成员":1}}
  范例:删除"张三"的年龄与成绩信息
  db.students.update({"name":"张三"},{"$unset":{"age":1,"score":1}})
  执行之后指定的成员内容就消失了。
  4、$push:相当于将内容追加到指定的成员之中(基本上是数组)
  语法:${"$push":{"成员":value}}
  范例:向张三添加课程信息(此时张三信息里面没有course信息)
  db.students.update({"name":"张三"},{"$push":{"course":["语文","数学"]}})
  范例:向"谷大声-E"里面的课程追加一个"美术"
  db.students.update({"name":"股大声-E"},{"$push":{"course":"美术"]}})
  push就是进行数组数据的添加操作使用,如果没有数组则进行一个新的数组的创建,如果有则进行内容的追加。
  5、$pushAll:与"$push"类似的,可以一次追加多个内容到数组里面:
  语法:${"$pushAll":{成员:数组内容}}
  范例:向"王五"的信息里面添加多个课程内容
  db.students.update({"name":"王五"},{"$pushAll":{"course":["语文","数学"]}})
  6、$addToSet:向数组里面添加一个新的内容,只有这个内容不存在的时候才会增加。
  语法:{"$addToSet":{成员:内容}}
  范例:向王五的信息增加新的内容
  db.students.update({"name":"王五"},{"$addToSet":{"course":"跳舞"}})
  此时会判断要增加的内容在数组里面是否已经存在了,如果不存在则追加内容,如果存在,则不增加内容。
  7、$pop:删除数组内的数据:
  语法:{"$pop":{成员:内容}},内容如果设置为-1表示删除第一个,1表示删除最后一个
  范例:删除王五的一个课程
  db.students.update({"name":"王五"},{"$pop":{"course":"-1"}})
  范例:删除王五的最后一个课程:
  db.students.update({"name":"王五"},{"$pop":{"course":"1"}})
  8、$pull:从数组内删除一个指定内容的数据
  语法:{"$pull",{成员:数据}} 进行数据比对,如果是此数据则进行删除
  范例:删除王五内的跳舞的课程,如果存在就删除
  db.students.update({"name":"王五"},{"$pull":{"course":"跳舞"}})
  9、$pullAll:一次性删除多个内容
  语法:{"$pull":{成员:[数据1,数据2]}}
  范例:删除”顾大神-a“的二门课程
  db.students.update({"name":"股大声"},{"$pullAll":{"course":["跳舞","音乐"]}})
  10、$rename:为成员名称重命名
  语法:{"$rename":{旧的成员名称:新的成员名称}}
  范例:将"张三"name成员名称修改为"姓名"
  db.students.update({"name":"王五"},{"$rename":{"name":"姓名"}})
  在整个的MongoDB数据库里面,提供的修改器的支持很到位。
  删除数据:
  在MongoDB里面数据的删除实际上并不复杂,只需要使用"remove()"函数就可以了。
  但是,整个函数是有两个可选项。1、删除条件:满足条件的数据被删除 2、是否只删除一个数据,如果设置为true或者是1表示只删除一个。
  范例:清空info集合中的内容。
  db.infos.remove({});
  范例:删除所有姓名里面带有“谷”的信息。
  db.students.remove({"name":/谷/})
  范例:删除姓名带有"高"的信息,要求只删除一个
  db.students.remove({"name":/高/},true)
  删除操作里面依然需要使用限定查询的相关操作内容。
  游标:
  所谓的游标就是指的数据可以进行一行行的进行操作,非常类似于ResultSet数据处理。在MongoDB数据里面对于游标的控制非常简单,只需要使用find()函数就可以返回游标了。对于返回的游标如果要进行操作,则可以通过两个函数:一个是是否有下一行数据:hasNext(),另一个是取出当前数据next();
  var cursor = db.students.find();
  cursor.hasNext()
  cursor.next();
  以上是游标的操作形式,但是实际上并不可能这么去用,因为必须利用循环才能输出内容。
  相当于每一个数据都单独拿出来逐行的控制。当我们游标数据取出来的时候,实际上每行数据返回的都是Object型的内容,那么如果需要数据按照json的形式出现,可以使用printjson()函数完成。
  在所有的已知数据里,MongoDB的游标操作是最简单的,最直观的。
  索引
  在任何的数据库之中,索引都是一种提升数据库检索性能的手段,这一点在MongoDB数据库之中同样是存在的,在MongoDB数据库之中,依然会存在两种的索引的创建:一种是自动创建的;另一种是手工创建。
  范例:重新准备一个新的简单集合
  此时,我们在students集合之上并没有设置任何的索引,那么下面通过getIndexes()函数来观察在students集合已经存在索引内容。
  范例:查询默认状态下的students集合的索引内容。
  db.students.getIndexes();
  想创建自己的索引,如下:
  索引创建:db.集合名称.ensureIndex({列:1})
  -设置的1表示索引将按照升序的方式进行排列,如果使用降序设置"-1"
  范例:创建一个索引,在age字段上设置一个降序索引
  db.students.ensureIndexes({"age":-1})
  此时并没有设置索引的名字,所以名字是自动命名的。命名规范:“字段名称_索引的排序"
  范例:针对于当前的age字段上的索引做一个分析
  db.students.find("age":19).explain();
  此时的查询使用了索引的技术,但是下面再来观察一个查询,不使用索引字段。
  范例:针对score字段上设置查询
  db.students.find({"score":{"$gt":60}}).explain();
  此时在score上的字段并没有设置索引,所以当前的索引形式就变为了全集合扫描的模式。
  但是,如果说现在年龄和成绩一起执行查询。
  db.students.find({"$or":[{"age":{"$gt":19}},{"score":{"$gt":60}}]}).explain();
  这个时候虽然age字段上存在有索引,但是由于score字段上没有索引,所以依然使用的是全表扫描操作,所以此时,为了解决这个问题,可以使用一个符合索引。
  db.students.ensureIndex({"age":-1,"score":-1},{"name":"age_-1_score_-1"})
  范例:默认使用索引
  db.students.find({"age":19,"score":89}).explain()
  但是如果换到了条件之中:
  db.students.find({"$or":[{"age":{"$gt":19},{"score":{"$gt":60}}}]}).explain();
  但是发现并没有使用索引,所以如果这个时候看能够强制使用一次索引。hint()函数为强制使用一次索引操作。
  范例:强制使用索引
  db.students.find({"$or":[{"age":{"$gt":19},{"score":{"$gt":60}}}]}).hint({"age":-1,"score":-1}).explain();
  正常来讲,这个代码根本就不可能调用默认的索引执行,可使用hint()函数,告诉MongDB必须使用一次索引,由于此时在age和score两个字段已经设设置了符合索引,那么现在 的复合索引。
  但是在一个集合里面设置了过多的索引,实际上会导致性能下降,那么就可以删除索引。
  范例:删除一个索引
  db.students.dropIndex({"age":-1,"score":-1})
  可是如果一个一个删除索引,也会很麻烦,所以提供有删除全部索引的操作。
  范例:删除全部索引。
  db.students.dropIndex()
  所谓的删除全部索引,就是非"_id"的索引,所有的自定义索引。
  唯一索引:
  唯一索引的主要目的是用在某一个字段上,使该字段的内容不重复。
  范例:创建一个唯一索引
  db.students.ensureIndex({"name":1},{"unique":true})
  在name字段上的内容绝对不允许重复。
  范例:在students集合里面增加重复的数据。
  此时除了,name字段上的内容之外,发现所有的数据都不一样,但是由于在name字段上设置了唯一索引,所以整个程序里面,如果增加了重复内容,会出现以下的错误信息。
  过期索引:
  在一些程序点上回出现若干秒之后信息被删除的情况,例如:手机的信息验证码,那么在MongoDB里面可以轻松的实现过期索引,但是这个时间往往不怎么准确。
  范例:设置过期索引
  db.phones.ensureIndex({"time":1},{expireAfterSeconds:10})
  设置索引在10秒后过期
  等到10秒之后(永远不会那么准确)所保存的数据就会消失。这个操纵在进行一些临时数据保存的时候非常有帮助,最早如果没有MongoDB,而是使用最简单的关系型数据库进行开发这个过程是非常麻烦的。
  范例:在一个叫做phone集合里面设置过期索引
  如果要想实现过期索引,需要保存一个时间信息。
  db.phones.insert({"tel":"110","code":"110","time":new Date()})
  全文索引
  在一些信息管理平台上经常要进行信息模糊查询,最早的时候是利用了某个字段上实现的模糊查询,但是这个时候返回的信息,并不会很准确,因为只能够查询A字段或者是B字段,而在MongoDB里面实现了非常简单的全文检索。
  范例:定义一个新的集合:
  db.news.insert({"titel":"mldn java","content":"gyh"})
  范例:设置全文检索
  db.news.ensureIndex({"title":"text","content":"text"})
  范例:实现数据的模糊查询
  如果要想表示出全文检索,则使用"$text"判断符,而要想进行数据查询则使用"$search"运算符:
  -查询指定的关键字;{"$serach":"查询关键字"}
  -查询多个关键字(或关系):{"$search":"查询关键字 查询关键字 ..."}
  -查询多个关键字(与关系){"$search":"\"查询关键字\""}
  -查询多个关键字(排除某一个):{"$search":" "}
  范例:查询单个内容
  db.news.find("{"$text":{"$search":"gyh"}}")
  范例:包含有"gry"与"sfq"的信息
  db.news.find("{"$text":{"$search":"gry sfq"}}")
  范例:同时包含有"mldn" 与 "lxh"的内容
  db.news.find({"$text":{"$search":"\"mldn\" \"lxh\""}})
  范例:包含有"mldn",但是没有"gyh"的信息。
  db.news.find("$text":{"$serach":"\"mldn\" \"lxh\" -gyh"})
  但是在进行全文检索操作的时候,还可以使用相似度的打分来判断检索成果。
  范例:为结果打分
  db.news.find({"$text":{"$search":"gyh"}},{"score":{"$meta":"textScore"}}).sort({"score":{"score":{"$meta":"textScore"}})
  按照打分的成绩进行排列可以实现更加准确的信息搜索。
  但是在这里面还有一个小问题,如果一个集合的字段太多了,那么每一个字段都分别设置全文检索麻烦点。所以简单一些,可以为所有字段设置全文检索。
  范例:为所有字段设置全文索引
  db.news.ensureIndex({"$**":"text"})
  这是一种最简单的设置全文索引的方式,但是尽可能不用,速度慢。
  地理信息索引
  地理信息索引分为两类:2D平面索引,另外就是2DSphere球面索引。在2D索引里面基本上能够保存的信息都是坐标,而且坐标保存的就是经纬度坐标。
  范例:定义一个商铺的集合
  db.shop.insert({loc:[10,11]})
  范例:为shop集合定义2D索引
  db.shop.ensureIndex({"loc":"2d"})
  这个时候shop集合就可以实现坐标位置的查询,而要进行查询有两种查询方式:
  -1:"$near"查询,查询距离某个点最近的坐标点
  -2:"$geoWithin"查询:查询某个形状内的点
  范例:假设我现在的坐标是[11,11]
  db.shop.find({"loc":{"$near":[11,11]}})
  但是,如果执行以上的查询,实际上会将集合里面的额前100个点的信息都返回来了,可是太远了,设置了一个距离范围--5个点内。
  范例:设置查询的距离范围
  db.shop.find({"loc":{"$near":[11,11],"$maxDistance":5}})
  但是,我们需要注意一点,在2D的索引里面虽然支持最大距离,但是不支持最小距离。
  但是,也可以设置一个查询的范围,使用"$geoWithin"查询,可以设置以下几种范围:
  -1、矩形范围($box) {"$box":[[x1,y1],[x2,y2]]}
  -2、圆形范围($centor):{"$center":[[x1,y1],r]}
  -3、多边形($polygon):{"$polygon":[[x1,y1],[x2,y2],[x3,y3]]}
  范例:查询矩形
  db.shop.find({"loc":{"$geoWithin":[[9,9],[11,11]]}})
  范例:查询圆形
  db.shop.find({"loc":{"$geoWithin":[[9,9],2]}})
  在MongoDB数据库里面,除了一些支持的操作函数之外,还有一个重要的命令:runCommand(),这个函数可以执行特定的MongoDB命令。
  范例:利用runCommand()实现信息查询
  db.runCommand({"geoNear":"shop",near:[10,10],maxDistance:5,num:2})
  这类的命令可以说是MongoDB之中最为基础的命令。
  聚合函数:
  MongoDB产生的背景是在大数据的环境下,实际上大数据就是进行信息的收集汇总。那么必须存在有信息的统计操作,而这样的统计操作称为聚合(直白:分组统计就是一种聚合操作)。
  取得集合的数据量
  对于集合的数据量而言,在MongoDB里面直接使用count()函数就可以完成。
  范例:统计students表中的数据量
  db.students.count();
  范例:模糊查询
  db.students.count({"name":/张/i})
  在信息查询的时候,不设置条件永远要比设置条件的查询快很多。也就是说,在之前的代码编写里面不管是查询全部还是模糊查询,实际最终都使用模糊查询的一种(没有设置关键字)。
  消除重复数据:
  在学习SQL的时候,对于重复的数据可以使用"DISTINCT",那么这一操作在MongoDB之中依然支持。
  范例:查询所有name的信息(没有重复)。
  -本次的操作没有直接的函数支持,只能怪利用runCommand()函数。
  db.runCommand({"distinct":"students","key":"name"})
  此时,实现了对于name数据重复值得筛选。
  group操作
  使用"group"操作可以实现数据的分组操作,在MongoDB里面会将集合指定的Key的不同进行分组操作,并且每一个组都会产生我们处理的文档结果。
  范例:查询所有年龄大于等于19岁的学生信息,并且按照年龄分组。
  db.runCommand({"group":{"ns":"students",
  "key":{"age":true},
  "initial":{"count":0},
  "condition":{"age":{"$gte":19}},
  "$reduce":function(doc,prev){
  prev.count++;
  }
  }});
  以上的操作代码里面实现的就属于一种MapReduce,但是这样只是根据传统的数据库设计思路操作实现所谓的分组操作。但是这个分组操作的最终结果是有限的。
  MapReduce
  MapReduce是整个大数据的精髓所在(实际中别用),所谓的MapReduce就是分为两步处理数据:
  -Map:将数据分别取出
  -Reduce:负责数据的最后处理。
  可是要想在MongoDB里面实现MapReduce处理,那么复杂度是相当高的。
  范例:建立一组雇员数据
  db.emps.insert({"name":"张三","age":30,"sex":"男","job":"CLERK","salary":1000})
  使用MapReduce操作会将处理结果保存在一个单独的集合里面,而最终的处理效果如下:
  范例:按照职位分组,取得每个职位的人名
  -    编写分组的定义:
  var jobMapFun = function()
  {
  emit(this.job,this.name);//按照job分组,取出name
  };
  第一组:{key:"CLERK",values:[姓名,姓名,,,,]}
  -编写Reduce操作:
  var jobReduceFun= function(key,values){
  return{"job":key,"names":values};
  }
  var jobFinalizeFun = function(key,values){
  if(key == "PRESIDENT"){
  return{"job":key,"names":value,"info":"公司的老大"}   
  }
  return{"job":key,"names":values};
  }
  -进行操作的整合:
  db.runCommand({"mapreduce":"emps","map":jobMapFun,"reduce":jobReduceFun,"out":"t_job_emp",”“finalize":jobFinalizeFun})
  现在执行之后,所有的处理结果都保存在"t_job_temp"集合里面。
  范例:统计出个性别的人数、平均工资、最低工资、雇员姓名
  var sexMapFun = function(){
  //定义好分组的条件,以及每个集合要取出的内容
  emit(this.sex,{"ccount":1,"cavg":this.salary,"cmax":this.salary,"cmin":this.salary,"cname":this.name});//emit表示分组处理函数
  }
  var sexReducnFun = function(key,values){
  var total =0;//统计
  var sum =0;//计算总工资
  var max = values[0].cmax; //第一个数据是最高工资
  var min = values[0].cmin;//第一个数据是最低工资
  var names = new Array();//定义数组内容
  for(var x in values){//表示循环取出里面的数据
  total+= values[x].ccount;//特殊增加
  sum+=values[x].csal;//就可以循环取出所有的工资,并且累加
  if(max<values[x].cmax){//不是最高工资
  max = values[x].cmax;
  }
  if(min>values[x].cmin){//不是最低工资
  min = values[x].cmin;
  }
  name[x] = values[x].cname; //保存姓名
  }
  var avg = (sum/total).toFixed(2);//设置两位小数
  //返回数据的处理结果
  return("count":total,"avg":avg,"sum":sum,"max":max,"min":min,"names":names);
  };
  db.runCommand({
  "mapreduce”:"emps",
  "map":sexMapFun,
  "reduce":sexReduceFun,
  "out","t_sex_emp"
  });
  虽然大数据的时代,提供有最强悍的MapReduce,但是从现实的开发中,真的不可能实现起来,很耗时。但是该功能可用定时任务来定期处理数据结果。
  聚合框架:
  MapReduce功能强大,但是它的复杂度和功能一样强大,那么我们说很多时候,我们需要MapReduce功能,但是又不想把代码写的太复杂,所以从Mongo 2.x版本之后引入了聚合框架并且提供了聚合函数:aggregate()。
  group操作
  group主要进行分组的数据操作:
  范例:实现聚合查询的功能--求出每个职位的雇员人数
  db.emps.aggregate(["$group":{"_id":"$job","job_count":{"$sum":1}}]);
  这样的操作,更加符合传统的group by子句的操作使用。
  范例:求出每个职位的总工资
  db.emps.aggregate(["$group":{"_id":"$job","job_count":{"$sum":"$salary"}}]);
  在整个聚合框架里面如果要使用每行的数据使用:"$字段名称"
  范例:计算出每个职位的平均工资
  db.emps.aggregate(["$group":{"_id":"$job","job_sal":{"$sum":"$salary"},"job_avg":{"$avg":"$salary"}}]);
  范例:求出最高与最低工资
  db.emps.aggregate(["$group":{"_id":"$job","max_sal":{"$max":"$salary"},"min_sal":{"$min":"$salary"}}]);
  以上的几个与SQL类似的操作计算成功的实现了。
  范例:计算出每个职位的工资数据(数组显示)
  db.emps.aggregate([{
  "$group":{
  "id":"$job",
  "sal_data":{"$push":"$salary"}
  }
  }]);
  范例:求出每个职位的人员:
  db.emps.aggregate([{
  "$group":{
  "_id":"$job",
  "sal_data":{"$push":"$name"}
  }
  }]);
  以上使用"$push"的确可以将使用的数据变为数组进行保存,但是有一个问题出现了,重复的内容也会进行保存。在MongoDB里面提供有取消重复的设置。
  范例:取消重复的数据
  db.emps.aggregate([{
  "$group":{
  "id":"$job",
  "addToSet":{"$push":"$name"}
  }
  }]);
  默认情况下,是将所有的数据都保存进去,但是希望可以保存第一个和最后一个的数据。
  范例:保存第一个内容
  db.emps.aggregate([{
  "$group":{
  "id":"$job",
  "addToSet":{"$first":"$name"}
  }
  }]);
  范例:保存最后一个内容
  db.emps.aggregate([{
  "$group":{
  "id":"$job",
  "addToSet":{"$last":"$name"}
  }
  }]);
  虽然可以实现分组处理,但是有一点需要注意,所有的分组数据是无序的,并且都是在内存之中完成,所以不可能支持大数据量。
  $project
  可以利用"$project"来显示数据列的显示规则,那么可以执行的规则如下:
  -普通列({成员:1|true}):表示要显示的内容
  -"_id"列({"_id":0|false}):表示"_id"列的显示是否显示
  -条件过滤列({成员:表达式}):满足表达式之后的数据可以进行显示
  范例:只显示name、job列
  db.emps.aggregate({"$project":{"_id":0,"name":1}})
  此时,只有设置进去的列才可以被显示出来,而其他的列不能够被显示出来。实际上这就属于数据库的投影机制。
  实际上在进行数据投影的过程里面也支持四则运算:加法("$add")、减法("$subtract")、乘法("$multiply")、除法("$divide")、求模("$mod")
  范例:观察四则运算
  db.emps.aggregate([{"$project":{
  "_id":0,
  "name":1
  "job":1,
  "salary":{"年薪":{"$multiply":["$salary",12]}}
  }}])
  除了四则运算之外,也支持如下的各种运算fu :
  -关系运算:大小比较("$cmp")、等于("$eq")、大于("$gt")、大于等于("$gte")、小于“$lt”、小于等于"$lte"、不等于"$ne"、判断NULL("$ifNull"),这些操作返回的结果都是布尔型数据。
  -逻辑运算:与"$and"、或"$or"、非"$not"
  -字符串操作:连接"$concat"、截取"$substr"、转小写"$ToLower"、转大写"$ToUpper"、不区分大小写比较"$strcasecmp"
  范例:找出所有工资大于等于2000的雇员的姓名、年龄、工资
  db.emps.aggregate([{"$project":{
  "_id":0,
  "name":1,
  "job":1,
  "工资":"$salary",
  "salary":{"$gte":["$salary",2000]}
  }}])
  范例:查询职位是manager的信息
  -MongoDB的数据是区分大小写的:
  db.emps.aggregate([{"$project":{
  "_id":0,
  "name":1,
  "职位":"$job",
  "工资":"$salary",
  "job":{"$eq":["$job",{"$toUpper":"manager"}]}
  }}])
  范例:使用字符串截取
  db.emps.aggregate([{"$project":{
  "_id":0,
  "name":1,
  "职位":"$job",
  "job":{"前面三位":{"$substr":["$job",0,3]}}
  }}])
  利用"$project"实现的投影操作功能相当强大,所有可以出现的操作几乎都能够使用。
  $sort
  使用"$sort"可以实现排序,设置1表示升序,设置-1表示降序。
  范例:实现排序
  db.emps.aggregate([{"sort":{"age":-1,"salary":1}}])
  分页处理:$limit $skip
  $limit:负责数据的取出个数
  $skip:数据的跨过个数
  $unwind
  在查询数据的时候经常会返回数组信息,但是数组并不方便信息的浏览,所以提供有"$unwind"可以将数组数据变为独立的字符串内容。
  $geoNear
  使用"$geoNear"可以得到附近的坐标点
  $out
  "$out":利用此操作可以将查询结果输出到指定的集合里面。
  范例:将投影的结果输出到集合之中
  db.emps.aggregate([{
  "$project":{"_id":0,"name":1,"salary":1,"job":1}},
  {"$out":"emp_infos"}
  ])
  这类的操作相当于实现最早的数据表的复制操作。
  固定集合:
  所谓的固定集合指的是规定集合大小,如果要保存的内容已经操作了集合的长度,那么会采用LRU的算法(最近最少使用原则)将最早的数据移出,从而保存新的数据。
  默认情况下一个集合可以使用createCollection()函数创建,或者使用增加数据后自动创建,如果使用固定集合,必须明确的创建一个空集合。
  范例:创建一个空集合(固定集合)
  db.createCollection("depts",{"capped":true,"size":1024,"max":5})
  其中,"capped":为一个固定集合,而"size:1024"指的是集合所占的空间容量(字节),"max:5"表示最多只能有5条记录文档。
  范例:向集合里面保存5条数据。
  db.depts.insert({"deptno":10,"dename":"财务部-A","loc":"北京"})
  db.depts.insert({"deptno":11,"dename":"财务部-A","loc":"北京"})
  db.depts.insert({"deptno":12,"dename":"财务部-A","loc":"北京"})
  db.depts.insert({"deptno":13,"dename":"财务部-A","loc":"北京"})
  db.depts.insert({"deptno":14,"dename":"财务部-A","loc":"北京"})
  此时已经达到集合的上限,那么继续保存新的内容。
  db.depts.insert({"deptno":15,"dename":"财务部-F","loc":"北京"})
  此时,最早保留的数据已经消失了。实际上,这种操作跟缓存机制非常相似的,例如:在百度上经常会出现搜索的关键词,这些词都是会被不断替换的。
  GridFS
  在MongoDB里面支持大数据的存储(例如:图片、音乐、各种二进制数据),但是这个做法需要用户自己处理了。使用"mongofiles"完成。
  1、利用命令行进入到所在的路径下
  2、将文件保存到文件库之中:
  mongofiles --port=27001 put photo.tif
  此时,会向数据库里面写入要保存的二进制数据。
  3、查看保存的文件
  mongofiles --port=27001 list
  4、在MongoDB里面有fs系统的集合,这个集合默认保存在test集合的数据库中
  use test;
  db.fs.files.find();
  5、删除文件
  mongofiles --prot=27001 delete photo.tif
  等于在MongoDB里面支持二进制数据的保存,但是存在的意义不大。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-424156-1-1.html 上篇帖子: mongodb 3.4复制集配置 下篇帖子: 利用Scrapy爬取所有知乎用户详细信息并存至MongoDB
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表