MongoDB简介、功能以及示例

578 阅读4分钟

什么是MongoDB

MongoDB是一个文档数据库,提供好的性能,领先的非关系型数据库。采用JSON存储文档数据。2007年10月,MongoDB由10gen团队所发展。2009年2月首度推出。MongoDB用c++编写的。

优势:

  • 面向文档的存储:以 JSON 格式的文档保存数据。
  • 任何属性都可以建立索引。
  • 复制以及高可扩展性。
  • 自动分片。
  • 丰富的查询功能。
  • 快速的即时更新。
  • 来自 MongoDB 的专业支持。

elasticsearch与MongoDB相同点与不同点

相同点:

1、都是以json格式管理数据的nosql数据库。

2、都支持CRUD操作。

3、都支持聚合和全文检索。

4、都支持分片和复制。

5、都支持阉割版的join操作。

6、都支持处理超大规模数据。

不同点:

1、es是java编写,通过RESTFul接口操作数据。MongoDB是C++编写,通过driver操作数据。(es对java开发更有好,利于排查理解)

2、MongoDB的分片有hash和range两种方式,es只有hash一种。

3、es是天生分布式,主副分片自动分配和复制,开箱即用。mongodb的分布式是由“前置查询路由+配置服务+shard集合”,需要手动配置集群服务。

4、内部存储ES是到排索引+docvalues+fielddata。MongoDB暂时未知。

5、es全文检索有强大的分析器且可以灵活组合,查询时智能匹配。MongoDB的全文检索字段个数有限制。

6、es所有字段自动索引,MongoDB的字段需要手动索引。

7、MongoDB支持多文档事务

MongoDB特性:灵活性,可扩展性,强大的查询语言,优异的性能

mongodb安装与启动

MongoDB下载网址: www.mongodb.com/download-ce…

选择版本、系统环境以下将以.tgz 为例

下载完成将得到mongodb-linux-x86_64-rhel70-4.2.3.tgz包,将其上传到linux(sentos7)某个目录

上传完成后解压并移动到/usr/local/mongodb目录

mv mongodb-linux-x86_64-rhel70-4.2.3 /usr/local/mongodb

创建专门的负责的用户并赋予权限

cd /usr/local/mongodb 
groupadd mongodb
useradd -s /sbin/nologin -g mongodb -M mongodb
mkdir data log run
chown -R mongodb:mongodb data log run

在/usr/local/mongodb 里面创建一个配置文件 mongodb.conf

vim mongodb.conf 并写入下面的信息:

bind_ip=0.0.0.0
port=27017
dbpath=/usr/local/mongodb/data/
logpath=/usr/local/mongodb/log/mongodb.log
pidfilepath =/usr/local/mongodb/run/mongodb.pid
logappend=true
fork=true    
maxConns=500
noauth = true


配置解释:
fork=true  运行在后台
logappend=true  如果为 true,当 mongod/mongos 重启后,将在现有日志的尾部继续添加日志。否则,将会备份当前日志文件,然后创建一个新的日志文件。默认为 false。
noauth=true  不需要验证用户密码
maxConns=500  最大同时连接数,默认2000

以上是MongoDB的安装与启动的准备工作,可直接启动 启动命令:

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/mongodb.conf

配置环境变量

vim /etc/profile

在/etc/profile文件末尾添加一行:

 export PATH=/usr/local/mongodb/bin:$PATH

让其生效:

source /etc/profile

查看当前mongodb的版本:

mongod --version

MongoDB的crud

执行mongo命令连接MongoDB

数据库的操作

MongoDB自带的原始数据库:

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

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

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

查看:show dbs

创建:use 数据库名

使用use时,如果数据库存在则会进入到相应的数 据库,如果不存在则会自动创建

一旦进入数据库,则可以使用db来引用当前库 ;如果是第一次创建,那个这个数据库只是存在于内存当中,直到这个数据库中创建了集合后才会持久化到磁盘上

删除:db.dropDatabase()

用于删除已经持久化的数据库,刚创建在内存中的数据库删除无效

集合的操作

创建: db.createCollection("集合名称")

查看: show tables 或者 show collections

删除: db.集合名称.drop()

文档的操作

添加:

db.xyj.insert({name:"猪八戒",age:600,gender:"男"});   

db.xyj.insertOne({_id:"1001",name:"孙悟空",age:500,gender:"男"});

xyj是集合名称,如果该集合还没有被创建,则会自动创建

批量添加:

db.xyj.insert([
     {name:"沙和尚",age:400,gender:"男"},
     {name:"白骨精",age:200,gender:"女"},
     {name:"蜘蛛精",age:200,gender:"女"}
]);

db.xyj.insertMany([
     {name:"沙和尚",age:400,gender:"男"},
     {name:"白骨精",age:200,gender:"女"},
     {name:"蜘蛛精",age:200,gender:"女"}
]);

db.collection.insertOne()     插入一个文档对象
db.collection.insertMany()    插入多个文档对象

当我们向集合中插入文档时,如果没有给文档指定 _id属性,则数据库会自动为文档添加 _id该属性用来作为文档的唯一标识。 _id我们可以自己指定,如果我们指定了数据库就不会在添加了,如果自己指定 _id 也必须确保它的唯一性

额外小知识

try{
	db.xyj.insert([
      {name:"沙和尚",age:400,gender:"男"},
      {name:"白骨精",age:200,gender:"女"},
      {name:"蜘蛛精",age:200,gender:"女"}
    ]);
}catch(e){
	print(e)
}

可以知道那条插入失败

覆盖修改:db.xyj.update({_id:"1001"},{age:NumberInt(520)})

执行效果:_id:1001这条数据只有age一个字段了

局部修改: db.xyj.update({_id:"1001"},{$set:{age:NumberInt(30)}})

执行效果:只会修改这条数据的某个字段

批量修改: db.xyj.update({name:"蜘蛛精"},{$set:{age:NumberInt(100)}},{multi:true})

在修改条数据时,必须要加上第三个参数**{multi:true}**,否则只会修改第一条数据

字段增加操作: db.xyj.update({_id:"1001")},{$inc:{age:NumberInt(1)}})

注意:$inc 对应的字段必须是数字,而且递增或递减的值也必须是数字。

删除文档: db.xyj.remove({_id:"1001"})

删除文档字段: db.xyj.update({"_id":1001}, {"$unset": {"name":1}})

$unset 指定字段的值只需是任意合法值即可。

删除所有: db.xyj.remove({})

插入测试数据

db.xyj.insertMany([
     {name:"沙和尚",age:400,gender:"男",hobby:["挑扁担", "游泳"]},
     {name:"白骨精",age:200,gender:"女",hobby:["杀人", "吃喝"]},
     {name:"蜘蛛精",age:200,gender:"女",hobby:[""洗澡", "跳舞"]},
	 {name:"唐僧",age:26,gender:"",hobby:["坐禅","骑马"]}
]);

数组添加($push):

db.xyj.update({"name": "白骨精"}, {"$push": {"hobby": "念佛"}})

删除元素($pop):

db.xyj.update({"_id": ObjectId("8fe58t098n6d71f45h274l16")}, {"$pop": {"hobby": 1}})  // 删除最后一个元素
db.xyj.update({"_id": ObjectId("8fe58t098n6d71f45h274l16")}, {"$pop": {"hobby": -1}})  // 删除第一个元素

删除特定元素($pull):

db.xyj.update({"_id": ObjectId("8fe58t098n6d71f45h274l16")}, {"$pull": {"hobby": "吃喝" }})

更新嵌套数组的值:

db.xyj.insert({_id:"1001", name:"孙悟空", age:500, gender:"男", address: [{place: "五行山", tel: 123}, {place: "花果山", tel: 321}]});

db.xyj.update({"_id": "1001"}, {"$set": {"address.0.tel": 213}})

数组查询:

db.xyj.find({"hobby":"骑马"})

多个元素的查询
db.xyj.find({"hobby":{"$all": ["挑扁担", "游泳"]}})
只有hobby数组同时存在"挑扁担",和"游泳"才会匹配

限制数组长度查询
db.xyj.find({"hobby": {"$size": 2}})
只有数组的长度是2才会匹配

查看所有:

mongodb:   db.xyj.find()
sql:   select * from xyj

投影查询:

mongodb:   db.xyj.find({name:"孙悟空"},{name:1, age:1, _id:0})   
sql:   select name, age from xyj where name= "孙悟空"
1表示显示 0表示强制隐藏

按字段条件查询:

mongodb:   db.xyj.find({name:"猪八戒"})
sql:   select * from xyj where name= "猪八戒"

按字段条件查询并只返回一条:

db.xyj.findOne({name:"猪八戒"})

组合查询:

语法:db.xyj.find($and:[{},{},{}])
//查询年龄大于300并且小于500的
db.xyj.find({$and:[{age:{$gt:NumberInt(300)}},{age:{$lt:NumberInt(500)}}]})  
//查询名字里有”精“的或者年纪大于200的
db.xyj.find({$or:[{age:{$gt:NumberInt(30)}},{name:/精/}]})  

$gt--》大于;$lt--》小于;$gte--》大于等于;$lte--》小于等于;$ne---》不等于(不等于不一定要用于数字)

包含查询:

 db.xyj.find({age:{$in:[200,300]}})

不包含:

 db.xyj.find({age:{$nin:[300,400]}})

Like:

db.xyj.find({"name": /精/})

统计查询:

db.xyj.count()或者db.xyj.count({字段:条件})

取模:

db.xyj.find({"age": {$mod: [5, 1]}})
比如我们要匹配 age % 5 == 1

是否存在($exists)

db.xyj.find({"love": {"$exists": true}})  // 如果存在字段love,就返回
db.xyj.find({"love": {"$exists": false}}) // 如果不存在字段love,就返回

分页查询

limit:显示几条记录

skip:跳过几条记录

只查询2条:db.xyj.find().limit(2)

跳过前2条再查询2条:db.xyj.find().limit(2).skip(2)

结合排序:db.xyj.find().limit(2).skip(2).sort({age:1}) // 1代表升序,-1代表降序

执行顺序: sort > skip > limit

聚合管道

较常见的管道操作符以及他们的作用:

操作符 描述 语法
$project 数据投影,主要用于重命名,增加,删除字段 db.article.aggregate({ $project : {title : 1 ,author : 1 ,}});
$match 过滤,筛选符合条件的文档,作为下一阶段输入 db.articles.aggregate( [{ $match : { score : { $gt : 70, $lte : 90 } } },{ $group: { _id: null, count: { $sum: 1 } } }] );
$limit 限制经过管道的文档数量 db.article.aggregate({ $limit : 5 });
$skip 待操作集合处理前跳过部分文档 db.article.aggregate({ $skip : 5 });
$unwind 将数组拆分成独立字段 db.article.aggregate({$project:{author:1,title:1,tags:1}},{$unwind:"$tags"})
$group 对数据进行分组 db.article.aggregate({ $group : {_id : "$author",docsPerAuthor : { $sum : 1 },viewsPerAuthor : { $sum : "$pageViews" }}});
$sort 对文档按照指定字段排序 db.users.aggregate( { $sort : { age : -1, posts: 1 } });
$sample 随机选择从其输入指定数量的文档。 { $sample: { size: <positive integer> } }
$out 必须为pipeline最后一个阶段管道,因为是将最后计算结果写入到指定的collection中
$indexStats 返回数据集合的每个索引的使用情况 { $indexStats: { } }

插入测试数据

document1=({name:'dogOne',age:1,tags:['animal','dog'],type:'dog',money:[{min:100},{norm:200},{big:300}]});

document2=({name:'catOne',age:3,tags:['animal','cat'],type:'cat',money:[{min:50},{norm:100},{big:200}]});

document3=({name:'catTwo',age:2,tags:['animal','cat'],type:'cat',money:[{min:20},{norm:50},{big:100}]});

document4=({name:'dogTwo',age:5,tags:['animal','dog'],type:'dog',money:[{min:300},{norm:500},{big:700}]});

document5=({name:'appleOne',age:0,tags:['fruit','apple'],type:'apple',money:[{min:10},{norm:12},{big:13}]});

document6=({name:'appleTwo',age:0,tags:['fruit','apple'],type:'apple',money:[{min:10},{norm:12},{big:13}]});

document7=({name:'pineapple',age:0,tags:['fruit','pineapple'],type:'pineapple',money:[{min:8},{norm:9},{big:10}]});

db.mycol.insert(document1)

db.mycol.insert(document2)

db.mycol.insert(document3)

db.mycol.insert(document4)

db.mycol.insert(document5)

db.mycol.insert(document6)

db.mycol.insert(document7)

假定我们想提取money中min为100的文档,并且只输出名称和money数组中的min那一项

db.mycol.aggregate(
   {$match:{'money.min':100}},
   {$project:{_id:0,name:'$name',minprice:'$money.min'}}
)

假定我们想提取money中min小于100的文档,并且限制3个文档,跳过一个文档再显示

db.mycol.aggregate(

    {$match:{'money.min':{$lt:100}}},

    {$limit:3},

    {$skip:1},

    {$project:{_id:0,name:'$name',minprice:'$money.min'}}

    )

通过type类型来对数据进行分类,并且同时统计他们的年龄age总和

db.mycol.aggregate(
    {$group:{_id:'$type',sumage:{$sum:'$age'}}}
)

按照年龄对数据进行排序

db.mycol.aggregate(
    {$group:{_id:'$type',sumage:{$sum:'$age'}}},
    {$sort:{sumage:1}}
)