Mongodb基础 学习小结

 

 

 

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展
的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰
富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以
存储比较复杂的数据类型。
Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,
几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

MongoDB主要场景如下:
1)网站实时数据处理。非常适合实时的插入、更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
2)缓存。由于性能很高,它适合作为信息基础设施的缓存层。在系统重启之后,由它搭建的持久化缓存层可以避免下层的数据源过载。
3)高伸缩性的场景。非常适合由数十或数百台服务器组成的数据库,它的路线图中已经包含对MapReduce引擎的内置支持。

不适用的场景如下:
1)要求高度事务性的系统。
2)传统的商业智能应用。
3)复杂的跨文档(表)级联查询。

 

/*********** Windows版
Windows 安装 msi 包
下载:https://www.mongodb.com/download-center#community
选择 Community Server 选择一个稳定版
安装...

然后找个合适位置 建立目录,用于存放数据和日志:
md "\data\db" "\data\log"

测试启动:(必须指定db路径) */
"C:\Program Files\MongoDB\Server\3.6\bin\mongod.exe" --dbpath="c:\data\db"

/*将 MongoDB 安装为 Windows 服务:
创建配置文件:C:\Program Files\MongoDB\Server\3.6\mongod.cfg
内容如下:(指定了log和db的位置) */
systemLog:
destination: "file"
path: "c:\\data\\log\\mongod.log"
storage:
dbPath: "c:\\data\\db"

// 安装服务:
sc.exe create MongoDB binPath= "\"C:\Program Files\MongoDB\Server\3.6\bin\mongod.exe\" --service --config=\"C:\Program Files\MongoDB\Server\3.6\mongod.cfg\"" DisplayName= "MongoDB" start= auto

// 如果成功则显示:CreateService SUCCESS
// 以后就可以系统服务中管理了。

// 卸载服务:(需要先停止服务)
sc.exe delete MongoDB

/**
管理工具 Robomongo :
下载:https://robomongo.org/download
MongoChef
下载:http://3t.io/mongochef/#mongochef-download-compare
*/
// ----------------------------------------------------------------------------

 


//-------------------------------------- Linux版 -------------------------------
// Mongodb 需要64位linux 本次使用ubuntu18.0.4 如果是centos,某些命令会不同。
// 到官网下载tar包 tgz,  注意选择OS版本

// https://www.mongodb.com/download-center

tar zxvf mongodb-linux-x86_64-3.6.4.tgz
mv mongodb-linux-x86_64-3.6.4 mongodb364
cd mongodb364

// 建立所需的db文件夹和logs文件夹,在根目录创建,是因为停止服务时,不带参数,会默认寻找此路径
mkdir -p /data/db
mkdir -p /data/logs

// 修改环境变量,vim /etc/profile 添加
export PATH=/home/mongodb364/bin:$PATH

source /etc/profile // 生效

// ------------如果不能启动服务,ubuntu18 可能需要安装某些依赖。--------------
// 如果 apt-get 出现 E: Unable to locate package 问题, 则执行以下命令:
add-apt-repository main
add-apt-repository universe
add-apt-repository restricted
add-apt-repository multiverse

apt-get update
// 如果不能解决,再执行这条:
apt-get upgradee

// 移除某个包:
apt-get remove xxx

// 错误: libcurl.so.4: version `CURL_OPENSSL_3' not found
apt-get install curl
apt-get install libcurl3

// CentOS的依赖
yum install libcurl openssl  // 安装依赖 

 

// 启动服务    指定路径       --port 10086 指定端口
bin/mongod --dbpath=/opt/mongodb/data/db --logpath=/opt/mongodb/logs/mongo.log --fork  

bin/mongo --help // 使用客户端帮助 
bin/mongo        // 首次本机登录无需认证

bin/mongod --config /etc/mongod.conf  // 或者指定配置文件启动,通常在设置安全后

// 停止服务 mongod --shutdown   // 不指定dbpath, 默认会寻找 /data/db
// 停止服务 mongod --shutdown --dbpath /home/mongodb364/data/db  // 指定dbpath
// 停止服务 mongod --shutdown --config=/etc/mongod.conf  // 也可指定配置文件

 

/************* 安全性流程:
    1.创建超级管理员
    2.修改配置文件,启用身份验证
    3.重启服务
    4.使用超级管理员登录
    5.创建并使用普通用户
*************/

// -------------------------------使用配置文件:--------------------------
vim /etc/mongod.conf  // 较简单的格式
bind_ip = 0.0.0.0
port=27017
dbpath=/data/db
logpath=/data/logs/mongod.log
pidfilepath=/opt/mongodb/mongod.pid
fork=true
logappend=true
auth=true

 

// ---------------------------YAML格式配置文件--------------------------

如果是 rpm / yum 方式安装,则自动添加 /etc/mongo.conf ,示例:

# mongod.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/logs/mongod.log

# Where and how to store data.
storage:
  dbPath: /data/db
  directoryPerDB: true
  journal:
    enabled: true
#  engine:
#  mmapv1:
#  wiredTiger:

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /opt/mongodb/mongod.pid  # location of pidfile
  timeZoneInfo: /usr/share/zoneinfo

# network interfaces
net:
  port: 27017
  bindIp: 127.0.0.1  # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.

security:
  authorization: "enabled"
#operationProfiling:

#replication:

#sharding:

## Enterprise-Only Options

#auditLog:

#snmp:

 

 

bin/mongod --config /etc/mongod.conf // 指定配置文件启动
// 如果启动失败,请检查配置中文件路径是否正确

 

//------------------------配置远程连接---------------------------------
systemctl status ufw    // 查看防火墙状态
systemctl stop ufw      // 停止防火墙
ufw allow 27017         // 防火墙允许端口
// --------上面是ubuntu的,下面是centos7的。--------------------------
firewall-cmd --permanent --add-port=27017/tcp  // 添加3306
systemctl restart firewalld.service   // 重启fw
firewall-cmd --list-all         // 查看fw

 

 

如果需要,也可将Mongodb安装为服务,便于管理

############# 在 CentOS7 中安装为服务
# 1. 创建用户和组
groupadd -g 1002 mongogrp
useradd -u 1002 -g mongogrp -G mongogrp mongouser
passwd mongouser

# 2. 更改mongodb文件夹拥有者
chown -R mongouser:mongogrp mongod/

# 3.编辑服务文件 /etc/systemd/system/mongod.service 并保存
#   写User和Group是限制指定用户可以操作
[Unit]
Description=MongoDB Server
After=network.target remote-fs.target nss-lookup.target

[Service]
User=mongouser
Group=mongogrp
Type=forking
ExecStart=/data/mongod/bins/mongod -f /data/mongod/conf/mongod.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/data/mongod/bins/mongod --shutdown -f /data/mongod/conf/mongod.conf
PrivateTmp=true
LimitFSIZE=infinity
LimitCPU=infinity
LimitAS=infinity
LimitNOFILE=64000
LimitNPROC=64000

[Install]
WantedBy=multi-user.target


# 4. 试试用 mongouser 操作服务
sudo systemctl daemon-reload
sudo systemctl start mongod 
sudo systemctl status mongod 
sudo systemctl stop mongod 
sudo systemctl enable mongod      # 开机启动
sudo systemctl disable mongod     # 关闭开机启动
sudo systemctl is-enabled mongod  # 检查是否开机启动
sudo systemctl kill mongod        # 杀死进程mongod

sudo systemctl list-units -t service          # 列出已激活的服务
sudo systemctl list-units -t service -a       # 列出所有服务

 

 

//###########################  mongo shell  ####################################

mongo  // 登录本机(未开启认证时)
    // 执行以下命令,可以添加 管理员
    use admin;
        db.createUser(  
          {  
            user: "admin",  
            pwd: "admin",  
            roles: [ { role: "root", db: "admin" } ]  
          }  
        );
        
/**  下面是成功时返回的提示:
    Successfully added user: {
        "user" : "admin",
        "roles" : [
            {
                "role" : "userAdminAnyDatabase",
                "db" : "admin"
            }
        ]
    };
**/
    db.auth("admin","admin")   // 认证测试,若 1 则成功
    
//-----------------------------------------------------------------------------

// 如开启认证:
    use admin  // 进入管理库
    db.auth("username","pwd")  // 认证登录

// 或直接认证连接: 本地方式
mongo --username admin --password --authenticationDatabase admin 
mongo -u admin -p --authenticationDatabase admin 
// 远程方式 
mongo "mongodb://admin:pwd@localhost:27017/?authSource=admin" 

// 更多连接实例 // 参考: https://www.mongodb.org.cn/tutorial/7.html //连接本地数据库服务器,端口是默认的。 mongodb://localhost //使用用户名fred,密码foobar登录localhost的admin数据库。 mongodb://fred:foobar@localhost //使用用户名fred,密码foobar登录localhost的baz数据库。 mongodb://fred:foobar@localhost/baz //连接 replica pair, 服务器1为example1.com服务器2为example2。 mongodb://example1.com:27017,example2.com:27017 //连接 replica set 三台服务器 (端口 27017, 27018, 和27019): mongodb://localhost,localhost:27018,localhost:27019 //连接 replica set 三台服务器, 写入操作应用在主服务器 并且分布查询到从服务器。 mongodb://host1,host2,host3/?slaveOk=true //直接连接第一个服务器,无论是replica set一部分或者主服务器或者从服务器。 mongodb://host1,host2,host3/?connect=direct;slaveOk=true //当你的连接服务器有优先级,还需要列出所有服务器,你可以使用上述连接方式。 //安全模式连接到localhost: mongodb://localhost/?safe=true //以安全模式连接到replica set,并且等待至少两个复制服务器成功写入,超时时间设置为2秒。 mongodb://host1,host2,host3/?safe=true;w=2;wtimeoutMS=2000
// Shell 常用操作 db show dbs show collections use local show users // 查看当前库中用户 db.test.insert({'a':'333323'}) // 插入数据时会自动创建库和Collections db.test.find() // 显示所有数据 exit // 退出mongo shell // 最后用Robo 3T 测试连接

 

/*************** 如果需要重置密码: ***************/
//  1. 停止服务 mongod --shutdown --config=/etc/mongod.conf
//  2. 修改conf文件,关掉security和authorization
//  3. 启动服务 mongod --config /etc/mongod.conf
//  4. mongo 登录,use admin , 
      show users // 显示本库用户
      db.system.users.find() // 查看全部用户信息。
      db.dropUser(用户名)  // 删除指定的用户
//  5. 使用上面的 db.createUser 添加用户:
    use admin
        db.createUser(  
          {  
            user: "admin",  
            pwd: "admin",  
            roles: [ { role: "root", db: "admin" } ]  
          }  
        );

    // 或者修改用户:
    db.updateUser('admin',
          {
            pwd: "admin",  
            roles: [{ role: "root", db: "admin" } ]  
          }  
        )
//  6. 重新开启配置文件的安全选项,并重启服务 

 

/*********** Mongodb内置的用户角色
1. 数据库用户角色:read、readWrite
2. 数据库管理角色:dbAdmin、dbOwner、userAdmin
3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager
4. 备份恢复角色:backup、restore
5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
6. 超级用户角色: root (最大权限,可访问所有库)
间接或直接提供了系统超级用户访问的角色:dbOwner、userAdmin、userAdminAnyDatabase
7. 内部角色:__system

 

#  创建一个 __system 用户,可以访问 所有元数据

db.createUser({user:"sa", pwd:"123456", roles:[{role:"__system", db:"admin"}]})  

获得数据库的所有用户权限信息:db.getUsers()

获得某个用户的权限信息:db.getUser()
创建角色: db.createRole()
更新角色:db.updateRole()
删除角色:db.dropRole()
获得某个角色信息:db.getRole()
删除用户:db.dropUser()
删除所有用户:db.dropAllUsers()
将一个角色赋予给用户:db.grantRolesToUser()
撤销某个用户的某个角色权限:db.revokeRolesFromUser()
更改密码:db.changeUserPassword()

***********/

    // 例1:给test库添加用户,只能访问test   即 --authenticationDatabase test
    use test
        db.createUser(  
          {  
            user: "test1",  
            pwd: "test1",  
            roles: [ { role: "readWrite", db: "test" } ]  
          }  
        );
        
    // 例2:修改user1权限  --authenticationDatabase demo
    use demo
    db.updateUser('user1',{
        roles: [{role:"dbOwner", db:"demo"}]
    })
    
    // 例3:修改test用户权限,读写所有库
    use admin
    db.updateUser('test',{
        roles: [{role:"readWriteAnyDatabase", db:"admin"}]
    })

/************ 其它命令 ******
    创建 capped collection 上限集合:
       size:大小KB,如果size字段小于或等于4096,则集合的上限为4096字节
       max: 最大文档数。size参数总是必需的,即使指定max的文件数量。
            如果集合在达到最大文档计数之前达到最大大小限制,MongoDB将删除旧文档。 
    参考官方文档 或 https://www.jianshu.com/p/86942c09094a    
************/
db.createCollection("sub",{capped:true,size:100000,max:5})

db.sub.isCapped() // 检查是否 capped

// 转换为 capped 集合
db.runCommand({"convertToCapped": "test", size: 100000, max:10 });

 

/**************
*** CURD ***
**************/

//----------------------------------- 增 ---------------------------------------
// Mongodb 数据库名称不区分大小写
// 数据库不必创建,直接 use databasename 即可
// 集合名称可以db.createCollection创建,
// 一般集合也可不创建,直接在插入数据时自动生成。
// 集合名称只能字母或下划线开头,不能包含 . $ 和空字符,不能 system. 开头
// 更多限制,参考官文:https://docs.mongodb.com/manual/reference/limits/
// 插入多条 db.users.insert([ {name:'Allan',age:32,gender:1}, {name:'Bob',age:25,gender:1}, {name:'Celine',age:32,gender:2}, {name:'Dennis',age:12,gender:1}, ]) // 以下两种插入方法是为了语义清晰 // db.test.insertOne() // db.test.insertMany() // ================================== 查 ======================================= db.test.find({}) // 空对象或空,查询所有的 db.test.find({name:"Jerry"}) // 返回数组 db.test.findOne({name:"Jerry"}) // 返回对象 db.test.find({}).count() // 数量 或 db.test.count() db.users.count({age:{$gt:25}}) // 条件数量 db.test.find({age:{$gt:25}}) // 大于 或者 $gte 即 >= db.test.find({create_ts:{$gt:ISODate("2019-09-19T00:00:00.000Z")}}).pretty() //大于某时间
db.test.find({age:{$eq:25}}) // 等于 或者 $ne 不等于 db.test.find({age:{$lt:25}}) // 小于 或者 $lte 即 <= db.test.find({age:{$gt:20, $lt:30}}) // 大于且小于 db.demoes.find({$or:[{num:{$lt:10}},{num:{$gt:1990}}]}) // 或 $or:[{},{},] db.users.find({age:{$in:[25,32]}}) // 在in 或 不在nin db.users.find({name:/^C/}) // 正则,以C开头 db.users.find({name:{$regex:"^A"}}) // 正则, 以A开头 db.users.find({name:{$regex:/EV/, $options:'mi'}}) // 正则,包含EV的,选项m 包含换行符,i不区分大小写 db.users.find({nickname:{$exists: false}})  // 找出不存在昵称字段的 (true 存在)
db.users.find({hobby:{$elemMatch:{$eq: "football"}}}) // 找出爱好中包含足球的。查找数组中包含某个。
// 虽然可用JS语法,但是效率低 db.users.find({$where:function(){return this.age>20}}) db.users.find({$where:function(){return this.name.startsWith("C")}}) db.test.find().limit(10) // 前10条 db.test.find().skip(10).limit(10) // 跳过10条
// 官方文档也提到 skip() 由于是从头开始数,所以,当数据量变大时,会变得非常慢。
// 我测试的时候,大于5万条数据,已经很慢了。 所以,尽量使用 条件查询,避免使用 skip()
// skip( (页码 -1) * 每页条数).limit(每页条数) // limit()与skip()或sort() 前后无关,mongodb自动调整 db.test.find().sort({age:-1}) // 按年龄降序 // 1取0不要,只有_id需要特别指定为0 db.test.find({},{name:1, _id:0, age:1}) // 投影:只要name和age,不要_id db.users.distinct("gender", {age:{$lt:20}}) // 年龄小于20的性别 // --------------------------------- 改 ---------------------------------------- // db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{age:23}) // 小心,新对象替换旧对象 // 应该使用修改符来操作 db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{$set:{name:"老沙", gender:"",address:"流沙河"}}) db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{$set:{age:88}}) db.test.update({_id:ObjectId("5cede5d81f3b03073319abd0")},{$unset:{address:0}}) // 删除某字段 只删除第一个匹配到的
// $unset 删除字段时,字段写什么值并不影响结果。

  db.test.update({},{$unset:{"Profiles":""}},{multi:true})      // 删除了所有的 Profiles 字段
  // db.test.update({},{$unset:{"Profiles":""}})
  db.test.update({},{$unset:{"Qr":"SOCKET_EMPTY"}},{multi:true})  // 删除所有的 Qr 字段

db.test.update({name:"AAAA"},{$set:{address:"test"}})       // 默认只改一条
db.test.update({name:"AAAA"},{$set:{address:"999"}},{multi:true})  // 使用multi改所有的

db.test.update({dept:'HR'},{$addToSet:{Groups: "reader"}},{multi:true}); // 给数组字段添加内容。不重复值。使用 push 的话,允许重复。
db.test.updateMany({name:"AAAA"},{$set:{address:"105"}}) // 改所有符合的 db.demo.updateMany({created_at:{$exists:false}},{$set:{created_at:'2019-07-08 16:20:00'}}) //不存在某字段时,加上
db.test.find().forEach(function(item){
  db.test.update({"_id":item._id},
  {"$set": {"client_ts":item.create_ts}},false,true)
})   // 用某个字段值 更新另一个字段

// 修改某个json字段的值
db.aaa.update({"Header.Tz":{"$exists":true}},{"$set":{"Header.Tz":"+0800"}},{multi:true});

// 修改Data数组中包含 L 项的键名 db.bbb.find({"Data.L":{"$exists":true}}).forEach(function(item) {
for(i = 0; i != item.Data.length; ++i) {
item.Data[i].Lb= item.Data[i].L;
delete item.Data[i].L;
}
db.bbb.update({_id:item._id},item);
});

/*
********************************* 删 ****************************************/

db.test.remove({address:
"999"}) // 默认删除全部匹配的行
db.test.remove({create_time:{$lt: ISODate("2020-05-01T00:00:00Z")}})  // 删除某个日期前的全部数据
db.test.update({Section:'TE'},{$pull:{Groups: "Test"}},{multi:true});  // remove a item from array.
db.test.remove({address:"999"}, true)    // 只删除符合条件的一行
db.test.remove({},true)    // 删除第一行
db.test.remove({})         // 清空集合 

db.
test.find({client_ts:{$lt:ISODate("2021-07-13T00:00:00.094Z")}}).forEach(function(item){ db.demo.remove({"RootId":item._id}) }) // 先删除关联表的内容, 再删除 test表的内容。 db.test.remove({client_ts:{$lt:ISODate("2021-07-13T00:00:00.094Z")}});

db.test.drop()     // 删除集合
db.dropDatabase()  // 删当前库(赶紧跑路)

// 通常上面的都不会在生产环境中执行, 而使用字段isDel代替
db.test.updateOne({name:"AAAA"},{$set:{isDel:1}})  
db.test.find({isDel:0})

 

// $type 操作符 https://www.mongodb.org.cn/tutorial/15.html
db.test.find({"title" : {$type : 2}})  // 条件:字段为string类型

 

 // -----------#-----------#----------- 练习 ------#-----------#-----------#------- 

// 属性值也可以是个文档 即 内嵌文档
db.goods.update({name:"CPU"},{$set:{band:{Intel:["I3","I5","I7"],AMD:["R5","R7"]}}})
db.goods.find({'band.AMD':'R5'})   // 查询内嵌时必须引号, 包含
db.goods.find({'band.AMD':'R5'},{'band.AMD.$':1})   // 仅取出AMD.R5的列表元素,不要其它的元素。

db.goods.update({name:"Mem"},{$push:{'band.KST':'3333'}})     // 插入一个,可重复
db.goods.update({name:"Mem"},{$addToSet:{'band.KST':'3333'}}) // 添加到集合,不重复
db.goods.update({name:"Mem"},{$pop:{'band.KST':1}})           // 移除最后一个元素

//# 插入20000条数据
 // 1. 笨方法:
    for(var i=1; i<20001;i++){
           db.demos.insert({num:i})
    }
    
 // 2. 好方法:
    var arr=[];
    for(var i=1; i<20001;i++){
       arr.push({num:i});
    }
    db.demoes.insert(arr)

 

 文档间关系

//# 1. 一对一:通过内嵌文档的形式.
    db.married.insert([
        {name:"Tom",guy:{name:"Jerry"}},
        {name:"大郎",guy:{name:"金莲"}}
    ])

//# 2. 一对多, 多对一:
    db.users.insert([
        {username:"swk"},
        {username:"zbj"}
    ])
    
    db.order.insert([
        {list:["CPU","Mem","Disk"],
         user_id: ObjectId("5cef98a9e4a985f4ad3e897c")
        },
        {list:["Apple","Pear","Cherry"],
         user_id: ObjectId("5cef98a9e4a985f4ad3e897c")
        }
    ])
    
    db.order.insert(
        {list:["Pig","Donkey","Sheep"],
         user_id: ObjectId("5cef98a9e4a985f4ad3e897d")
    })
    
    // 查询
    var user_id=db.users.findOne({username:"swk"})._id;
    db.order.find({user_id:user_id})

//# 3. 多对多:
    db.teacher.insert([
        {name:"如来"},
        {name:"观音"},
        {name:"菩提"}
    ])
    
    db.students.insert([
        {"name" : "Tom",
        "teacher_ids" : [ 
            ObjectId("5cefbdffe4a985f4ad3e8984"), 
            ObjectId("5cefbdffe4a985f4ad3e8982")
        ]},
        {"name" : "Jerry",
        "teacher_ids" : [ 
            ObjectId("5cefbdffe4a985f4ad3e8984"), 
            ObjectId("5cefbdffe4a985f4ad3e8983")
        ]}
    ])
    
    // 查询: 如来的徒弟
    var tid = db.teacher.find({name:"如来"})[0]._id     //
    var tid = db.teacher.findOne({name:"如来"})._id 
    db.students.find({teacher_ids:tid})

//-----------------#----------------- 练习 ------------------#------------------- 
// 1. 找出财务部员工
var deptno = db.dept.findOne({dname:"财务部"}).deptno 
db.emp.find({deptno:deptno})

// 2. 低于1000的加400
db.emp.updateMany({sal:{$lt:1000}},{$inc:{sal:400}})
db.emp.updateMany({deptno:2},{$inc:{sal:500}})   // IT部加工资

// 3. 价高于300的减100
db.goods.updateMany({price:{$gt:300}},{$inc:{price:-100}})

 

聚合,统计

// aggregate 用于聚合统计,$group 类似于SQL的sum(), avg() 

// 各求男女人数
db.users.aggregate([{$group:{_id:'$gender',counter:{$sum:1}}}])

db.users.aggregate([{$group:{_id:'$gender',counter:{$sum:'$age'}}}]) // 年龄合计
db.users.aggregate([{$group:{_id:'$gender',counter:{$avg:'$age'}}}]) // 平均年龄
db.users.aggregate([{$group:{_id:'$gender',counter:{$max:'$age'}}}]) // 或 min
db.users.aggregate([{$group:{_id:'$gender',counter:{$first:'$age'}}}]) // 或 last

// 列出各年龄到数组,不去重
db.users.aggregate([{$group:{_id:'$gender',counter:{$push:'$age'}}}])

// 男女分组,列出name到数组
db.users.aggregate([{$group:{_id:'$gender',counter:{$push:'$name'}}}])

// 男女分组,基于整个文档列出所有字段到数组
db.users.aggregate([{$group:{_id:'$gender',counter:{$push:'$$ROOT'}}}])


//##  $match
db.users.aggregate([{$match:{age:{$gt:25}}}])    // 筛选

// 将筛选后的结果再聚合
db.users.aggregate([
    {$match:{age:{$gt:25}}},
    {$group:{_id:'$gender',counter:{$sum:1}}}
])                                       

// 筛选,聚合,投影
db.users.aggregate([
    {$match:{age:{$gt:25}}},
    {$group:{_id:'$gender',counter:{$push:'$name'}}},
    {$project:{_id:0,counter:1}}
])

// 排序,和find()中的几乎一样 {$sort:{_id: -1}} 也可以用在多个地方
db.users.aggregate([
    {$match:{age:{$gt:25}}},
    {$sort:{name: 1}}, 
    {$group:{_id:'$gender',counter:{$push:'$name'}}},
    {$sort:{_id:-1}}
])    // 内按name升序,外按_id降序

// 跳过 {$skip:10},限制{$limit:5} 此处区分顺序

/**  
* {$unwind:"$counter"} 将数组字段拆开成单条 
*/
db.students.aggregate([
    {$match:{name:"WuKong"}},
    {$unwind:"$teacher_ids"}
])

db.t3.insert([
    {"_id":1,"item":"a","size":["S","M","L"]},
    {"_id":2,"item":"b","size":[]},
    {"_id":3,"item":"c","size":"M"},
    {"_id":4,"item":"d"},
    {"_id":5,"item":"e","size":null},
])

db.t3.aggregate([{$unwind:{path:"$size"}}])  // 取出值 不含空和null以及无字段

// 取出值,不丢任何数据
db.t3.aggregate([
    {$unwind:{path:"$size", preserveNullAndEmptyArrays:true}}
])


// 示例1:找出几个字段组合的重复数据
db.products.aggregate([
{
    '$group': {
        '_id': {
            'Product': '$Product',
            'Machine': '$Machine',
            'Station': '$Station',
            'Status': '$Status',
            'Price': '$Price'
        },
        'uniqueIds': {
            '$addToSet': '$_id'
        },
        'count': {
            '$sum': 1
        }
    }
},
{
    '$match': {
        'count': {
            '$gt': 1
        }
    }
}], 
{
    allowDiskUse: true
});

// 示例2: 排除某个状态的重复数据查找

db.members.aggregate([
{'$match': {'Status': {'$ne': '-1'}}},
{
        '$group': {
            '_id': {
            'Station': '$Station',
            'Name': '$Name'            
            },
            'uniqueIds': {
                '$addToSet': '$_id'
            },
            'count': {
                '$sum': 1
            }
        }
    },
    {
        '$match': {
            'count': {
                '$gt': 1
            }
        }
    }
], {
    allowDiskUse: true
    });

// 示例3: 某日期之前,产品按名称统计。
db.cms.aggregate([
{'$match':{'create_ts':{'$lt':ISODate('2020-06-01T06:30:29.655Z')}}},
{$group:{_id:'$ProductName',counter:{$sum:1}}}])

 

 

索引

/********************************** 索引 **************************************/
注意,索引区分大小写,创建索引时,一定要准确的字段名。否则不生效。
不论是普通索引,还是hash索引,都会带来性能提升
只有日期时间类型可以设置TTL索引,单位为秒
db.t1.ensureIndex({name: 1, background:true
}) // 添加索引
db.t2.ensureIndex({cardno:1},{name:'idx_cardno'})       // 创建并指定索引名称 db.t1.ensureIndex({name:1, background:true}, {unique:true}) // 多参数 db.t1.ensureIndex({name:1,age:1, background:true}) // 联合索引 db.t1.getIndexes() // 查询索引 db.t1.dropIndex("name_1") // 删除指定索引 db.t1.dropIndexes() // 删除全部索引,保留默认 _id_ 索引
// 重要:在生产环境中添加索引,一定要使用 background 参数。原因是添加索引时会锁库,影响正常使用。尤其在数据量大时,添加索引时间很长。
// 测试数据 let arr=[]; for(let i=0;i<50000;i++){ arr.push({name:"test"+i, age:i}); } db.t1.insert(arr) // 性能评估 其中结果 executionTimeMillis 为毫秒 db.t1.find({name:'test9999'}).explain('executionStats') db.t1.ensureIndex({name:1}) // 添加索引 // 再次执行 结果 executionTimeMillis 降低很多 db.t1.find({name:'test9999'}).explain('executionStats')

// 删除数据时,索引不会删除,并且体积可能变得更大,必要时可以删除索引,重建索引。

 

/*************************** 复制集 ******************************/

 // 官文 https://docs.mongodb.com/manual/replication/

主备

// 例1:单机演示。(生产环境不适用)使用不同端口,不同数据目录,必须设置相同 replSet
    mongod --bind_ip 192.168.112.9 --port 27018 --dbpath /data/t1 --replSet rs0
    mongod --bind_ip 192.168.112.9 --port 27019 --dbpath /data/t2 --replSet rs0
    // 服务运行窗口可以看到后续操作的日志显示

    // 然后客户端分别连接服务
    mongo --host 192.168.112.9 --port 27018    
    rs.initiate()   // 初始化为master 提示符发生变化 
    rs.status()     // 查看复制集状态  rs0:PRIMARY>
    rs.isMaster()   // 是否主节点
    rs.add("192.168.112.9:27019")  // 添加slave

    mongo --host 192.168.112.9 --port 27019
    rs.slaveOk()    // slave上执行同意
    
// 例2:不同机器(接近生产环境)。必须设置相同 replSet
    // 注意OS版本,rs1:PRIMARY 可低版本,rs1:SECONDARY可高版本。反之则rs.add出错
    // 本例PRIMARY为112.6(centos6.5), SECONDARY为112.9(centos7.5)
    mongod --bind_ip 192.168.112.6 --port 27018 --dbpath /data/t3 --replSet rs1
    mongod --bind_ip 192.168.112.9 --port 27018 --dbpath /data/t3 --replSet rs1

    // 使用客户端分别连接服务
    mongo --host 192.168.112.6 --port 27018
    mongo --host 192.168.112.9 --port 27018

    rs.initiate()   // 112.6 为 master 
    rs.status()     // 查看复制集状态  rs1:PRIMARY>
    rs.add("192.168.112.9:27018")  // 添加slave

    rs.slaveOk()    // 112.9 上执行同意

// 测试数据 master
use demo1
db.users.insert({name:"Allen",age:28})
db.users.insert({name:"Bob",age:38})

// slave上查询
use demo1
db.users.find()  // 得到相同结果

// 发现master当机后,某slave则自动变为PRIMARY 
// 而修好后的master启用,加入到rs1,转为SECONDARY 

/////////////////////// 添加一个节点(192.168.112.7:27017) // 新节点 安装,添加目录,然后执行: mongod --bind_ip 192.168.112.7 --port 27017 --dbpath /data/t3 --replSet rs1 // 然后 rs1:PRIMARY> 上执行: rs.add("192.168.112.7:27017") // 添加slave节点 // slave 新窗口上执行: mongo --host 192.168.112.7 --port 27017 > rs.slaveOk() // 即可自动更新所有数据 /////////////////////// 删除一个节点 rs1:PRIMARY> rs.remove("192.168.112.6:27018") // 被删节点将不可用 /////////////////////// 停止节点服务 mongod --bind_ip 192.168.112.7 --port 27017 --dbpath /data/t3 --shutdown // 其它命令:
rs.conf() // 显示复制集信息
rs.printReplicationInfo() // 从primary角度显示复制集信息,显示 oplog 信息
rs.printSlaveReplicationInfo() // 从secondary角度显示复制集信息
db.serverStatus().asserts // 查看断言
rs.stepDown() // 强制primary降为secondary,并引发新的选举. 慎用

#故障案例:某个 slave 节点自动down掉,状态为 recovering 再次启动时过一会还是down掉,检查日志后发现:
[replication-0] We are too stale to use 10.1.1.34:27017 as a sync source. Blacklisting this sync source because our last fetched timestamp: 
Timestamp(1584041941, 30) is before their earliest timestamp: Timestamp(1584399943, 455) for 1min until:

解决办法:将此 slave 停止服务, 改名或删除 mongodb 仅数据目录后,重新启动 mongodb, 如果状态显示为 startup2 则表示成功开始同步数据。等待同步完成即可恢复正常。

 

最近试用了4.4.5版本,直接用配置文件的方式搭了个复制集: 

#官网下载 tgz 包,https://www.mongodb.com/try/download/community
# 选 centos7.0, 选tgz 下载

cd /data/
tar zxf mongodb-linux-x86_64-rhel70-4.4.5.tgz
mv mongodb-linux-x86_64-rhel70-4.4.5 mongo4.4.5

cd mongo4.4.5/
# 新建目录用来存放配置文件,日志,数据
mkdir -p conf logs data
cd data/
# 新建3个子目录用来存放3个节点的数据
mkdir -p mongo0 mongo1 mongo2 

在 conf目录下添加3个不同的配置文件,mongod0.conf   mongod1.conf   mongod2.conf 并将内容中的文件名和路径改成各自对应的。以及不同的端口。

systemLog:
    path: /data/mongo4.4.5/logs/mongod0.log
    logAppend: true
    logRotate: rename
    destination: file

operationProfiling:
  mode: slowOp
  slowOpThresholdMs: 300
  slowOpSampleRate: 1.0

storage:
    dbPath: /data/mongo4.4.5/data/mongo0
    journal:
        enabled: true
    directoryPerDB: true
    engine: wiredTiger
    wiredTiger:
        engineConfig:
            cacheSizeGB: 2
            directoryForIndexes: true

processManagement:
    fork: true
    pidFilePath: /data/mongo4.4.5/data/mongod0.pid

net:
    port: 27000
    bindIpAll: true
    maxIncomingConnections: 5120


replication:
    oplogSizeMB: 1024
    replSetName: testRS

然后,依次按不同的配置文件启动   /data/mongo4.4.5/bin/mongod -f /data/mongo4.4.5/conf/mongod0.conf

三个都启动成功后,会看到各自不同端口的服务。然后初始化:

/data/mongo4.4.5/bin/mongo localhost:27000

> rs.initiate({_id:'testRS',members:[{_id:0,host:'10.170.6.17:27000'},{_id:1,host:'10.170.6.17:27001'},{_id:2,host:'10.170.6.17:27002'}]})

> rs.status()

> rs.conf()

好了,可以用了。

 

 

备份与恢复

// mongodump -h dbhost -d dbname -o outdir  
// 备份将在输出目录中生成Collection对应的 bson 和 metadata.json 文件
mongodump -u admin -p admin --authenticationDatabase admin -h 192.168.112.9 -d test -o /bak/mongodb

// 恢复 mongorestore -h dbhost -d dbname --dir inputdir
mongorestore -u admin -p admin --authenticationDatabase admin -h localhost -d test --dir /bak/mongodb/test/

 

Python pymongo

// driver for python 
pip3 install pymongo

/*************************************** demo.py ******************************/

#!/usr/bin/env python
# coding:utf-8

from pymongo import *

client = MongoClient('mongodb://test1:test1@192.168.112.9:27017/test')

db = client.test  #
users = db.users  # Collections

# res = users.insert({'name':'python','age':88,'gender':'female'})      # 插入
# print(res)  # 返回 id
# ret = users.insert_one({'name':'pip3','age':19,'gender':'male'})      # 插入
# print(ret)  # 返回 object

# ret2 = users.update_one({'name':'python'},{'$set':{'name':'Python3'}}) # 修改
# print(ret2)

# ret3 = users.delete_one({'name':'pip3'})    # 删除
# print(ret3)

# cursor = users.find_one({'name':'Python3'})   # 只取一条
# print(type(cursor), cursor)
# for k, v in cursor.items():
#     print(k + " --> " + str(v))

# cursor = users.find({'age':{'$gt':30}})       # 取多条
# for s in cursor:
#     print(s)
#     # print(s['name'], s['age'])

# cursor = users.find().sort([('gender',1),('age',-1)])   # 多条件降序
# for s in cursor:
#     print(s)

cursor = users.find().sort('age',-1).limit(2).skip(3)    # 子集
for s in cursor:
    print(s)

 

Node.js  mongoose

因为npm 库中的 mongodb没有这个好用,所以直接使用 mongoose 

/**
    1. 安装
        npm i mongoose --save

    2. 引入
        let mongoose = require("mongoose");

    3. 连接数据库
        mongoose.connect('mongodb://root:123456@192.168.112.9/odm_demo?authSource=admin',{useNewUrlParser:true});
        默认27017可以省略
        通常只需连接一次,除非项目停止或服务器关闭,否则连接一般不会断开。

    4. 断开连接(一般不需要)
        mongoose.disconnect();
        一般情况下,只需要连接一次; 除非项目停止服务器关闭,否则连接一般不会断开

    - 监听Mongodb连接状态
            - 在mongoose对象中,有一个属性叫做connection 表示的就是数据库连接
              通过监视该对象的状态,可以来监听数据库的连接与断开

            - 连接成功事件
              mongoose.connection.once("open",function(){});

            - 数据库断开事件
              mongoose.connection.once("close",function(){});
*/

const mongoose = require('mongoose');


mongoose.connect('mongodb://admin:admin@192.168.112.9/admin', {useNewUrlParser: true}); // 正确写法

mongoose.connection.once('open',function () {
    console.log("连接成功.");
});

mongoose.connection.once('close',function () {
    console.log('数据库已断开.');
});

mongoose.disconnect(); // 一般不会调用

mongoose demo

const mongoose = require('mongoose');

mongoose.connect('mongodb://root:123456@192.168.112.9/odm_demo?authSource=odm_demo',{useNewUrlParser:true});
mongoose.connection.once('open',function (err) {
    if(err){
        console.log(err);
    }else{
        console.log("连接成功~~~");
    }
});

/**
 *  Schema
 *  Model
 *  Document
*/

// 1. 定义 Schema
let Schema = mongoose.Schema;

// 2. 创建模式(约束) Schema 对象
let stuSchema = new Schema({
    name: String,
    age: Number,
    gender:{
        type: String,
        default:"female"
    },
    address:String
});

// 3. 通过Schema创建 Model (Collection),通过Model才能操作数据库
let StuModel = mongoose.model("child", stuSchema); // 集合名自动变复数。
let StuModel = mongoose.model("child", stuSchema,"child"); // 但是,如果第3个参数也传集合名称,则不会变复数。
 
// 4. 插入文档 Model.create(doc,function (err) {}); StuModel.create({ name:"紫霞仙子", age:18, // gender:"male", address:"水帘洞" },function (err) { if(!err){ console.log("插入成功."); } });

 

 

模型用法示例

require("./common/conn_mongo");
let StuModel = require('./model/users');

// 插入 Model.create(doc(s),[callback])
/* StuModel.create([
    {
        name:"沙和尚",
        age:42,
        gender:"male",
        address:"流沙河"
    }
],function (err) {
    if(!err){
        // console.log('插入成功~~~');
        console.log(arguments);  // 返回插入的内容
    }
});

StuModel.create(
    [{
        name:'白骨精',
        age:90,
        address:'山洞'
    },{
        name:'女王',
        age:38,
        address:'女儿国'
    }], function (err) {
        if(!err){
            console.log('插入成功.')
        }
    }
);   */


/** 查询:
 *  Model.find(conditions,[projection],[options],[callback])
 *     查询所有符合条件的文档 返回数组
 *  Model.findById(id,[projection],[options],[callback])
 *     根据ID属性查询文档
 *  Model.findOne([conditions],[projection],[options],[callback])
 *     查询符合条件的第一个文档
 *
 *  Model.count(conditions, [callback])  // 数量
 *
 *    - projection 投影 想要的字段
 *         两种方式: {name:1, _id:0}
 *                   "name -_id"
 *    - options  选项(skip, limit )
 *          {skip:2, limit:1}
 *    - callback  回调函数 必传, 不传则不会查询
 */

// StuModel.count({},function (err, count) {  // count 是旧方法
StuModel.countDocuments({},function (err, count) { // 新方法
    if(!err){
        console.log(count);
    }
});


StuModel.find({name:"八戒"},function (err,docs) {  // 返回数组
    if(!err){
        if(docs.length > 0){
            console.log(docs)
            // console.log(docs[0].name)
        }else{
            console.log('docs is empty')
        }
    }
});

StuModel.findOne({name:"唐僧"},function (err, doc) {  // 返回文档对象, 就是Document
    if(!err){
        if(doc){
            // console.log(doc)
            console.log(doc.address);
            console.log(doc instanceof StuModel)  // Document对象是Model的实例
        }else{
            console.log('no result')
        }
    }
});

StuModel.findById('5d108a9fe15d35242c37666c',function (err, doc) {
    if(!err){
        console.log(doc.name);
    }
});


// // 投影 只取name age 不要 _id
// StuModel.find({},{name:1, age:1, _id:0},function (err, docs) { // 对象参数
StuModel.find({},'name age -_id', {skip:1,limit:1}, function (err, docs) {   // 字符串参数
    if(!err){
        if(docs.length > 0){
            console.log(docs)
        }
    }
});



/** 修改
 *  Model.update(conditions, doc, [options], [callback])
 *  Model.updateMany(conditions, doc, [options], [callback])
 *  Model.updateOne(conditions, doc, [options], [callback])
 *  Model.replaceOne(conditions, doc, [options], [callback])
 *     - conditions 查询条件
 *     - doc 修改后的对象
 */

StuModel.updateOne({name:"唐僧"},{$set:{age:77}},function (err) {
    if(!err){
        console.log('修改成功.');
    }
});


/** 删除  一般不用
 *  Model.remove(conditions, [callback])
 *  Model.deleteOne(conditions, [callback])
 *  Model.deleteMany(conditions, [callback])
 */

// StuModel.deleteOne({name:'白骨精'},function (err) {
//     if(!err){
//         console.log('删除成功.')
//     }
// });

common/conn_mongo.js

let mongoose = require("mongoose");

mongoose.connect('mongodb://admin:admin@192.168.112.9/odm_demo?authSource=admin',{useNewUrlParser:true});
mongoose.connection.once('open',function (err) {
    if(err){
        console.log(err);
    }else{
        console.log("连接成功~~~");
    }
});

model/users.js

/**
  定义模型
 */

const mongoose = require('mongoose');
let Schema = mongoose.Schema;

let stuSchema = new Schema({
    name: String,
    age: Number,
    gender:{
        type: String,
        default:"female"
    },
    address:String
});

// 有了model 即可CURD
let UsrModel = mongoose.model("users", stuSchema); // 如果是复数,则名称不变
module.exports = UsrModel;

 

 

文档用法示例:

require('./common/conn_mongo');
let monster = require('./model/monster');

/**
 *  Document 和 集合中的文档一一对应,Document是Model的实例
 *  通过 Model 查询到的结果都是 Document
 */
let stu = new monster({
    name:"奔波儿霸",
    age:888,
    gender:"male",
    address:"碧波潭"
});

// console.log(stu);


// Document对象的方法  Model#save([options],[fn])
// 而 create 是 Model 的方法
// stu.save(function (err) {
//     if(!err){
//         console.log("写入成功~")
//     }
// });


monster.findOne({},function (err, doc) {
    if(!err){
        /**
         *  update(update,[options],[callback]))
         *    - 修改对象
         *  save([callback]))
         *  update([callback]))
         */
        // console.log(doc);
        // doc.update({$set:{age:22}},function (err) {  // 过时的方法
        //     if(!err){
        //         console.log("修改成功.")
        //     }
        // });

        // doc.updateOne({$set:{address:"高老庄"}},function (err) {
        //     if(!err){
        //         console.log("修改成功");
        //     }
        // });

        // // 直接修改
        // doc.age = 2000;
        // doc.save();

        // 删除
        // doc.remove(function (err) {
        //     if(!err){
        //         console.log("师傅再见~")
        //     }
        // })

        /**
         * get(name)
         * set(name, value)
         * id 获取文档的 _id 属性
         * toJSON()
         * toObject()  document对象方法则失效
         */
        console.log(doc.get("name"));
        console.log(doc.address);

        // doc.set("name","猪悟能");  // 需要 save() 才能修改数据库
        // doc.name = "奔波霸";
        // doc.save();
        console.log(doc.id);

        console.log(typeof doc); // Object
        let t = doc.toObject();  // 转为普通 JS 对象
        delete t.__v;
        delete t._id;
        console.log(t);
    }
});

 model/monster.js

const mongoose = require('mongoose');
let Schema = mongoose.Schema;
let stuSchema = new Schema({
    name: String,
    age: Number,
    gender:{
        type: String,
        default:"female"
    },
    address:String
});

let MonsterModel = mongoose.model("monsters", stuSchema);
module.exports = MonsterModel;


posted @ 2019-06-01 22:25  枫若雪  阅读(910)  评论(0编辑  收藏  举报