# MongoDB 基础 ## 一、数据类型 ### 1.1 BSON 文档 (Documents) 是 MongoDB 中数据的基本单元,它是键值对的有序集合,在数据结构上类似于 JSON,但是由于 JSON 只支持基本的 6 种数据类型:null,布尔,数字,字符串,数组和对象,因此其表达能力有限,所以 MongoDB 基于 JSON 拓展出新的数据格式 BSON 。BSON (Binary JSON) 是类似 JSON 文档的二进制编码序列化格式,与 JSON 类似,但支持更多的数据类型。当前 MongoDB 4.0 的 BSON 支持以下所有的数据类型: | Type (类型) | Alias (别名) | Notes (注释) | | :---------------------- | :-------------------- | :----------------------------------------------------------- | | Double | “double” | 双精度型 | | String | “string” | 字符串类型,默认的编码类型为 UTF-8 | | Object | “object” | 对象类型 | | Array | “array” | 数据类型 | | Binary data | “binData” | 二进制数据 | | Undefined | “undefined” | 该数据类型已经废弃 | | ObjectId | “objectId” | 对象 id | | Boolean | “bool” | 布尔值 | | Date | “date” | 日期类型 | | Null | “null” | 空 | | Regular Expression | “regex” | 正则表达式 | | DBPointer | “dbPointer” | 该数据类型已经废弃 | | JavaScript | “javascript” | | | Symbol | “symbol” | 该数据类型已经废弃 | | JavaScript (with scope) | “javascriptWithScope” | | | 32-bit integer | “int” | 32 位整型 | | Timestamp | “timestamp” | 时间戳类型 | | 64-bit integer | “long” | 64 位整型 | | Decimal128 | “decimal” | 3.4 版本新增的数据类型,类似 Java 中的 BigDecimal 类型,
用于解决浮点型丢失精度的问题 | | Min key | “minKey” | | | Max key | “maxKey” | | ### 1.2 ObjectId 在上面的表格中有一个比较重要的数据类型是:ObjectId 。存储在 MongoDB 集合中的每个文档都有一个唯一的 `_id` 字段作为主键,可以在插入的时候手动指定,或者由程序自动生成,这个字段可以是任何的数据类型,默认是 ObjectId 类型。ObjectId 使用 12 字节的存储空间,是一个由 24 个十六进制数字组成的字符串,每个字符串可以存储两个十六进制数字: + 前 4 个字节是从标准纪元开始的时间戳,单位为秒; + 中 5 个字节是由两个部分组成:前 3 个字节是所在主机的唯一标识,通常是主机名的散列值,用于保证不同主机生成不同 ObjectId ,后两个字节是进程标识符 (PID) ,用于保证同一主机上不同进程产生不同的 ObjectId。 + 最后 3 个字节是一个自动增加的计数器,确保相同主机上相同进程在同一秒产生的 ObjectId 也是不同的,即每个进程一秒最多可以拥有 16777216 个不同的 ObjectId (16777216 = (2\*\*8)\*\*3,1 个字节等于 8 位二进制) 。 ## 二、新增数据 ```shell db.collection.insert() ``` 在 MongoDB 3.2 之前,插入数据的语法如上,可以用于插入单条或者多条数据。在 3.2 之后,MongoDB 为了增强 API 的语义,增加了如下两个 API ,分别用于显示表达插入单条数据和多条数据的行为: ```shell db.collection.insertOne() db.collection.insertMany() ``` 新增单条数据的示例如下。额外需要说明的是,在插入文档前,文档所属的集合不必预先创建,程序会自动创建: ```shell db.user.insertOne({ name: "heibai", age: 26, birthday: new Date(1998,08,23), createTime: new Timestamp(), Hobby: ["basketball", "football", "tennis"] }) ``` 新增多条数据的示例如下: ```shell db.user.insertMany([ { name: "hei", age: 32, birthday: new Date(1989,08,23), createTime: new Timestamp(), Hobby: ["basketball", "football", "tennis"] }, { name: "ying", age: 46, birthday: new Date(1978,08,23), createTime: new Timestamp(), Hobby: ["basketball", "football", "tennis"] } ]) # 此时会返回新插入的数据的ObjectId { "acknowledged" : true, "insertedIds" : [ ObjectId("5d3d0489ad38cd3becc7b03b"), ObjectId("5d3d0489ad38cd3becc7b03c") ] } ``` ## 三、查询数据 ### 3.1 基本 API 查询数据的基本语法如下: ```shell db.collection.find(, ) ``` + ``:用于指定查询条件,不加任何条件则默认查询集合中全部数据; + ``:可选操作,用于过滤返回字段,1 表示对应的字段包含在返回结果中,0 表示不包含,示例如下: ```shell db.user.find({},{name: 1, ObjectId:-1})} ``` ### 3.2 等值查询 想要查询某个字段等于指定值的数据,可以使用如下语法: ```shell db.user.find({name:"heibai"}) ``` 上面的语法实际上是 `$eq` 操作的简写形式,如下: ```shell db.user.find({name: {$eq: "heibai"}}) ``` 所以如果你想要进行非等值查询,则可以使用 `$ne` 操作符,代表 not equal ,示例如下: ```shell db.user.find({name: {$ne: "heibai"}}) ``` 特别的,如果你想允许某个字段等于多个值,可以使用 `$in` 操作符,示例如下: ```shell db.user.find({name: {$in:["heibai","ying"]} }) ``` ### 3.3 范围查询 Mongodb 提供了比较操作符 `$lt`、`$lte`、`$gt` 和 `$gte` ,分别对应 <、 <=、 >和 >= ,主要用于范围查查询,示例如下: ```shell db.user.find({age: {$gt: 20, $lt: 40}}) ``` ### 3.4 逻辑查询 Mongodb 提供了逻辑操作符 `$or`、`$and`、`$not`、`$nor` ,用于处理多个条件间的逻辑关系,示例如下: 查询姓名为 heibai 或者年龄大于 30 岁的所有用户,此时可以使用 $or 操作符: ```shell db.user.find( { $or: [{ name: "heibai" }, { age: { $gt: 30 } }] }) ``` 查询姓名不是以 hei 开头的所有用户,此时可以使用 $not 操作符来配合正则表达式: ```shell db.user.find({name: {$not: /^hei*/}}) ``` 如果要求 name 字段的值不能为 heibai, age 字段的值不能大于 30,则对应的查询语句如下: ```shell db.user.find( { $nor: [{ name: "heibai" }, { age: { $gt: 30 } }] }) ``` $and 操作符的使用率比较低,因为此时更好的方式是把多个条件写到同一个对象中,从而减少额外嵌套。 ### 3.5 集合查询 如果需要查询个人爱好中有 football 的所有用户,即集合 Hobby 中存在 football 即可,对应的查询语句如下: ```shell db.user.find({Hobby: "football"}) ``` 如果想要获取集合中指定位置等于指定值的文档,对应的查询语句如下: ```shell db.user.find({"Hobby.2": "football"}) ``` 如果想要约束集合必须包含多个指定值,此时可以使用 $all 操作符: ```shell db.user.find({Hobby:{ $all: ["football", "tennis"]}}) ``` 查询时如果只想返回集合的部分内容,则可以使用 $slice ,$slice 接收一个参数 n,正数表示获取集合的前 n 个参数,负数表示获取集合末尾的 n 个参数,示例如下: ```shell db.user.find({name: "heibai"},{Hobby:{$slice: 2}}) ``` ### 3.6 额外操作 在查询操作之后,MongoDB 还提供了三个额外的函数 `skip`、`sort` 和 `limit` ,分别用于表示跳过一定量的数据、按照指定规则对数据进行排序和限制返回数据的量,示例如下: ```shell db.user.find({}) .skip(1) .sort({_id:-1}) .limit(10) ``` ## 四、修改数据 ### 4.1 基本 API MongoDB 提供了以下三个 API,分别用于修改单条数据、多条数据和执行单条数据替换: ```shell db.collection.replaceOne(, , ) db.collection.updateOne(, , ) db.collection.updateMany(, , ) ``` + ``:过滤条件,用于查询需要修改的数据; + ``:更改操作或新文档数据; + ` `:可选操作,常用的可选操作是 `upsert` ,当其为 true 时,代表如果按照过滤条件没有找到对应的文档,则将待更改的数据插入到集合中;当其为 false 时,如果没有找到数据,则不执行任何操作。示例如下: ```shell db.user.replaceOne( { _id: ObjectId("5d3d00a4ad383d3becc7b03a")}, { name: "danrenying", age: 32, birthday: new Date(1995,08,23), createTime: new Timestamp(), Hobby: ["basketball", "football", "tennis"] }, {upsert : true} ) ``` ### 4.2 常规修改器 想要对数据进行修改,必须明确表达修改行为,在 MongoDB 中这是通过修改器来实现的,常用的修改器如下: #### 1. $set 用于修改具体的字段,如果待修改的字段不存在,则会新增该字段。示例如下: ```shell db.user.updateOne( { name: "danrenying"}, { $set: {age: 66} } ) ``` #### 2. $inc 用于对指定字段的值进行增加或减少,示例如下: ```shell db.user.updateOne( { name: "danrenying"}, { $inc: {age: -10} } ) ``` ### 4.3 数组修改器 在修改操作中,比较复杂的是对数组数据的修改,为了解决这个问题,MongoDB 提供了一系列的数组修改器,用于数组操作: #### 1. $push 用于往数组中新增数据,示例如下。使用 `$each` 可以一次添加多个元素: ```shell db.user.updateOne( { name: "danrenying"}, { $push: {"Hobby": {$each: ["film","music"]}} } ) ``` #### 2. $addToSet 该修改器可以把数组当做集 (set) 来使用,即只能添加当前数组中不存在的数据,示例如下: ```shell db.user.updateOne( { name: "danrenying"}, { $addToSet: {"Hobby": {$each: ["film","music"]}} } ) ``` #### 3. $pop 该修改器可以从数组任意一端删除元素,`-1` 代表从数组头删除元素,`1` 代表从数组尾删除元素,示例如下: ```shell db.user.updateOne( { name: "danrenying"}, { $pop: {"Hobby": -1} } ) ``` #### 4. index 用于直接修改指定下标位置的元素,示例如下: ```shell db.user.updateOne( { name: "danrenying"}, { $set: {"Hobby.0": "Cooking"} } ) ``` ## 五、删除数据 MongoDB 提供了以下两个 API 用于删除操作: ```shell db.collection.deleteMany() db.collection.deleteOne() ``` 使用示例如下: ```shell db.user.deleteOne( { name: "danrenying"} ) ``` ## 参考资料 + 官方文档:[MongoDB CRUD Operations](https://docs.mongodb.com/manual/crud/) + Kristina Chodorow . MongoDB权威指南(第2版). 人民邮件出版社 . 2014-01