MongoDB
MongoDB 的核心优势
- 灵活模式 json 文档
- 高可用性 副本集
- 可扩展性 sharded cluster 分片集群,可以将数据分散存储到多个分片 shard 上来实现高扩展性。
使 MongoDB 具备了横向扩展的能力。
部署方式
- 主从复制 master-slave ,目前不推荐使用了。
- 副本集 replica set,将数据复制多份,不同服务器保存同一份数据,在出现故障时自动切换,实现故障转移。
- 分片模式 sharding,适合处理大量数据,它将数据分开存储,不同的服务器保存不同的数据,所有服务器数据的总和即为整个数据集。(* 追求高性能*)
https://www.cnblogs.com/yanxinjiang/p/14600568.html
https://zhangquan.me/2023/03/26/mongodb-fen-pian-ji-qun-ji-zhi-ji-yuan-li/
副本集部署方式
增删改查基本操作
- Adhoc queries 临时查询
- Data transformations 数据转换
- Document join support 文档连接
$lookup
$unionwith
- Graph and geospatial queries 图形和地理空间查询
$geoWithin
$geoNear
graphLookup
- Full-text search 全文检索
$search
- Semantic search 语义搜索
$vectorSearch
- Indexing 索引
- On-demand materialized views 按需创建实体索引
$out
$merge
- Time series analysis 时间序列分析
1. 查询数据库列表
db.adminCommand({ listDatabases: 1 });
2. $unwind
db.inventory.insertMany( [
{ prodId: 100, price: 20, quantity: 125 },
{ prodId: 101, price: 10, quantity: 234 },
{ prodId: 102, price: 15, quantity: 432 },
{ prodId: 103, price: 17, quantity: 320 }
] );
db.orders.insertMany( [
{ orderId: 201, custid: 301, prodId: 100, numPurchased: 20 },
{ orderId: 202, custid: 302, prodId: 101, numPurchased: 10 },
{ orderId: 203, custid: 303, prodId: 102, numPurchased: 5 },
{ orderId: 204, custid: 303, prodId: 103, numPurchased: 15 },
{ orderId: 205, custid: 303, prodId: 103, numPurchased: 20 },
{ orderId: 206, custid: 302, prodId: 102, numPurchased: 1 },
{ orderId: 207, custid: 302, prodId: 101, numPurchased: 5 },
{ orderId: 208, custid: 301, prodId: 100, numPurchased: 10 },
{ orderId: 209, custid: 303, prodId: 103, numPurchased: 30 }
] );
// 根据 orders 创建 sales 集合
db.createView("sales", "orders", [
{
$lookup: // 使用 orders.prodId 匹配 inventory.prodId
{
from: "inventory",
localField: "prodId",
foreignField: "prodId",
as: "inventoryDocs" // 匹配到的数据添加到 inventoryDocs 集合中
}
},
{
$project: // 选择显示的字段
{
_id: 0, // 不显示
prodId: 1, // 显示
orderId: 1,
numPurchased: 1,
price: "$inventoryDocs.price" // 聚合成 price 字段(数组)
}
},
{
$unwind: "$price" // 将数组展开
}
]);
$unwind
的作用: 展开数组字段
假设 collection 的数据如下所示
[
{ "_id": 1, "product": "A", "price": [100, 200, 300] },
{ "_id": 2, "product": "B", "price": [400, 500] },
{ "_id": 3, "product": "C", "price": [] },
{ "_id": 4, "product": "D" }
]
在使用 $unwind
函数之后
[
{ "_id": 1, "product": "A", "price": 100 },
{ "_id": 1, "product": "A", "price": 200 },
{ "_id": 1, "product": "A", "price": 300 },
{ "_id": 2, "product": "B", "price": 400 },
{ "_id": 2, "product": "B", "price": 500 }
]
3. 创建视图,并排序
db.places.insertMany([
{ _id: 1, category: "café" },
{ _id: 2, category: "cafe" },
{ _id: 3, category: "cafE" }
]);
db.places.find();
根据 places 创建视图 placesView , 只保留 category 字段,并且根据 fr 进行排序
db.createView(
"placesView",
"places",
[{
$project: {
category: 1
}
}],
{
collation: {
locale: "fr", // 指定语言环境为 法语
strength: 1 // 排序强度, 1 表示只比较基本字符.
}
}
);
统计 category = cafe 的文档数量
db.placesView.countDocuments({
category: "cafe"
});
4. 视图修改
先删除,再新建
db.createView("places-view-modify", "places", [
{
$match: {
category: "cafe"
}
}
]);
db["places-view-modify"].find();
db["places-view-modify"].drop();
直接修改视图
db.runCommand(
{
collMod: "places-view-modify",
viewOn: "places",
"pipeline": [{
$match: {
category: "cafE"
}
}]
}
);
5. 物化视图
// 物化视图 $merge $out 的结果
db.bakesales.insertMany( [
{ date: new ISODate("2018-12-01"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
{ date: new ISODate("2018-12-02"), item: "Cake - Peanut Butter", quantity: 5, amount: new NumberDecimal("90") },
{ date: new ISODate("2018-12-02"), item: "Cake - Red Velvet", quantity: 10, amount: new NumberDecimal("200") },
{ date: new ISODate("2018-12-04"), item: "Cookies - Chocolate Chip", quantity: 20, amount: new NumberDecimal("80") },
{ date: new ISODate("2018-12-04"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
{ date: new ISODate("2018-12-05"), item: "Pie - Key Lime", quantity: 3, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-01-25"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-01-25"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
{ date: new ISODate("2019-01-26"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-01-26"), item: "Cookies - Chocolate Chip", quantity: 12, amount: new NumberDecimal("48") },
{ date: new ISODate("2019-01-26"), item: "Cake - Carrot", quantity: 2, amount: new NumberDecimal("36") },
{ date: new ISODate("2019-01-26"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-01-27"), item: "Pie - Chocolate Cream", quantity: 1, amount: new NumberDecimal("20") },
{ date: new ISODate("2019-01-27"), item: "Cake - Peanut Butter", quantity: 5, amount: new NumberDecimal("80") },
{ date: new ISODate("2019-01-27"), item: "Tarts - Apple", quantity: 3, amount: new NumberDecimal("12") },
{ date: new ISODate("2019-01-27"), item: "Cookies - Chocolate Chip", quantity: 12, amount: new NumberDecimal("48") },
{ date: new ISODate("2019-01-27"), item: "Cake - Carrot", quantity: 5, amount: new NumberDecimal("36") },
{ date: new ISODate("2019-01-27"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-01-28"), item: "Cookies - Chocolate Chip", quantity: 20, amount: new NumberDecimal("80") },
{ date: new ISODate("2019-01-28"), item: "Pie - Key Lime", quantity: 3, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-01-28"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
] );
db.bakesales.find();
// 定义了一个包含每月累计销售信息的物化视图
updateMonthlySales = function(startDate) {
db.bakesales.aggregate([
{
$match: {
date: {
$gte: startDate // 大于等于传入的日期的数据
}
}
},
{
$group: {
_id: {
$dateToString: { // 根据 date 字段的 %Y-%m 进行分组
format: "%Y-%m",
date: "$date"
}
},
sales_quantity: {
$sum: "$quantity" // 新增一个 sales_quantity 字段
},
sales_amount: {
$sum: "$amount" // 新增 sales_amount 字段
}
}
},
{
$merge: {
into: "monthlybakesales", // 将数据输出到 monthlybakesales 集合中
whenMatched: "replace" // 当文档已经存在于目的集合时,替换目的集合中的文档
}
}
]);
};
updateMonthlySales(new ISODate("1970-01-01"));
db.monthlybakesales.find();
更新物化视图
db.bakesales.insertMany( [
{ date: new ISODate("2019-01-28"), item: "Cake - Chocolate", quantity: 3, amount: new NumberDecimal("90") },
{ date: new ISODate("2019-01-28"), item: "Cake - Peanut Butter", quantity: 2, amount: new NumberDecimal("32") },
{ date: new ISODate("2019-01-30"), item: "Cake - Red Velvet", quantity: 1, amount: new NumberDecimal("20") },
{ date: new ISODate("2019-01-30"), item: "Cookies - Chocolate Chip", quantity: 6, amount: new NumberDecimal("24") },
{ date: new ISODate("2019-01-31"), item: "Pie - Key Lime", quantity: 2, amount: new NumberDecimal("40") },
{ date: new ISODate("2019-01-31"), item: "Pie - Banana Cream", quantity: 2, amount: new NumberDecimal("40") },
{ date: new ISODate("2019-02-01"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-02-01"), item: "Tarts - Apple", quantity: 2, amount: new NumberDecimal("8") },
{ date: new ISODate("2019-02-02"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-02-02"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
{ date: new ISODate("2019-02-03"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") }
]);
updateMonthlySales(new ISODate("2019-01-01"));
6. capped collections
capped collections: 固定大小的集合。
db.createCollection("log", {
capped: true,
size: 100000 // 最大大小为 100000 的集合 ( mongodb 会四舍五入到最接近 256 的整数倍)
});
db.createCollection(
"log2",
{
capped: true,
size: 5242880,
max: 5000 // 文档数最大为 5000
}
);
db.log.insertMany( [
{
message: "system start",
type: "startup",
time: 1711403508
},
{
message: "user login attempt",
type: "info",
time: 1711403907
},
{
message: "user login fail",
type: "warning",
time: 1711404209
},
{
message: "user login success",
type: "info",
time: 1711404367
},
{
message: "user logout",
type: "info",
time: 1711404555
}
] )
db.log.find();
db.log.find({
type: "info"
});
db.log.find().sort({
$natural: - 1 // 与插入的顺序相反
});
判断集合是否为capped collections
db.log.isCapped();
修改限制
db.runCommand( { collMod: "log", cappedSize: 5242880 } )
db.runCommand( { collMod: "log", cappedMax: 5000 } )
7. null
与 undefined
MongoDB 8.0 之后,null
的比较不会匹配 undefined
。
db.people.insertMany([
{
_id: 1,
name: null
},
{
_id: 2,
name: undefined
},
{
_id: 3,
name: ["Gabriel", undefined]
},
{
_id: 4,
names: ["Alice", "Charu"] // 注意,这里是 names ,不是 name
}
]);
db.people.find();
db.people.find({
name: null
}); // v8.0 之后,只匹配 null 和 name 不存在的数据. 即 id = 1 and id = 4
// 删除 name 字段中包含 undefined 的文档
db.people.updateMany(
{
name: {
$type: "undefined"
}
},
[{
$set: {
"name": {
$cond: {
// When "name" is an array, convert { name: [ "Alice", undefined ] }
// to { name: [ "Alice" ] }
if : {
$eq: [{
$type: "$name"
}, "array"]
},
then: {
$filter: {
input: "$name",
cond: {
$not: {
$eq: [{
$type: "$$this"
}, "undefined"]
}
}
},
},
// When "name" is scalar undefined, remove it
else : "$$REMOVE"
}
}
}
}]
);
db.people.updateMany( // 批量更新
{ },
[ {
$replaceWith: { // 集合更新管道,替换文档的根节点, 接受一个文档作为参数
$arrayToObject: { // $arrayToObject + $filter 生成不包含 undefined 字段的文档
$filter: {
input: { $objectToArray: "$$ROOT" }, // 讲文档的每个字段转为键值对的数组形式
cond: {
$not: { $eq: [ { $type: "$$this.v" }, "undefined" ] } // 检查 this.v 是否为 undefined
}
}
}
}
} ]
)
db.people.updateMany(
{ name: { $type: "undefined" } },
[ {
$set: {
"name": {
$cond: {
// When "name" is an array, convert { name: [ "Alice", undefined ] }
// to { name: [ "Alice", null ] }
if: {
$eq: [ { $type: "$name" }, "array" ]
},
then: {
$map: {
input: "$name",
in: {
$cond: {
if: { $eq: [ { $type: "$$this" }, "undefined" ] },
then: null,
else: "$$this"
}
}
},
},
// When "name" is the scalar undefined, convert to null
else: null
}
}
}
} ]
)
8. insert
db.inventory.insertOne(
{
item: "canvas",
qty: 100,
tags: ["cotton"],
size: {
h: 28,
w: 35.5,
uom: "cm"
}
}
);
db.inventory.find();
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
{ item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
{ item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])
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" }
]);
9. select
// status = 'D'
db.inventory.find({
status: "D"
});
// status in ('A','D')
db.inventory.find({
status: {
$in: ["A", "D"]
}
});
// status = 'A' and qty < 30
db.inventory.find({
status: "A",
qty: {
$lt: 30
}
});
// status = 'A' or qty < 30
db.inventory.find({
$or: [{
status: "A"
}, {
qty: {
$lt: 30
}
}]
});
// status = 'A' and (qty < 30 or item like 'p%')
db.inventory.find({
status: "A",
$or: [{
qty: {
$lt: 30
}
}, {
item: /^p/
}]
})
// embedded 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" }
]);
db.inventory.find({
"size.uom": "in"
});
db.inventory.find({
"size.h": {
$lt: 15
}
});
// size.w = 21 and size.h = 14 and size.uom = 'cm'
db.inventory.find({
size: {
w: 21,
h: 14,
uom: "cm"
}
});
// arrays
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"]
});
// tags contains ["red","blank"]
db.inventory.find({
tags: {
$all: ["red", "blank"]
}
});
// 'red' in tags
db.inventory.find({
tags: "red"
});
// dim_cm 存在大于 25 的元素
db.inventory.find({
dim_cm: {
$gt: 25
}
});
// dim_cm 存在大于 15 小于 20 的元素
db.inventory.find({
dim_cm: {
$gt: 15,
$lt: 20
}
});
// dim_cm 的第 2 个元素大于 25
db.inventory.find({
"dim_cm.1": {
$gt: 25
}
});
// tags.length = 3
db.inventory.find({
"tags": {
$size: 3
}
});
// arrays of embeeded documents
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.warehouse = 'A' and instock.qty = 5 (需要顺序一致)
db.inventory.find({
"instock": {
warehouse: "A",
qty: 5
}
});
// instock.qty = 5 and instock.warehouse = 'A'
db.inventory.find({
"instock": {
qty: 5,
warehouse: "A"
}
});
// instock.qty <= 20
db.inventory.find({
'instock.qty': {
$lte: 20
}
});
// instock[0].qty <= 20
db.inventory.find({
'instock.0.qty': {
$lte: 20
}
});
// instock contains {qty: 5, warehouse: 'A'}
db.inventory.find({
"instock": {
$elemMatch: { // 不匹配顺序
qty: 5,
warehouse: "A"
}
}
});
// instock contains { 10 < qty <= 20 }
db.inventory.find({
"instock": {
$elemMatch: {
qty: {
$gt: 10,
$lte: 20
}
}
}
});
// instock.qty = 5 and instock.warehouse = 'A'
db.inventory.find({
"instock.qty": 5,
"instock.warehouse": "A"
})
指定返回字段
// project results
db.inventory.insertMany( [
{ item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
{ item: "notebook", status: "A", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
{ item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
{ item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
{ item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
// status = 'A'
db.inventory.find( { status: "A" } )
// select _id,item , status from inventory where status = 'A'
db.inventory.find( { status: "A" }, { item: 1, status: 1 } );
// select item , status from inventory where status = 'A'
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )
// 不显示 status instock 字段
db.inventory.find( { status: "A" }, { status: 0, instock: 0 } )
// 返回 item status size.uom 字段
db.inventory.find(
{
status: "A"
},
{
item: 1,
status: 1,
"size.uom": 1
}
)
db.inventory.find({
status: "A"
});
// 返回 item status instock.qty 字段
db.inventory.find({
status: "A"
}, {
item: 1,
status: 1,
"instock.qty": 1
});
分支条件
db.inventory.find(
{},
{
_id: 0,
item: 1,
status: {
$switch: { // 分支条件 $switch + branches + default 一起使用
branches: [
{
case: {
$eq: ["$status", "A"]
},
then: "Available"
},
{
case: {
$eq: ["$status", "D"]
},
then: "Discontinued"
},
],
default: "No status found"
}
},
area: { // $size.h * $size.w + " " + $size.uom
$concat: [
{
$toString: {
$multiply: ["$size.h", "$size.w"]
}
},
" ",
"$size.uom"
]
},
reportNumber: {
$literal: 1 // $literal 是一个特殊运算符,用于返回一个常量值(即字面值),它的作用是将提供的值直接作为结果输出,而不会被解释为字段或表达式。
}
}
);
10. $literal
$literal
是一个特殊运算符,用于返回一个常量值(即字面值),它的作用是将提供的值直接作为结果输出,而不会被解释为字段或表达式。
db.inventory.aggregate([
{
$project:
{
aa: {
$literal: 1
}
}
}
]);
11. $exist
查找不存在 item 的字段
db.inventory.find({
item: {
$exists: false
}
});
12. $currentDate
将字段的值设置为当前日期。
db.customers.updateOne(
{ _id: 1 },
{
$currentDate: {
lastModified: true, // 将 lastModified 字段修改为当前时间
"cancellation.date": { $type: "timestamp" } // 将 cancellation.date 修改为当前时间戳
},
$set: {
"cancellation.reason": "user request", // 将 cancellation.reason 修改为 user request
status: "D" // 将 status 修改为 D
}
}
);
聚合方式
聚合变量需要使用$$
,并且使用双引号。eg: "$$NOW"
db.customers.updateOne(
{
_id: 1
},
[
{
$set: {
lastModified: "$$NOW", // 聚合变量需要使用 $$ ,并且使用双引号。
cancellation: {
date: "$$CLUSTER_TIME", // 时间戳
reason: "user request"
},
status: "D"
}
}
]
);
13. 更新文档
- updateOne: 修改单个文档
db.inventory.updateOne(
{ item: "paper" },
{
$set: { "size.uom": "cm", status: "P" },
$currentDate: { lastModified: true }
}
)
- updateMany: 修改多个文档
db.inventory.updateMany(
{ "qty": { $lt: 50 } },
{
$set: { "size.uom": "in", status: "P" },
$currentDate: { lastModified: true }
}
)
- replaceOne: 替换
_id
字段之外的整个文档内容。
db.inventory.replaceOne(
{ item: "paper" },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)
14. $set
db.students.updateOne({
_id: 3
}, [{
$set: {
"test3": 98,
modified: "$$NOW"
}
}]);
15. $mergeObjects
$mergeObjects
合并多个对象为一个对象,相同的字段以最后一个对象为准。
db.students2.updateMany({}, [
{
$replaceRoot: {
// 新增一个文档,因为第一个参数是 {}
newRoot:
{
$mergeObjects: [{ // $mergeObjects 合并多个对象为一个对象,相同的字段以最后一个对象为准。
quiz1: 0,
quiz2: 0,
test1: 0,
test2: 0
}, "$$ROOT"]
}
}
},
{
$set: {
modified: "$$NOW"
}
}
]);
16. $replaceRoot
用于替换文档的根内容,即完全替换当前文档的顶层结构为一个新的结构。
17. $trunc
$trunc
是一个算术运算符,用于截断数字的小数部分,保留指定位数小数。不会四舍五入
db.students3.updateMany(
{}, // 会匹配到所有字段
[
{
$set: {
average: { // 新增一个 average 字段
$trunc: [{ // 截断小数,保留指定位数,此处保留 0 位小数
$avg: "$tests"
}, 0]
},
modified: "$$NOW"
}
},
{
$set: {
grade: {
$switch: {
branches: [
{
case: { // >= 90 A
$gte: ["$average", 90]
},
then: "A"
},
{
case: { // >= 80 B
$gte: ["$average", 80]
},
then: "B"
},
{
case: { // >= 70 C
$gte: ["$average", 70]
},
then: "C"
},
{
case: { // >= 60 D
$gte: ["$average", 60]
},
then: "D"
}
],
default: "F"
}
}
}
}
]
)
18. $concatArrays
$concatArrays
是一个用于将两个或多个数组连接在一起的操作符。
db.students4.updateOne({
_id: 2
}, [{
$set: {
quizzes: {
$concatArrays: ["$quizzes", [8, 6]]
}
}
}]);
19. $map
{ $map: { input: <expression>, as: <string>, in: <expression> } }
20. 删除文档
db.inventory.find();
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: "P" },
{ 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" },
] );
db.inventory.deleteMany({}); // 删除集合中所有文档
db.inventory.deleteMany({ status : "A" }) // 删除 status = 'A'
db.inventory.deleteOne( { status: "D" } ) // 删除 status ='D' 的第一个文档
// 1. 不会丢弃索引
// 2. 单个文档的操作是原子性的
// 3. write concern
21. Write Concern 写入确认级别
写操作的确认策略和数据持久化级别的一组配置。
w
: 定义需要确认写入的节点数量或策略。
- 开发和测试环境
w:0
或w:1
- 一般业务系统:
w: majority
或w: 2~3
- 金融系统:
w: majority and j: true
会显著的降低写操作的性能,需要权衡写入速度和可靠性。
j
:是否等待数据持久化到磁盘 (journal 文件)
wtimeoutMS
:等待写操作确认的超时时间。
db.collection.insertOne(
{ name: "Alice", age: 25 },
{ writeConcern: { w: "majority", j: true } }
);
majority
- 写入数据必须被直接点持久化到内存中。
- 被副本集中大多数节点确认完成 (
N/2 + 1
)- 副本集有 3 个节点, majority = 2
- 副本集有 5 个节点, majority = 3
22. Bulk Write
try {
db.pizzas.bulkWrite( [
{ insertOne: { document: { _id: 3, type: "beef", size: "medium", price: 6 } } },
{ insertOne: { document: { _id: 4, type: "sausage", size: "large", price: 10 } } },
{ updateOne: {
filter: { type: "cheese" },
update: { $set: { price: 8 } }
} },
{ deleteOne: { filter: { type: "pepperoni"} } },
{ replaceOne: {
filter: { type: "vegan" },
replacement: { type: "tofu", size: "small", price: 4 }
} }
] )
} catch( error ) {
print( error )
}
Ordered,默认 true
有序:其中一个写操作失败,剩余的写操作不会执行。
无序:其中一个写操作时发生错误,MongoDB 会继续处理列表中剩余的写操作。
db.collection.bulkWrite(
[
{ insertOne : <document> },
{ updateOne : <document> },
{ updateMany : <document> },
{ replaceOne : <document> },
{ deleteOne : <document> },
{ deleteMany : <document> }
],
{ ordered : false } // 默认有序。
)
分片集合的批量插入
- 预分割集合 Pre-Split the Collection
- 无序写入到 mongos Unordered Writes to mongos
- 避免单调 Avoid Monotonic Throttling
23. Retryable Writes 可重试写入
mongosh --retryWrites=true
Prerequisites:
- 需要副本集或分片集群,不支持独立实例。
- 需要支持文档级锁定的存储引擎,例如 WiredTiger 或内存存储引擎。
- MongoDB 3.6+
- Write Concern 的
w
不为0
,0
表示不重试
24. Retryable Reads 可重试读取
MongoDB Server 4.2+
25. Geospatial Queries 地理空间搜索
<field>: { type: <GeoJSON type> , coordinates: <coordinates> }
先 longitude
经度(-180,180) ,后 latitude
维度(-90,90)。
location: {
type: "Point",
coordinates: [-73.856077, 40.848447]
}
Legacy Coordinate Paris 传统坐标对
- 数组指定:
<field>: [ <x>, <y> ]
- 文档嵌入:
<field>: { <field1>: <x>, <field2>: <y> }
26. Read Concern
readConcern
选项,可以控制从 replica sets
和 sharded clusters
读取的数据的 一致性 consistency 和 隔离性
isolation。
在执行 find
或者 aggregate
时,可以通过 readConcern
来把控返回数据的准确性和可靠性。
Read Concern Levels 读关注级别
read concern
决定了查询返回的数据的一致性级别。它指定了查询应返回的数据版本。
MongoDB 支持的 Read Concern 级别
local
(default)available
majority
linearizable
snapshot
causally consistent sessions 因果一致性会话
Causal Consistency 是一种保证,指在启用因果一致性的情况下,读操作会确保在逻辑上尊重因果顺序。
在发生相关的操作时,Causal Consistency 能确保:如果有依赖关系,读操作不会早于依赖的写操作完成。
MongoDB 默认不开启因果一致性,但可以通过 API 显式启用。
eg: 如果 A 写入了数据,随后 B 读取了数据,在因果一致性中,B 应该总是能看到 A 的写操作结果,而不会读到那个写入之前的快照。
local
readConcern
的默认级别。
local
仅从主节点读取本地数据,这些数据已经过一些基本的同步,且不会读取到孤立文档。
local
: 返回实例中的数据,不保证返回的数据被写入大多数副本集成员。(返回的数据可能会被回滚)
使用前提(任何情况)
- 单独使用
- 开启了因果一致性会话
causally consistent session
- 开启了事务
transactions
available
available
适用于对实时场景非常高的情况。
使用前提:
- 没有开启
causally consistent session
和transactions
的会话。 (即无法在causally consistent session
和transactions
的会话中使用) - 仅适用于
sharded clusters
对于 sharded clusters
, available 提供了最低延迟的读取。
- 不需要等下多少票确认
- 直接从当前在线节点返回数据
- 读取逻辑简单,无需进行额外的状态检查或锁定
majority
对于与多文档事务无关的读取操作, majority
读取的数据已被副本集的大多数成员确认。(读取的文档是持久的,并保证不会被回滚)