1. MongoDB简介

什么是MongoDB

MongoDB是一个基于分布式文件存储的数据库,由C++语言编写。为WEB应用提供可扩展的高性能数据存储解决方案。

是一个介于关系型和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富,最像关系型数据库的,支持的数据结构非常松散,是类似json和bson格式,因此可以存储比较复杂的数据类型。MongoDB最大的特点是支持的查询语言非常强大,其语法有点类似面向对象的查询语言,几乎可以实现雷士关系型数据库单表查询的绝大部分功能,而且还支持对数据建立树形索引。

缺点:没有表连接,只能单表查询。

2. MongoDB 与关系型数据库术语对比

SQL术语/概念MongoDB术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
columnfiled数据字段/域
indexindex索引
table joins 表连接,MongoDB不支持
primary keyprimary key主键,MongoDB自动将_id字段设置为主键

下表列出了 RDBMS 与 MongoDB 对应的术语:

RDBMSMongoDB
数据库数据库
表格集合
文档
字段
表联合嵌入文档
主键主键 (MongoDB 提供了 key 为 _id )
数据库服务和客户端
Mysqld/Oraclemongod
mysql/sqlplusmongo

需要注意的是:

  1. 文档中的键/值对是有序的。
  2. 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  3. MongoDB区分类型和大小写。
  4. MongoDB的文档不能有重复的键。
  5. 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:

  • 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
  • .和$有特别的意义,只有在特定环境下才能使用。
  • 以下划线"_"开头的键是保留的(不是严格要求的)。

3. MongoDB 数据类型

数据类型描述
String字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean布尔值。用于存储布尔值(真/假)。
Double双精度浮点值。用于存储浮点值。
Min/Max keys将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array用于将数组或列表或多个值存储为一个键。
Timestamp时间戳。记录文档修改或添加的具体时间。
Object用于内嵌文档。
Null用于创建空值。
Symbol符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID对象 ID。用于创建文档的 ID。
Binary Data二进制数据。用于存储二进制数据。
Code代码类型。用于在文档中存储 JavaScript 代码。
Regular expression正则表达式类型。用于存储正则表达式。

下面说明下几种重要的数据类型。

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

  • 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
  • 接下来的 3 个字节是机器标识码
  • 紧接的两个字节由进程 id 组成 PID
  • 最后三个字节是随机数

4. Linux 下载安装/运行

  • centos7 下载链接:https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.2.tgz
  • 上传至服务器的路径:/usr/local/
  • 解压:tar -zxf mongodb-linux-x86_64-rhel70-4.4.2.tgz
  • 修改名字:mv mongodb-linux-x86_64-rhel70-4.4.2.tgz mongodb
  • 创建数据存放文件夹:mkdir - p data/db
  • 创建日志存放文件:touch logs/mongodb.log
  • 创建子进程方式、指定数据存放地址、指定日志存放地址运行:bin/mongod --dbpath data/db/ --logpath logs/mongodb.log --logappend --fork

编写配置文件

  • mkdir conf

  • cd conf

  • vi mongodb.conf

    dbpath=/usr/local/mongodb/data/db
    logpath=/usr/local/mongodb/logs/mongodb.log
    logappend=true
    bind_ip_all=true
    port=27017
    fork=true
    
  • 启动测试

    • bin/mongodb -f conf/mongodb.conf

客户端访问

  • 运行客户端:bin/mongod --host --port
  • 不加参数,默认连接本地

函数关闭服务

  • 连接到MongoDB服务后,切换到admin库,并使用关闭命令
  • user admin
  • db.shutdownServer();
  • db.runCommand("shutdown");

5. 用户管理

MongoDB作为当下最热门的数据库,安全校验也是必不可少的,可以通过创建用户的方式来降低风险

常用权限

权限名描述
read允许用户读取指定数据库
readWrite允许用户读写指定数据库
dbAdmin允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin允许用户想system.users集合写入,可以在指定数据库中创建、删除和管理用户
clusterAdmin只在admin数据库中可用赋予用户所有分片和复制集相关函数和管理权限
readAnyDatabase只在admin数据库中可用,赋予用户所有数据的读权限
readWirteAnyDatabase只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase只在admin数据中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限
root只在admin数据库中可用。超级账号,超级权限

创建用户

  • 连接客户端

  • use admin

  • 创建用户

    db.createUser({"user":"root","pwd":"root","roles":[{"role":"root","db":"admin"}],"customData":{"infomation":"first mongodb user"}});
    

查看已创建用户

  • use admin
  • show users
{
        "_id" : "admin.root",
        "userId" : UUID("c7f149f2-2336-4b29-afb8-aa3899978375"),
        "user" : "root",
        "db" : "admin",
        "customData" : {
                "infomation" : "first mongodb user"
        },
        "roles" : [
                {
                        "role" : "root",
                        "db" : "admin"
                }
        ],
        "mechanisms" : [
                "SCRAM-SHA-1",
                "SCRAM-SHA-256"
        ]
}

修改用户信息

  • 修改用户“guest”的密码和权限

    db.updateUser("guest",{"pwd":"123","roles":[{"role":"readWriteAnyDatabase","db":"admin"}]});
    
  • 修改用户“guest”的密码

    db.changeUserPassword("guest","guest");
    

删除用户

  • 需要有admin库的修改权限

  • 当前使用root角色操作

    db.dropUser("guest");
    
    • 返回true为删除
    • 返回false删除失败

6. 数据库操作

  • 查看当前已有数据库

    show dbs
    
  • 切换数据库,use,没有就创建

    use test
    
    • 由于没有向当前库是空的,所以现在使用show dbs命令是看不到的,但是存在内存当中
  • 删除数据库,想删哪个就进到那个库中,不用传参

    db.dropDatabase();
    
    • 在当前库中,不能使用命令删除其他的库

7. Collection 操作

MongoDB 中的集合是彝族文档的集,相当于关系型数据库中的表

创建集合

  • 选择当前数据库

    use test
    
  • 创建空的表

    db.createCollection("ego_users");
    
  • 创建有数据的表

    db.ego_items.insert({"title":"Thinking in java","price":"9900","num":999});
    
  • 创建带有参数设定的表

    db.createCollection("ego_item_desc",{"capped":true, "size":20000, "max":100, "autoIndexId":true});
    
    • capped:指定容量,为true时必须跟上size参数
    • size:长度,会自动转换为2的幂等数,例如:2000,会转换为2048
    • max:指定集合中包含文档的最大数量
    • autoIndexId:自动为id创建索引

查看集合

  • 查看有哪些集合

    show collections  或者 show tables
    
  • 查看集合状态

    db.ego_users.stats();
    
    "ns" : "test.ego_item_desc", # 名字
    "size" : 0,  # 已使用长度
    "count" : 0, # 数据条数
    "storageSize" : 4096, # 存储空间
    "freeStorageSize" : 0,
    "capped" : true, # 有存储限制
    "max" : 100, # 最多存几个
    "maxSize" : 20224, # 最大容量
    "sleepCount" : 0, # 当容量超标时,会进行阻塞,删除旧数据时,会写进来
    "sleepMS" : 0,
    

删除集合

  • 先进入要删除的库中

  • 通过集合名称删除

    db.ego_item_desc.drop();
    

8. document操作

在MongoDB 中,文档是值多个键及其关联的值有序的放置在一起就是文档,其实指的就是数据,也是平时操作最多的部分

MongoDB 中的文档的数据结构和 JSON 基本一样,所有存储在集合中的数据都是

插入数据

  • 添加单条数据,两种方式

    db.ego_users.insert({"name":"zhangsan","pswd":"123","ago":20});
    
    db.ego_users.save({"name":"lisi","pswd":"123","ago":25});
    
    • insert:一定是新增
    • save:可能是覆盖
  • 3.0之后的新方法

    db.ego_users.insertOne({"name":"wangwu","pswd":"123","ago":21});
    
  • 添加多条数据,数组插入

    db.ego_users.insert([{"name":"lily","pswd":"123","age":18},{"name":"lucy","pswd":"123", "age":18}]);
    
    db.ego_users.save([{"name":"n1","pswd":"p1","age":18},{"name":"n2","pswd":"p2", "age":18}]);
    
    db.ego_users.insertMany([{"name":"n3","pswd":"p3","age":18},{"name":"n4","pswd":"p4", "age":18}]);
    

变量定义和应用

  • 先定义对象,然后再插入

    ego_user=({"name":"var", "pswd":"var", "age":50})
    db.ego_users.insert(ego_user);
    
    ego_user_array=([{"name":"array", "pswd":"array", "age":100}])
    db.ego_users.insert(ego_user_array)
    
  • 查询全部数据

    db.ego_users.find();
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "ago" : 20 }
    { "_id" : ObjectId("5fe05d85385f27d277d9dc5a"), "name" : "lisi", "pswd" : "123", "ago" : 25 }
    { "_id" : ObjectId("5fe05dd8385f27d277d9dc5b"), "name" : "wangwu", "pswd" : "123", "ago" : 21 }
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5c"), "name" : "lily", "pswd" : "123", "age" : 18 }
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5d"), "name" : "lucy", "pswd" : "123", "age" : 18 }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc5f"), "name" : "n1", "pswd" : "p1", "age" : 18 }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc60"), "name" : "n2", "pswd" : "p2", "age" : 18 }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc61"), "name" : "n3", "pswd" : "p3", "age" : 18 }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc62"), "name" : "n4", "pswd" : "p4", "age" : 18 }
    { "_id" : ObjectId("5fe060cb385f27d277d9dc63"), "name" : "var", "pswd" : "var", "age" : 50 }
    { "_id" : ObjectId("5fe06179385f27d277d9dc64"), "name" : "array", "pswd" : "array", "age" : 100 }
    
  • 条件查询

    db.ego_users.find({"name":"zhangsan"})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "ago" : 20 }
    
    db.ego_users.find({"age":18})
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5c"), "name" : "lily", "pswd" : "123", "age" : 18 }
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5d"), "name" : "lucy", "pswd" : "123", "age" : 18 }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc5f"), "name" : "n1", "pswd" : "p1", "age" : 18 }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc60"), "name" : "n2", "pswd" : "p2", "age" : 18 }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc61"), "name" : "n3", "pswd" : "p3", "age" : 18 }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc62"), "name" : "n4", "pswd" : "p4", "age" : 18 }
    
  • 查询年龄小于30

     db.ego_users.find({"age":{"$lt":30}})
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5c"), "name" : "lily", "pswd" : "123", "age" : 18 }
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5d"), "name" : "lucy", "pswd" : "123", "age" : 18 }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc5f"), "name" : "n1", "pswd" : "p1", "age" : 18 }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc60"), "name" : "n2", "pswd" : "p2", "age" : 18 }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc61"), "name" : "n3", "pswd" : "p3", "age" : 18 }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc62"), "name" : "n4", "pswd" : "p4", "age" : 18 }
    
    条件描述
    大于$gt
    大于等于$gte
    小于$lt
    小于等于$lte
    不等于$ne
  • 查自然排序的第一条

    db.ego_users.findOne()
    
  • 投影:查询表格中的部分数据

    • 投影约束:显示字段要么同为1,要么同为0,除了_id
  • 默认为显示,可以设定多个字段不显示,或者只显示多个字段

    db.ego_users.find({}, {"name":0, "_id":1,"age":0}){ "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "pswd" : "123" }
    { "_id" : ObjectId("5fe05d85385f27d277d9dc5a"), "pswd" : "123" }
    { "_id" : ObjectId("5fe05dd8385f27d277d9dc5b"), "pswd" : "123" }
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5c"), "pswd" : "123" }
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5d"), "pswd" : "123" }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc5f"), "pswd" : "p1" }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc60"), "pswd" : "p2" }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc61"), "pswd" : "p3" }
    { "_id" : ObjectId("5fe05fa8385f27d277d9dc62"), "pswd" : "p4" }
    { "_id" : ObjectId("5fe060cb385f27d277d9dc63"), "pswd" : "var" }
    { "_id" : ObjectId("5fe06179385f27d277d9dc64"), "pswd" : "array" }
    
    
  • pretty()函数,find().pretty(),使查出来的数据格式化,findOne()不能使用

    db.ego_users.find({"name":"zhangsan"}).pretty();
    {
            "_id" : ObjectId("5fe05d1d385f27d277d9dc59"),
            "name" : "zhangsan",
            "pswd" : "123",
            "ago" : 20
    }
    
    db.ego_users.findOne().pretty();
    uncaught exception: TypeError: db.ego_users.findOne(...).pretty is not a function :
    
  • 多条件查询(且)

    db.ego_users.find({"name":"zhangsan","age":{"$lt":25}})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    
  • 多条件查询(或)

    db.ego_users.find({"$or":[{"name":"zhangsan"},{"age":{"$gt":25}}]})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    { "_id" : ObjectId("5fe060cb385f27d277d9dc63"), "name" : "var", "pswd" : "var", "age" : 50 }
    { "_id" : ObjectId("5fe06179385f27d277d9dc64"), "name" : "array", "pswd" : "array", "age" : 100 }
    
  • 多条件查询,有 or 有 and

     db.ego_users.find({"$or":[{"name":"zhangsan"},{"age":{"$gt":25}}], "pswd":"123"})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    

类型查询

  • 查询name类型为string的数据

    db.ego_users.find({"name":{"$type":"string"}})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    
  • 查询age类型为double的数据

    • 所有数字默认为double类型
    db.ego_users.find({"age":{"$type":"double"}})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    
  • 数据类型介绍

TypeNumberAliasNotes
Double1“double”
String2“string”
Object3“object”
Array4“array”
Binary data5“binData”
Undefined6“undefined”Deprecated.
ObjectId7“objectId”
Boolean8“bool”
Date9“date”
Null10“null”
Regular Expression11“regex”
DBPointer12“dbPointer”Deprecated.
JavaScript13“javascript”
Symbol14“symbol”Deprecated.
JavaScript (with scope)15“javascriptWithScope”
32-bit integer16“int”
Timestamp17“timestamp”
64-bit integer18“long”
Decimal12819“decimal”New in version 3.4.
Min key-1“minKey”
Max key127“maxKey”

正则表达式查询

  • 查询名字以z开头的

    db.ego_users.find({"name":/^z/})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    
    # 正则表达式完整性
    db.ego_users.find({"name":{$regex:/^z/}})
    
  • 查询名字以y结尾的

    db.ego_users.find({"name":/y$/})
    { "_id" : ObjectId("5fe05ef2385f27d277d9dc5c"), "name" : "lily", "pswd" : "123", "age" : 18 }
    
  • 查询名字以a开头,y结尾的

    db.ego_users.find({"name":/^a.*y$/})
    { "_id" : ObjectId("5fe06179385f27d277d9dc64"), "name" : "array", "pswd" : "array", "age" : 100 }
    
  • 查询名字中包含a的

    db.ego_users.find({"name":/a/})
    { "_id" : ObjectId("5fe05dd8385f27d277d9dc5b"), "name" : "wangwu", "pswd" : "123", "age" : 21 }
    
  • 忽略大小写查询

    db.ego_users.find({"name":/A/i})
    { "_id" : ObjectId("5fe060cb385f27d277d9dc63"), "name" : "var", "pswd" : "var", "age" : 50 }
    
  • 最完整的查询方式,也是最复杂的

     db.ego_users.find({"name":{$regex:/^L/,$options:"i"}})
     { "_id" : ObjectId("5fe05d85385f27d277d9dc5a"), "name" : "lisi", "pswd" : "123", "age" : 25 }
    
  • 查询以 z 或者 l 开头的

    db.ego_users.find({"name":{"$in":[/^z/,/^l/]}})
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    { "_id" : ObjectId("5fe05d85385f27d277d9dc5a"), "name" : "lisi", "pswd" : "123", "age" : 25 }
    
  • 查询不以 z 或者 l 开头的

    db.ego_users.find({"name":{"$nin":[/^z/,/^l/]}})
    { "_id" : ObjectId("5fe05dd8385f27d277d9dc5b"), "name" : "wangwu", "pswd" : "123", "age" : 21 }
    { "_id" : ObjectId("5fe05f6d385f27d277d9dc5f"), "name" : "n1", "pswd" : "p1", "age" : 20 }
    

分页查询

  • limit()和skip()

    # 查询3条数据,跳过0条
    db.ego_users.find().limit(3).skip(0)
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    { "_id" : ObjectId("5fe05d85385f27d277d9dc5a"), "name" : "lisi", "pswd" : "123", "age" : 25 }
    { "_id" : ObjectId("5fe05dd8385f27d277d9dc5b"), "name" : "wangwu", "pswd" : "123", "age" : 21 }
    
  • limit()和 skip()两个是可以分开使用的

    • skip 不传参,代表0
    • limit 不传参,代表查全部

排序

  • sort()

    # 按照年龄降序,姓名升序
    db.ego_users.find({},{"_id":0}).sort({"age":-1, "name":1})
    { "name" : "array", "pswd" : "array", "age" : 100 }
    { "name" : "var", "pswd" : "var", "age" : 50 }
    { "name" : "lisi", "pswd" : "123", "age" : 25 }
    { "name" : "wangwu", "pswd" : "123", "age" : 21 }
    { "name" : "n1", "pswd" : "p1", "age" : 20 }
    { "name" : "n2", "pswd" : "p2", "age" : 20 }
    { "name" : "n3", "pswd" : "p3", "age" : 20 }
    { "name" : "n4", "pswd" : "p4", "age" : 20 }
    { "name" : "zhangsan", "pswd" : "123", "age" : 20 }
    { "name" : "lily", "pswd" : "123", "age" : 18 }
    { "name" : "lucy", "pswd" : "123", "age" : 18 }
    

更新

  • 两种,覆盖,新增

  • 覆盖

     # id为已经存在
     db.ego_users.save({"_id" : ObjectId("5fe06179385f27d277d9dc64"), "name":"saveUpdate", "pswd":"saveUpdate", "age":15});
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    
  • 新增

    # 使用不存在的id作为条件,
    db.ego_users.save({"_id" : ObjectId("5fe06179385f27d277d9dc60"), "name":"saveUpdate", "pswd":"saveUpdate", "age":15});
    WriteResult({
            "nMatched" : 0,
            "nUpserted" : 1,
            "nModified" : 0,
            "_id" : ObjectId("5fe06179385f27d277d9dc60")
    })
    
  • update()匹配式覆盖,匹配一条数据后修改结束

    db.ego_users.update({"name":"saveUpdate"},{"name":"update", "pswd":"update", "age":19})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    
  • 使用update()修改时,需要加入$set指令

     db.ego_users.update({"name":"zhangsan", "age":20},{"$set":{"pswd":"update", "age":19}}
    
    • 否则就会成为覆盖原有数据
    db.ego_users.update({"name":"zhangsan"},{"name":"zhangsan123"})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan123" }
    
    • 如果更新数据中存在新的字段,将会添加新字段
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan123" }
    db.ego_users.update({"name":"zhangsan123"},{"$set":{"name":"zhangsan", "age":19, "pswd":"123"}})
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "age" : 19, "pswd" : "123" }
    

表达式更新

  • $inc,用于数学字段,并不是当前字段自增,而是需要指定增加的数,像加法,加的数可以是负数,类似于减法

    db.ego_users.update({"name":"zhangsan"}, {"$inc":{"age":3}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "age" : 22, "pswd" : "123" }
    
  • $unset,把对应的字段删除,跟字段名,只关注字段名,后面跟的值只是一个占位作用,让整个对象是完整的,没有实质作用

    db.ego_users.update({"name":"zhangsan"}, {"$unset":{"age":0}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123" }
    
  • $push,向数组字段后面追加一元素,不能同时追加多个元素

    db.ego_users.update({"name":"zhangsan"},{"$push":{"courses":"Dubbo"}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Boot", "Spring Cloud", "MongoDB", "Dubbo" ] }
    
    • 不支持同时追加多个元素
    db.ego_users.update({"name":"zhangsan"},{"$push":{"courses":["Linux", "FastDFS"]}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Boot", "Spring Cloud", "MongoDB", "Dubbo", [ "Linux", "FastDFS" ] ] }
    
    • 向一个不存在字段追加元素,会创建新字段
     db.ego_users.update({"name":"zhangsan"},{"$push":{"aliases":"xiaozhang"}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Boot", "Spring Cloud", "MongoDB", "Dubbo", [ "Linux", "FastDFS" ] ], "aliases" : [ "xiaozhang" ] }
    
  • $addToSet,可以自动检查当前数组中是否存在相同的值,不存在就在数组后面追加一个元素,存在就不追加(相对于数据的大小写是敏感的)

    db.ego_users.update({"name":"zhangsan"}, {"$addToSet":{"courses":"JDBC"}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.update({"name":"zhangsan"}, {"$addToSet":{"courses":"Spring Boot"}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
    
  • $pop,一次删一个数据元素,字段名1表示数组元素从左往右数,移除最右边的元素,-1反之

     db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Boot", "Spring Cloud", "MongoDB", "Dubbo", [ "Linux", "FastDFS" ], "Spring Boot", [ "linux, FastDFS" ] ], "aliases" : [ "xiaozhang" ] }
    
    db.ego_users.update({"name":"zhangsan"}, {"$pop":{"courses":1}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Boot", "Spring Cloud", "MongoDB", "Dubbo", [ "Linux", "FastDFS" ], "Spring Boot" ], "aliases" : [ "xiaozhang" ] }
    
     db.ego_users.update({"name":"zhangsan"}, {"$pop":{"courses":-1}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Cloud", "MongoDB", "Dubbo", [ "Linux", "FastDFS" ], "Spring Boot" ], "aliases" : [ "xiaozhang" ] }
    
  • $pull,删除数组中的指定元素

    db.ego_users.update({"name":"zhangsan"}, {"$pull":{"courses":"Dubbo"}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Cloud", "MongoDB", [ "Linux", "FastDFS" ], "Spring Boot" ], "aliases" : [ "xiaozhang" ] }
    
    • 如果数组中存在多个相同的值,那么会该值会全部删除
    db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Cloud", "MongoDB", [ "Linux", "FastDFS" ], "Spring Boot", "Dubbo", "Dubbo", "Dubbo", "Dubbo" ], "aliases" : [ "xiaozhang" ] }
    
    db.ego_users.update({"name":"zhangsan"}, {"$pull":{"courses":"Dubbo"}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Cloud", "MongoDB", [ "Linux", "FastDFS" ], "Spring Boot" ], "aliases" : [ "xiaozhang" ] }
    
  • $pullAll,删除一个数组类型字段中的多个元素,有等值元素,同时删除

    db.ego_users.update({"name":"zhangsan"}, {"$pullAll":{"courses":["JDBC","Spring Boot"]}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.find()
    { "_id" : ObjectId("5fe05d1d385f27d277d9dc59"), "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Cloud", "MongoDB", [ "Linux", "FastDFS" ] ], "aliases" : [ "xiaozhang" ] }
    
    • 无法移除一个数组对象,使用$pull就可以
    db.ego_users.update({"name":"zhangsan"},{"$pullAll":{"courses":["Linux","FastDFS"]}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
    > db.ego_users.find({"name":"zhangsan"},{"_id":0})
    { "name" : "zhangsan", "pswd" : "123", "courses" : [ "Spring Cloud", "MongoDB", [ "Linux", "FastDFS" ] ], "aliases" : [ "xiaozhang" ] }
    
  • $rename,修改字段名称

    db.ego_users.update({"name":"zhangsan"},{"$rename":{"pswd":"password"}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.ego_users.find({"name":"zhangsan"},{"_id":0})
    { "name" : "zhangsan", "courses" : [ "MongoDB" ], "aliases" : [ "xiaozhang" ], "password" : "123" }
    
    • 实现原理,删除原字段名,拼接新字段

update( criteria, objNew, upsert, multi )

  • criteria : update的查询条件,类似sql update查询内where后面的
  • objNew : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
  • upsert : 这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
  • multi : mongodb默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。

==覆盖式更新不允许多行同时操作,但是表达式可以==

总结

  • save()函数做更新操作,如果存在“_id"字段,必须提供数据
    • 覆盖
    • 替换
  • update()提供四个参数
    • 查询条件
    • 直接传对象是覆盖,传入表达式,是修改
    • 如果更新数据不存在,那就插入数据,默认false不插入
    • 所有匹配的数据都更新,默认是false,只更新一条数据,只能在表达式更新中使用

删除操作

  • remove()

    • 删除年龄大于30的数据
    db.ego_users.remove({"age":{"$gt":30}})
    
    • 删除所有年龄大于等于25的数据,第二个参数是justOne,默认为false
    db.ego_users.remove({"age":{"$gte":25}}, false)
    
    • 删除一条年龄大于等于20的数据
    db.ego_users.remove({"age":{"$gte":20}}, true)
    
  • deleteOne()

    • 删除一条年龄大于等于20的数据
     db.ego_users.deleteOne({"age":{"$gte":20}})
    
  • deleteMany()

    • 删除多条年龄大于等于20的数据
    db.ego_users.deleteMany({"age":{"$gte":20}})
    

==先做标记,然后在机器空闲的时候释放硬盘空间==

9. 聚合查询

向test库中添加五条数据

db.ego_users.insertMany([
    {"name":"n1", "age": 20, "gender":"m", "birth": new ISODate("2000-04-10"), "pswd":"1"},
    {"name":"n2", "age": 21, "gender":"f", "birth": new ISODate("1999-04-10"), "pswd":"2"},
    {"name":"n3", "age": 22, "gender":"m", "birth": new ISODate("1998-04-10"), "pswd":"3"},
    {"name":"n4", "age": 23, "gender":"f", "birth": new ISODate("1997-04-10"), "pswd":"4"},
    {"name":"n5", "age": 24, "gender":"m", "birth": new ISODate("1996-04-10"), "pswd":"5"},
]);
  • 根据_id字段按照gender分组求和

    db.ego_users.aggregate([{"$group":{"_id":"$gender", "count_user":{"$sum":1}}}])
    { "_id" : "f", "count_user" : 2 }
    { "_id" : "m", "count_user" : 3 }
    
    • 按照哪个字段,需要在字段名前添加$,将字符常量转变成字符后面的变量
  • 根据_id字段按照gender分组求和年龄

    db.ego_users.aggregate([{"$group":{"_id":"$gender", "sum_age":{"$sum":"$age"}}}])
    { "_id" : "f", "sum_age" : 44 }
    { "_id" : "m", "sum_age" : 66 }
    
  • $match,写的位置不一样,效果不一样

    • 放在group前表示 where
    • 放在group后表示 having
  • 查询年龄大于22的人数

    db.ego_users.aggregate([{"$match":{"age":{"$gt":22}}},{"$group":{"_id":null, "count":{"$sum":1}}}])
    
  • 查询性别为男的人数

    db.ego_users.aggregate([{"$match":{"gender":"m"}},{"$group":{"_id":null, "count":{"$sum":1}}}])
    
  • 根据性别分组,求人数和

    db.ego_users.aggregate([{"$group":{"_id":"$gender","count":{"$sum":1}}}])
    
  • 查询年龄大于21,根据性别分组求人数和

    db.ego_users.aggregate([{"$match":{"age":{"$gt":21}}},{"$group":{"_id":"$gender","count":{"$sum":1}}}])
    
  • 根据性别分组求和,查询人数大于2的

    db.ego_users.aggregate([{"$group":{"_id":"$gender","count":{"$sum":1}}},{"$match":{"count":{"$gt":2}}}])
    

开发时性能分析

根据$match放置的位置不同,含义不同,需要注意性能问题,尽量将$match放在$group前面,当做where使用,先筛选出大部分的数据

  • $max,$min,求年龄的最大和最小

    db.ego_users.aggregate([{"$group":{"_id":null, "max_age":{"$max":"$age"}, "min_age":{"$min":"$age"}}}])
    
  • $avg,求年龄的平均值

    db.ego_users.aggregate([{"$group":{"_id":null, "avg_age":{"$avg":"$age"}}}])
    
  • $concat,字符串拼接,管道传输

    • 根据性别分组,求出用户名最大的,拼接性别和用户名
    db.ego_users.aggregate([{"$group":{"_id":"$gender", "max_name":{"$max":"$name"}}},{"$project":{"group_info":{"$concat":["$_id", " : ", "$max_name"]}}}])
    
  • $toUpper,$toLower,姓名大写,性别小写

    db.ego_users.aggregate([{"$project":{"upper_name":{"$toUpper":"$name"}, "lower_gender":{"$toLower":"$gender"}}}])
    
  • $substr:[字段名,起始下标,截取位数]

    db.ego_users.aggregate([{"$project":{"pswd_pre":{"$substr":["$pswd", 1, 4]}}}])
    
  • $dateToString,时间显示格式化

    db.ego_users.aggregate([{"$project":{"birth_format":{"$dateToString":{"format":"%Y-%m-%d %H:%M:%S %j", "date":"$birth"}}}}])
    

10. 数学运算符

  • $add,用于数值字段是加1,对于日期是加一毫秒

    db.ego_users.aggregate([{"$project":{"age":"$age", "age+1":{"$add":["$age",1]}, "birth":"$birth", "birth+1":{"$add":["$birth", 1]}}}])
    { "_id" : ObjectId("5fe4572c22f69a0b7d27d0f2"), "age" : 20, "age+1" : 21, "birth" : ISODate("2000-04-10T00:00:00Z"), "birth+1" : ISODate("2000-04-10T00:00:00.001Z") }
    
  • $subtract,用于数值字段是减1,对于日期是减一毫秒

    db.ego_users.aggregate([{"$project":{"age":"$age", "age-1":{"$subtract":["$age",1]}, "birth":"$birth", "birth-1":{"$subtract":["$birth", 1]}}}])
    { "_id" : ObjectId("5fe4572c22f69a0b7d27d0f2"), "age" : 20, "age-1" : 19, "birth" : ISODate("2000-04-10T00:00:00Z"), "birth-1" : ISODate("2000-04-09T23:59:59.999Z") }
    
  • $multiply,只能用于数值字段做乘法

    db.ego_users.aggregate([{"$project":{"age":"$age", "age*2":{"$multiply":["$age",2]}}}])
    
  • $divide,只能用于数值字段做除法,同时只能有两个参数

    db.ego_users.aggregate([{"$project":{"age":"$age", "age/2":{"$divide":["$age",2]}}}])
    
  • $mod,模运算

    db.ego_users.aggregate([{"$project":{"age":"$age", "age%2":{"$mod":["$age",2]}}}])
    

11. 索引

索引简介

索引能够极大的提高查询的效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

索引可以提升查询的效率,但是会影响写的效率。在查询的语句中尽量避免使用模糊查询,不等值查询。

MongoDB 会额外存储一份按字段升序/降序排序的索引数据,索引结构通常采用类似btree的结构持久化存储,保证从索引里快速找出某个值对应的位置信息,根据位置信息能够读取出对应的文档,

  • 创建索引,createIndex()、ensureIndex()

  • 不建议创建集合的时候就创建索引,因为在一开始就创建索引,添加的数据并不一定适合做根节点,当数据量变大时,MongoDB 会自动调节索引树,从而浪费资源。建议当存在大量数据后再创建索引

  • 创建索引的重要参数

    • background:创建索引时,通过子线程操作,如果为false,会导致MongoDB 阻塞,影响性能。
    • unique:唯一标识,如果为true,整个集合中的数据只能唯一,可能影响数据插入
    # 字段名参数:正数:正序;负数:降序
    db.ego_users.createIndex({"birth":-1}, {"background":true, "unique":false})
    
  • dropIndex()删除指定索引

    db.ego_users.dropIndex("birth_-1")
    
  • dropIndexes()删除_id 之外的全部索引

    db.ego_users.dropIndexes()
    {
            "nIndexesWas" : 3,
            "msg" : "non-_id indexes dropped for collection",
            "ok" : 1
    }
    
  • reIndex()重建索引,当发现字段已经建立了索引,可以查询效率还是不高,发现磁盘占有率也高,此时就需要重建索引

     db.ego_users.totalIndexSize(1)
    _id_    36864
    name_1  20480
    57344
    db.ego_users.reIndex()
    {
            "nIndexesWas" : 2,
            "nIndexes" : 2,
            "indexes" : [
                    {
                            "v" : 2,
                            "key" : {
                                    "_id" : 1
                            },
                            "name" : "_id_"
                    },
                    {
                            "v" : 2,
                            "key" : {
                                    "name" : 1
                            },
                            "name" : "name_1"
                    }
            ],
            "ok" : 1
    }
    db.ego_users.totalIndexSize(1)
    _id_    20480
    name_1  20480
    40960
    
    

索引类型

MongoDB 支持多种类型的索引,单字段索引、复合索引、多key索引、文本索引等,每种类型的索引有不同的使用场合。

  • 单字段索引

    # 通过explain()判断索引是否生效
    db.ego_users.find({"age":20}).explain()