# MongoDB 基础

# 参考文档

# 安装

// TODO

# 连接

使用连接字符串方式:

  • mongo "mongodb://host:port"
  • mongo "mongodb://user:pwd@host:port/db_name?authSource=admin...(即 options)"

# BSON

  • binary json。
  • BSON 文档的最大大小为 16 MB。

# ObjectId

ObjectId 很小,可能唯一,可以快速生成并排序。ObjectId 值的长度为 12 个字节,包括:

  • 一个 4 字节的_时间戳记值_,代表自 Unix 时代以来以秒为单位的 ObjectId 的创建。
  • 5 字节_随机值_。
  • 3 字节_递增计数器_,初始化为随机值。

在 mongoshell 中,可以使用 ObjectId.getTimestamp() 方法访问 ObjectId 的创建时间。

在存储 ObjectId 值的 _id 字段上按大致相当于创建时间进行排序。

重要: 尽管 ObjectId 值应随时间增加,但不一定是单调的。这是因为他们仅包含一秒的时间分辨率,因此在同一秒内创建的 ObjectId 值没有保证的顺序,并且由客户端生成,客户端可能具有不同的系统时钟。

# 字符串

BSON 字符串为 UTF-8。通常,在对 BSON 进行序列化和反序列化时,每种编程语言的驱动程序都会从该语言的字符串格式转换为 UTF-8。这样就可以轻松地将大多数国际字符存储在 BSON 字符串中。

此外,MongoDB $regex 查询在正则表达式字符串中支持 UTF-8

# 日期 Date

BSON Date 是一个 64 位整数,代表自 Unix 纪元(1970年1月1日)以来的毫秒数。这导致可以追溯到过去和未来约 2.9 亿年的日期范围。

官方 BSON 规范指的是 BSON Date 类型为 UTC日期时间

BSON 日期类型是有符号整数。负值表示 1970 年之前的日期。

示例:

  • 在 mongo shell 中使用构造函数 new Date() 构造一个 Date :var mydate1 = new Date()
  • 在 mongo shell 中使用构造函数 ISODate() 构造一个 Date :var mydate2 = ISODate()
  • 以字符串形式返回 Date 值:mydate1.toString()
  • 返回日期值的月份部分,月是零索引,因此一月是 0 月:mydate1.getMonth()

# 库操作

  • db,查看当前数据库。
  • show dbs
  • use db_name
  • db.dropDatabase(),删除当前使用的数据库。

MongoDB 中直接创建一条数据,如果对应的表不存在就会被创建出来。

比如:db.user.insertOne({name:"xiaoming"}),该命令会同步创建 user 集合(表),如果之前不存在的话。use Haha,就会将 Haha 库创建出来。

# 集合操作

  • 显示所有集合, show collections
  • 创建集合, db.createCollection('collection',[options])
  • 删除集合, db.collection.drop()

# 文档操作

db.collection.action():

  • find(query, projection).pretty()
    • 默认返回 20 条数据。
    • 返回值是一个 cursor。
    • query 是查询条件,
    • projection 控制字段的显示和隐藏
    • pretty 是格式化。
    • sort,1 正序, -1 倒序。
    • skip,跳过几个。
    • limit,控制返回条数。
  • insert(params)params 可以是对象,也可以是数组类型。
  • remove(query)query 是查询条件。
  • update(query, update, [options])
    • $set$inc$pull$push

# 插入文档

如果该集合当前不存在,则插入操作将创建该集合。

  • db.collection.insert(one or more),一个 or 多个对象数组。
  • db.collection.insertOne({}),一个对象。
  • db.collection.insertMany([{},{}]),对象数组。

另外:

  • db.collection.save(),如果包含 _id,执行 update,如果不包含,则执行创建。
  • ...

# 查询文档

  • db.collection.find(),返回游标 <==> SELECT * FROM tb
  • db.collection.findOne(),返回文档。

# 查询操作符

  • $or $and $not $nor,值是一个数组类型。
  • $eq$gt$gte$in$lt$lte$ne$ninregex
  • $elemMatch,匹配对象数组。

# $in

查找集合中 status = A 或 D 的所有文档:

db.collection.find( { status: { $in: [ "A", "D" ] } } ) <==> SELECT * FROM tb WHERE status in ("A", "D")

# $and

查找集合中 status = A 并且 qty <($lt) 30 的所有文档:

db.collection.find( { status: "A", qty: { $lt: 30 } } ) <==> SELECT * FROM tb WHERE status = "A" AND qty < 30

# $or

查找集合中 status = A 或者 qty <($lt) 30 的所有文档:

db.collection.find({ 
    $or: [ 
        { status: "A" }, 
        { qty: { $lt: 30 } }
    ] 
})
// <==> SELECT * FROM db WHERE status = "A" OR qty < 30
// And 操作与 Or 操作一样,也可以写成 $and:[{..},{..},...] 的形式,但可省略。
1
2
3
4
5
6
7
8

# 控制返回字段/projection

_id 默认返回,除_id 字段外,不能在映射文档中同时使用包含和去除语句!!!

# 所有字段

如果没有指定 projection,find() 会返回文档的所有字段。

db.collection.find( { status: "A" } ) <==> SELECT * from tb WHERE status = "A"

# 仅返回指定字段和_id字段

db.collection.find( { status: "A" }, { item: 1, status: 1 } ) <==> SELECT _id, item, status from tb WHERE status = "A"

# 去除_id字段

db.collection.find( { status: "A" }, { item: 1, status: 1, _id: 0 } ) <==> SELECT item, status from tb WHERE status = "A"

# 去除指定字段

除 _id 字段外,不能在映射文档中同时使用包含和去除语句。

db.collection.find( { status: "A" }, { status: 0, instock: 0 } )

# 查询空字段或缺少字段

使用 $exist 查询不包含 age 字段的文档:

db.collection.find( { age : { $exists: false } } )

# 数组查询

首先造一些实验数据:

db.inventory.insertMany([
    { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
    { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
    { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
    { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
    { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
1
2
3
4
5
6
7

# 单条件

  1. 查询数组字段 tags 值只包含两个元素 "red","blank" 并且有指定顺序的数组的所有文档:

    db.inventory.find( { tags: ["red", "blank"] } )

  2. 查询数组中包含 "red","blank" 两个元素并且不在乎元素顺序或者数组中是否有其它元素。可以使用 $all 操作符:

    db.inventory.find( { tags: { $all: ["red", "blank"] } } )

  3. 查询数组字段 tags 中有一个元素的值是 "red" 的所有文档:

    db.inventory.find( { tags: "red" } )

  4. 查询数组字段 dim_cm 中最少有一个元素的值 >25 的所有文档:

    db.inventory.find( { dim_cm: { $gt: 25 } } )

# 多条件

  • 满足任一: 查询数组字段 dim_cm 中单个元素同时满足 >15 并且 <20,或者一个元素满足 >15,另外一个元素 <20 的所有文档:

    db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } )

  • 全满足: 查询数组字段 dim_cm 中最少一个元素同时满足 >($gt)22 并且 <($lt) 30:

    db.inventory.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )

    使用$elemMatch来指定多个查询条件在数组中的元素上,数组中最少一个元素同时满足所有的查询条件。

# 使用数组长度来检索

使用 $size 操作符通过数组中的元素个数来进行检索。

查询数组字段 tags 中有三个元素的所有文档: db.inventory.find( { "tags": { $size: 3 } } )

# 查询嵌入式对象数组

首先造一些实验数据:

db.inventory.insertMany( [
    { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
    { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
    { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
    { item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
    { item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
1
2
3
4
5
6
7
  1. 查询 instock 数组中元素等于指定文档的的所有文档:

    正确的是:db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )

    当对整个嵌套文档使用等值匹配的时候是要求精确匹配指定文档,包括字段顺序。比如,下面的语句并没有查询到 inventory 集合中的任何文档:db.inventory.find( { "instock": { qty: 5, warehouse: "A" } } )

  2. 查询 instock 数组中的第一个元素是包含字段 qty <= 20 的所有文档:

    db.inventory.find( { 'instock.0.qty': { $lte: 20 } } )

    注意: 当查询使用点号.的时候,字段和索引必须在引号内。

  3. 使用 $elemMatch 操作符为数组中的嵌套文档指定多个查询条件,最少一个嵌套文档同时满足所有的查询条件。

# 更新文档

更新方法定义:

db.collection.update(
    <query>,
    <update>,
    {
        // 可选。如果设置为 true,则在没有文档符合查询条件时创建一个新文档。默认值为 false。
        upsert: <boolean>,
        // 可选。如果设置为 true,则更新满足 query 条件的多个文档。如果设置为false,则更新一个文档。默认值为false。
        multi: <boolean>,
        writeConcern: <document>,
        collation: <document>,
        arrayFilters: [ <filterdocument1>, ... ],
        hint:  <document|string>        // Available starting in MongoDB 4.2
    }
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

主要方法有:

  • db.collection.update()
  • db.collection.updateOne()
  • db.collection.updateMany()
  • db.collection.replaceOne()

附加方法:

  • db.collection.save()
  • db.collection.findAndModify()
  • db.collection.findOneAndUpdate()
  • db.collection.findOneAndReplace()
  • db.collection.bulkWrite()

# 更新操作符

$set$unset 移除字段$inc$pull$push

不加操作符,就是全量替换。

示例:

  1. 将 _id=15 的用户 name 修改为 xiaoming:

    db.user.updateOne( { _id: 15 }, { $set: { "name": "xiaoming"} })

# 删除文档

  • db.collection.deleteOne()
  • db.collection.deleteMany()

# 游标

在 mongo shell 脚本中打印结果游标中的所有项目,请使用以下惯用法:

cursor = db.collection.find();
while ( cursor.hasNext() ) {
    printjson( cursor.next() );
}
1
2
3
4

也可以使用游标方法 forEach() 来迭代游标并访问文档,如下例所示:

var myCursor =  db.users.find( { type: 2 } );
myCursor.forEach(printjson);
1
2

# 讨论区

由于评论过多会影响页面最下方的导航,故将评论区做默认折叠处理。

点击查看评论区内容,渴望您的宝贵建议~
Last Updated: 5/4/2023, 6:48:38 PM