# 三、MongoDB CRUD

参考:

  • https://docs.mongoing.com/mongodb-crud-operations
  • https://docs.mongodb.com/manual/crud/

# 1. 数据库 database

# 1.1 创建数据库

  • use dbname

数据库不在则会自动创建。

数据库命名规则

数据库名可以是满足以下条件的任意 UTF-8 字符串:

  • 不能是空字符串
  • 不得含有 ''(空格)、.$/\\0(空字符)
  • 应全部小写
  • 最多 64 字节

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库:

  • admin:从权限的角度看,这是个 “root” 数据库。要是一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local:这个数据库永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
  • config:当 mongo 用于分片配置时,config 数据库在内部使用,用于保存分片的相关信息。

# 1.2 删除数据库

  • db.dropDatabase()

主要用来删除提交持久化的数据库。

# 1.3 查看数据库

  • show dbs:查看所有数据库,只有当 db 里面有数据了,才会被持久化到磁盘当中,否则只在内存里面的话,用 show dbs 是查不到的。
  • db:查看当前使用的数据库。

MongoDB 默认的数据库为 test,如果你没有选择数据库,集合将存放在 test 数据库中。

# 2. 集合 collection

# 2.1 创建集合

# 2.1.1 显式创建

  • db.createCollection(name)

# 2.1.2 隐式创建

  • 当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合。

集合命名规范

  • 集合名不能是空字符串""。
  • 集合名不能含有 \0 字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以 "system."开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现 $。

# 2.2 删除集合

  • db.collectionName.drop()

如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。

# 2.2 查看集合

  • show collections
  • show tables

# 3. 文档 document

# 3.1 插入文档

  • db.collection.insertOne():插入单个
  • db.collection.insertMany():插入多个
  • db.collection.insert():插入单个或多个
  • db.collection.save(): 插入,底层调用 insert
db.collection.insert(
	<document or array of document>
	{
		writeConcern: <document>,  # 写关注,可选
		ordered: <boolean>         # 是否按顺序插入,默认为 true
                                    # true:有一个文档出现错误,则停止插入,返回还没处理的剩余的文档
                                    # false:如果出错,则继续处理后面的文档
	}
)

补充

  1. 创建或插入操作会将新文档添加到集合中。 如果该集合当前不存在,则插入操作将创建该集合。

  2. 在MongoDB中,插入操作针对单个集合。 MongoDB中的所有写操作都是单个文档级别的原子 (opens new window)操作。

    In MongoDB, a write operation is atomic (opens new window) on the level of a single document, even if the operation modifies multiple embedded documents within a single document.

  3. 在 MongoDB 中,存储在集合中的每个文档都需要一个唯一的 _id 字段作为主键。 如果插入的文档省略 _id 字段,则 MongoDB 驱动程序会自动为 _id 字段生成 ObjectId

  4. 插入当前日期,用 new Date()

  5. MongoDB 中的数字,默认情况下是 double 类型,如果要存整型,必须使用函数 NumberInt(整型数字),否则取出来就有问题了。

  6. 如果某字段没值,可以赋值为 null,或不写该字段。

insertOne、insertMany、insert 的区别

  1. 三者返回的错误信息不一样;
  2. insertOne、insertMany 不支持 db.collection.explain() 命令。

# 3.1.1 插入一个文档

> db.hedon.insertOne({ name: "hedon", age: 18, address: "beijing", tags: ["student", "software developer"], body: {weight: 70, height: 199} })
{
	"acknowledged" : true,
	"insertedId" : ObjectId("6115e5c33e8322a9d326c23f")
}

insertOne 返回一个文档,其中包含新插入的文档的 _id 字段值。

查看刚刚插入的文档:

> db.hedon.find({name: "hedon"})
{ "_id" : ObjectId("6115e5c33e8322a9d326c23f"), "name" : "hedon", "age" : 18, "address" : "beijing", "tags" : [ "student", "software developer" ], "body" : { "weight" : 70, "height" : 199 } }

注意

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

文档键命名规范:

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

# 3.1.2 插入多个文档

> db.hedon.insertMany([
... { name: "jack"},
... { name: "windy"},
... { name: "wuyifan"}
... ])
{
	"acknowledged" : true,
	"insertedIds" : [
		ObjectId("6115e6633e8322a9d326c240"),
		ObjectId("6115e6633e8322a9d326c241"),
		ObjectId("6115e6633e8322a9d326c242")
	]
}

# 3.1.3 主键

ObjectId

自定义主键

复合主键

# 3.2 查询文档

  • db.collection.find(<query>, [projection])
Parameter Type Description
query document 可选。使用查询运算符指定选择筛选器。若要返回集合中的所有文档,请省略此参数或传递空文档 ( {} )。
projection document 可选。指定要在与查询筛选器匹配的文档中返回的字段(投影)。若要返回匹配文档中的所有字段, 请省略此参数。

方便下面案例,请先插入数据:

db.inventory.insertMany([
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);

# 3.2.1 查询所有

> db.inventory.find()
{ "_id" : ObjectId("6115ec3e3e8322a9d326c243"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c244"), "item" : "notebook", "qty" : 50, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "A" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c245"), "item" : "paper", "qty" : 100, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "D" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c246"), "item" : "planner", "qty" : 75, "size" : { "h" : 22.85, "w" : 30, "uom" : "cm" }, "status" : "D" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c247"), "item" : "postcard", "qty" : 45, "size" : { "h" : 10, "w" : 15.25, "uom" : "cm" }, "status" : "A" }

# 3.2.2 条件查询

  • EQUAL

    db.inventory.find( { status: "D"} )
    
  • GT/LT/GTE/LTE

    db.inventory.find( { qty: { $lt: 30 } } )
    
  • IN

    db.inventory.find( { status: { $in: [ "A", "D" ] } } )
    

    尽管可以使用 $or 操作符表示此查询,但在对同一字段执行相等性检查时,请使用 $in 操作符而不是 $or 操作符,因为 $in 可以走索引,查询效率更高。

  • AND

    db.inventory.find( { status: "A", qty: { $lt: 30 } } )
    
  • OR

    db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
    
  • AND & OR

    db.inventory.find( {
         status: "A",
         $or: [ { qty: { $lt: 30 } }, { item: "journal" } ]
    } )
    
  • REGEX

    db.inventory.find( {
         status: "A",
         $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
    } )
    

    MongoDB支持正则表达式 $regex 查询来执行字符串模式匹配。

# 3.2.3 投影查询

默认情况下,MongoDB 中的查询返回匹配文档中的所有字段。 要限制 MongoDB 发送给应用程序的数据量,可以包含一个 projection 文档以指定或限制要返回的字段。

  • fieldName: 1:显示 filedName 字段,_id 字段默认为 1
  • fieldName: 0:不显示 fieldName 字段

如果 projection 中只有 0,那就是排除掉指定的 field

# 仅查询 _id, item, status
db.inventory.find( { status: "A" }, { item: 1, status: 1 } )

# 仅查询 item, status
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )

# 不要 status 和 instock 字段
db.inventory.find( { status: "A" }, { status: 0, instock: 0 } )

# 返回嵌入式文档中的特定字段
db.inventory.find(
  { status: "A" },
  { item: 1, status: 1, "size.uom": 1 }
)

# 禁止嵌入文档中的特定字段
db.inventory.find( 
      { status: "A" },
      { "size.uom": 0 }
)

# 返回数组中的项目特定数组元素,-1 表示最后一个元素
db.inventory.find( { status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } } )

对于包含数组的字段,MongoDB 提供以下用于操纵数组的投影运算符:$elemMatch$slice$$elemMatch$slice$ 是 projection 要包含在返回数组中的特定元素的唯一方法。 例如,您不能使用数组索引来投影特定的数组元素。 例如 {“ instock.0”: 1} 是不会返回数组的第一个元素的。

# 3.2.4 嵌入式文档查询

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 } ] }
]);
  • 上面插入的结构中,instock 是一个数组,其中每一个元素都是一个 document。我们可以直接对数据里面的元素进行条件查询。
db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )

整个嵌入式/嵌套文档上的相等匹配要求与指定文档(包括字段 顺序)完全匹配。 例如,以下查询与 inventory 中的任何文档都不匹配

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

# 3.2.5 数组查询

数据准备:

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 ] }
]);
# 查询 tags 值是按指定顺序恰好具有两个元素"red" 和"blank"的数组
db.inventory.find( { tags: ["red", "blank"] } )

# 查询一个同时包含元素“red”和“blank”的数组,而不考虑顺序或该数组中的其他元素,用 $all
db.inventory.find( { tags: { $all: ["red", "blank"] } } )

# 查询元素中的 tag 有 “red” 的数组
db.inventory.find( { tags: "red" } )

# 查询数组 dim_cm 包含至少一个值大于25的元素的所有文档
db.inventory.find( { dim_cm: { $gt: 25 } } )

# 查询在 dim_cm 数组中包含至少一个同时大于 ($gt)22 和小于 ($lt) 30的元素的文档,用 $elemMatch
db.inventory.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )

# 查询数组 dim_cm 中第 2 个元素大于 25 的所有文档,用数组下表
db.inventory.find( { "dim_cm.1": { $gt: 25 } } )

# 数组 tags 具有 3 个元素的文档,用 $size
db.inventory.find( { "tags": { $size: 3 } } )

# 3.2.6 空字段和不存在的字段查询

  • 平等过滤器

    # {item:null} 查询匹配包含值是 null 的 item 字段或不包含 item 字段的文档
    db.inventory.find( { item: null } )
    
  • 类型检查

    # {item:{$ type:10}} 查询只匹配包含 item 字段值为 null 的文档; 即 item 字段的值为 BSON Type 为 Null(类型编号 10):
    db.inventory.find( { item : { $type: 10 } } )
    
  • 存在检查

    # {item:{$ exists:false}} 查询与不包含 item 字段的文档匹配
    db.inventory.find( { item : { $exists: false } } )
    

# 3.3 更新文档

  • db.collection.updateOne(<filter>, <update>, <options>):更新一个,没有则创建
  • db.collection.updateMany(<filter>, <update>, <options>):更新多个,没有则创建
  • db.collection.replaceOne(<filter>, <update>, <options>):替换一个
  • db.collection.update(<filter>, <update>, <options>):更新一个或多个或替换,默认单个
db.collection.update(
    <query>, <update>, {
    upsert: <boolean>,
    multi: <boolean>,  # 是否更新多个
    writeConcern: <document>,
    collation: <document>,
    arrayFilters: [ <filterdocument1>, ... ],
    hint: <document|string> # MongoDB 4.2 后可用
} )

更新示例:

db.inventory.updateOne(
   { item: "paper" },
   {
     $set: { "size.uom": "cm", status: "P" },
     $currentDate: { lastModified: true }
   }
)
  • $set:修改一个字段,如果该字段不存在,则创建该字段
  • $currentDate:将 lastModified 字段更改为当前时间值,如果该字段不存在,则创建

替换示例:

  • 要替换 _id 字段以外的文档的全部内容,可以将一个全新的文档作为第二个参数传递给 db.collection.replaceOne
# 替换了 inventory 集合中的第一个文件,其中 item 为 "paper"
db.inventory.replaceOne(
   { item: "paper" },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)

注意

  • 替换
    • 当替换一个文档时,替换文档必须只包含字段/值对,即不包括更新操作符表达式。
    • 替换文档可以具有与原始文档不同的字段。在替换文档中,由于 _id 字段是不可变的,因此可以省略 _id 字段。但是,如果你确实包含 _id 字段,则它必须与当前值具有相同的值。
  • 原子性
    • MongoDB 中的所有写操作都是单个文档级别上的原子操作。
  • _id
    • _id 值不可变,也无法将现有文档替换为具有不同 _id 值的文档

# 3.4 删除文档

  • db.collection.deleteMany(<filter>):删除多个
  • db.collection.deletOne(<filter>):删除单个
  • db.collection.remover(<filter>, <justone>):删除单个或多个,用 justone 字段来指定是否删除单个
db.inventory.deleteOne( { status: "D" } )
上次更新: 11/25/2021, 7:13:25 PM