文章目录
- MongonDB笔记
- 1. 概述
- 2. 基本概念
- 3. 数据库与集合的基本操作
- 4. 文档的基本操作
- 游标
- 索引
- 聚合
- 聚合框架的核心概念:
- 常见的聚合阶段:
- 1. `$project` 阶段:
- 2. `$match` 阶段:
- 3. `$limit` 阶段:
- 4. `$skip` 阶段:
- 5. `$unwind` 阶段:
- 6. `$group` 阶段:
- 7. `$sort` 阶段:
- 8. `$geoNear` 阶段:
- 示例:
- MapReduce
- GridFS
MongonDB笔记
1. 概述
MongoDB是一种面向文档的NoSQL数据库,旨在提供高性能、高扩展性和灵活性。与传统的关系型数据库不同,MongoDB的数据存储形式是BSON(Binary JSON)格式的文档,这使得它更适合处理大量不同结构的数据。
2. 基本概念
数据库(Database)
a. 切换数据库
use mydatabase; // 切换到或创建名为mydatabase的数据库
b. 显示当前数据库
db; // 显示当前数据库
c. 显示所有数据库
show dbs; // 显示所有数据库
d. 删除当前数据库
db.dropDatabase(); // 删除当前数据库
集合(Collection)
集合是MongoDB中存储文档的地方,类似于关系数据库中的表。集合不要求预定义模式,即文档可以包含不同的字段。你可以使用createCollection
命令创建集合,例如:
a. 创建集合
db.createCollection("mycollection"); // 创建名为mycollection的集合
b. 显示当前数据库的所有集合
show collections; // 显示当前数据库的所有集合
c. 删除集合
db.mycollection.drop(); // 删除名为mycollection的集合
基本操作符
以下列出了一些常见的MongoDB操作符,以及它们的解释和使用场景:
操作符 | 语法示例 | 解释和使用场景 |
---|---|---|
$eq | { field: { $eq: value } } | 匹配与指定值相等的值。 |
$ne | { field: { $ne: value } } | 匹配与指定值不相等的值。 |
$gt | { field: { $gt: value } } | 匹配大于指定值的值。 |
$lt | { field: { $lt: value } } | 匹配小于指定值的值。 |
$gte | { field: { $gte: value } } | 匹配大于或等于指定值的值。 |
$lte | { field: { $lte: value } } | 匹配小于或等于指定值的值。 |
$in | { field: { $in: [value1, value2, ...] } } | 匹配数组中包含在指定数组中的任何值。 |
$nin | { field: { $nin: [value1, value2, ...] } } | 匹配不在指定数组中的值。 |
$exists | { field: { $exists: true/false } } | 匹配包含或不包含指定字段的文档。 |
$type | { field: { $type: type } } | 匹配字段值为指定BSON类型的文档。 |
$regex | { field: { $regex: /pattern/ } } | 在字段上执行正则表达式匹配。 |
$elemMatch | { field: { $elemMatch: { subfield: value } } } | 匹配包含至少一个元素符合所有指定条件的数组字段的文档。 |
$all | { field: { $all: [value1, value2, ...] } } | 匹配包含所有指定值的数组。 |
$size | { field: { $size: value } } | 匹配具有特定元素数量的数组。 |
$text | { $text: { $search: "keyword" } } | 在具有文本索引的字段上执行文本搜索。 |
$where | { $where: function() { /* JavaScript code */ } } | 基于JavaScript表达式匹配文档。 |
这个表格提供了这些操作符的简要概述,但详细信息和使用案例建议查阅MongoDB文档。
3. 数据库与集合的基本操作
a. 显示所有数据库和集合
show dbs; // 显示所有数据库
show collections; // 显示当前数据库的所有集合
b. 删除数据库和集合
use mydatabase; // 切换到要删除的数据库
db.dropDatabase(); // 删除当前数据库
db.mycollection.drop(); // 删除集合mycollection
4. 文档的基本操作
当然,让我们逐个讲解MongoDB文档操作的基本知识。
1. 插入文档
在MongoDB中,你可以使用insert
或insertOne
命令插入新的文档。以下是一个简单的插入文档的命令和示例:
// 使用insertOne插入单个文档
db.mycollection.insertOne({
name: "John Doe",
age: 30,
city: "New York"
});
这将在mycollection
集合中插入一个新的文档。
2. 查询文档
使用find
命令可以执行基本的查询操作。以下是一个简单的查询文档的命令和示例:
// 查询年龄为30的文档
db.mycollection.find({ age: 30 });
这将返回所有年龄为30的文档。
3. 更新文档
使用updateOne
或updateMany
命令可以更新文档的值。以下是一个基本的文档更新命令和示例:
// 更新名字为"John Doe"的文档,将年龄增加1
db.mycollection.updateOne(
{ name: "John Doe" },
{ $inc: { age: 1 } }
);
这将更新名字为"John Doe"的文档,将其年龄加1。
4. 删除文档
使用deleteOne
或deleteMany
命令可以删除文档。以下是一个删除文档的基本命令和示例:
// 删除名字为"John Doe"的文档
db.mycollection.deleteOne({ name: "John Doe" });
5. 嵌套文档
MongoDB支持嵌套文档,你可以在文档中嵌套其他文档。以下是一个简单的嵌套文档的插入、查询和更新命令的示例:
// 插入带有嵌套文档的文档
db.mycollection.insertOne({
name: "Alice",
contact: {
email: "alice@example.com",
phone: "123-456-7890"
}
});
// 查询具有特定嵌套文档的文档
db.mycollection.find({ "contact.email": "alice@example.com" });
// 更新嵌套文档的值
db.mycollection.updateOne(
{ name: "Alice" },
{ $set: { "contact.phone": "987-654-3210" } }
);
6. 文档字段的数据类型
MongoDB支持多种文档字段的数据类型,包括字符串、数字、日期、数组等。MongoDB会根据字段值的类型进行自动转换。详细的数据类型可以参考MongoDB文档。
7. 限制返回字段
在查询中,使用project
操作符限制MongoDB只返回文档的特定字段。以下是一个例子:
// 查询只返回name字段的文档
db.mycollection.find({}, { name: 1, _id: 0 });
这将返回所有文档,但只包含name字段。
8. 排序文档
使用sort
命令可以对查询结果进行排序。以下是一个排序文档的例子:
// 按年龄升序排序文档
db.mycollection.find().sort({ age: 1 });
这将返回按年龄升序排序的所有文档。
9. 查询操作符
a. 等于
db.mycollection.find({ field: "value" });
b. 大于/小于
db.mycollection.find({ age: { $gt: 25 } }); // 大于25岁的文档
db.mycollection.find({ age: { $lt: 30 } }); // 小于30岁的文档
c. 包含/不包含
db.mycollection.find({ tags: { $in: ["tag1", "tag2"] } }); // 包含tag1或tag2的文档
db.mycollection.find({ tags: { $nin: ["tag3", "tag4"] } }); // 不包含tag3和tag4的文档
游标
游标是什么?
在MongoDB中,游标是一种用于遍历查询结果集的指针。当执行查询时,MongoDB返回一个包含查询结果的游标对象。这个游标允许你逐步获取查询结果,遍历文档集合。
作用:
- 游标允许按需获取查询结果,而不是一次性返回所有文档。
- 通过使用游标,可以有效地处理大量数据,减少内存占用。
- 游标支持对查询结果进行排序、限制返回数量、跳过文档等操作,提供了灵活性。
1. 获取游标
// 执行查询,获取游标
var cursor = db.mycollection.find({ age: { $gt: 25 } });
// 使用游标遍历文档
while (cursor.hasNext()) {
printjson(cursor.next());
}
2. 遍历游标
// 执行查询,获取游标
var cursor = db.mycollection.find({ city: "New York" });
// 使用游标遍历文档
cursor.forEach(function(doc) {
printjson(doc);
});
3. 限制返回结果数量
// 执行查询,限制返回结果数量为5
var cursor = db.mycollection.find().limit(5);
// 遍历文档
while (cursor.hasNext()) {
printjson(cursor.next());
}
4. 跳过文档
// 执行查询,跳过前3个文档
var cursor = db.mycollection.find().skip(3);
// 遍历文档
while (cursor.hasNext()) {
printjson(cursor.next());
}
5. 获取部分字段
// 执行查询,只返回name字段
var cursor = db.mycollection.find({}, { name: 1, _id: 0 });
// 遍历文档
while (cursor.hasNext()) {
printjson(cursor.next());
}
6. 排序游标
// 执行查询,按年龄升序排序
var cursor = db.mycollection.find().sort({ age: 1 });
// 遍历文档
while (cursor.hasNext()) {
printjson(cursor.next());
}
7. 批量处理文档
// 执行查询,获取游标
var cursor = db.mycollection.find({ status: "active" });
// 批量更新文档
while (cursor.hasNext()) {
var doc = cursor.next();
doc.processed = true;
db.mycollection.save(doc);
}
8. 关闭游标
// 执行查询,获取游标
var cursor = db.mycollection.find({});
// 遍历文档
while (cursor.hasNext()) {
printjson(cursor.next());
}
// 关闭游标
cursor.close();
索引
1. 索引的基本概念
MongoDB索引是一种数据结构,用于提高数据库查询的性能。它类似于书籍目录,可以帮助数据库系统更快地找到存储在数据库中的数据。索引基于一个或多个字段,通过按照这些字段的值组织数据,减少了数据库的扫描时间。
2. 创建索引
在MongoDB中,你可以使用createIndex
命令来创建索引。以下是一些基本的创建索引的命令和语法示例:
// 在名为mycollection的集合上为"name"字段创建升序索引
db.mycollection.createIndex({ name: 1 });
// 在"name"和"age"字段上创建一个复合索引
db.mycollection.createIndex({ name: 1, age: -1 });
3. 查询索引
你可以使用getIndexes
方法来查询MongoDB中已存在的索引。例如:
// 显示mycollection集合的所有索引
db.mycollection.getIndexes();
4. 删除索引
使用dropIndex
命令可以删除MongoDB集合中的索引。注意,你需要知道要删除的索引的名称。示例:
// 删除mycollection集合中名为"name_1"的索引
db.mycollection.dropIndex("name_1");
5. 复合索引
复合索引是基于多个字段的索引。创建复合索引时,字段的顺序很重要,因为MongoDB将按照索引的字段顺序进行排序。示例:
// 在"name"和"age"字段上创建一个复合索引
db.mycollection.createIndex({ name: 1, age: -1 });
6. 查询优化
MongoDB索引可以显著提高查询性能。确保在经常查询的字段上创建索引,并避免全表扫描。了解数据的访问模式,并根据实际需求创建合适的索引。
7. 文本索引
MongoDB支持文本索引,允许你在文本字段上进行全文搜索。创建文本索引的方法如下:
// 在"text"字段上创建文本索引
db.mycollection.createIndex({ text: "text" });
8. 索引的种类
MongoDB支持多种类型的索引,包括单字段索引、复合索引、文本索引、哈希索引等。每种索引类型有其特殊的用途和优势。例如,文本索引适用于全文搜索,而哈希索引适用于均匀分布的查询。选择适当的索引类型取决于查询模式和数据特点。
9.例子
假设有一个名为employees
的集合,其中包含了大量员工记录。我们想要查找年龄为25岁的员工记录。首先,我们会演示没有索引的情况:
没有索引的查询
// 普通查询,没有使用索引
db.employees.find({ age: 25 });
在没有索引的情况下,MongoDB会对整个集合进行全表扫描,逐一检查每一行以找到符合条件的记录。这对于大型数据集合可能会很耗时。
现在,让我们使用索引来优化这个查询:
使用索引的查询
首先,我们为age
字段创建一个升序索引:
// 在"age"字段上创建升序索引
db.employees.createIndex({ age: 1 });
接下来,我们进行查询:
// 使用索引进行查询
db.employees.find({ age: 25 });
由于我们创建了age
字段的索引,MongoDB可以直接定位并检索符合条件的记录,而不需要扫描整个集合。这大大提高了查询性能,特别是在数据量庞大时。
说明
-
性能提升: 使用索引的查询通常比没有索引的查询更快,因为它允许数据库系统直接跳到可能包含符合条件的记录的地方。
-
资源消耗减少: 索引减少了需要扫描的数据量,从而减少了查询所需的系统资源。
-
适当选择索引字段: 在创建索引时,选择频繁用于查询的字段是重要的。在上述例子中,我们选择了
age
字段,但在实际应用中,选择合适的字段取决于数据的查询模式。
请注意,虽然索引提高了查询性能,但在某些情况下,它可能会导致写入性能略有下降。因此,在选择创建索引时,需根据应用的读写比例和查询模式进行权衡。
聚合
MongoDB的聚合框架是一个强大的工具,用于处理和分析文档数据。聚合框架提供了一系列的阶段,每个阶段都执行特定的操作,从而构建一个处理管道。这个管道包含了数据的转换和分析过程,最终生成所需的结果。下面是一些聚合框架的核心概念和一些常见的操作阶段:
聚合框架的核心概念:
-
聚合管道(Aggregation Pipeline):
聚合操作是通过一个由多个阶段组成的管道来完成的。每个阶段都负责一部分处理工作,并将处理结果传递给下一个阶段。这些阶段按顺序执行,形成了一个聚合管道。
-
聚合阶段(Aggregation Stage):
聚合管道由一个或多个聚合阶段组成。每个阶段代表一个特定的操作,例如过滤、投影、分组、排序等。常见的聚合阶段包括
$match
、$project
、$group
、$sort
等。 -
表达式和操作符:
在聚合框架中,可以使用各种表达式和操作符对数据进行处理。这包括数学操作符、逻辑操作符、日期操作符等,用于执行各种计算和转换操作。
当在MongoDB的聚合框架中使用表达式和操作符时,可以进行各种计算和转换操作,以便更灵活地处理数据。以下是一些例子:
1. 数学操作符:
$add
:将两个数相加{ $add: ["$price", "$discount"] }
$subtract
:计算两个数的差{ $subtract: ["$total_sales", "$returns"] }
$multiply
:计算两个数的乘积{ $multiply: ["$quantity", "$unit_price"] }
$divide
:计算两个数的商{ $divide: ["$total_revenue", "$units_sold"] }
2. 逻辑操作符:
$and
:逻辑与{ $and: [{ condition1: true }, { condition2: false }] }
$or
:逻辑或{ $or: [{ condition1: true }, { condition2: false }] }
$not
:逻辑非{ $not: { condition: true } }
3. 日期操作符:
$year
:提取日期的年份{ $year: "$order_date" }
$month
:提取日期的月份{ $month: "$order_date" }
$dayOfMonth
:提取日期的日{ $dayOfMonth: "$order_date" }
这些操作符可以嵌套在聚合管道的不同阶段中,根据实际需求组合使用。例如,你可以在
$project
阶段中使用数学操作符计算新字段,或者在$match
阶段中使用逻辑操作符进行筛选,还可以在$group
阶段中使用日期操作符进行按日期的分组统计。请注意,这些只是一小部分可用操作符的例子,MongoDB提供了丰富的操作符和表达式,可以满足各种数据处理需求。具体的使用可以根据具体的数据结构和分析任务来选择和应用。
-
聚合函数: MongoDB提供了许多内置的聚合函数,如
$sum
、$avg
、$min
、$max
等,用于在聚合操作中执行计算。
常见的聚合阶段:
-
下面是每个聚合阶段的示例:
当然!让我们为每个聚合阶段添加中文注释和说明:
1.
$project
阶段:db.orders.aggregate([ // 投影一个新的文档,包括指定的字段和计算结果 { $project: { _id: 0, 产品名称: "$product", 数量: 1, 总价: { $multiply: ["$quantity", "$price"] } } } ])
- 这个阶段通过投影指定的字段(
产品名称
、数量
)和计算一个新的字段总价
,实现了创建一个新文档的目的。
2.
$match
阶段:db.orders.aggregate([ // 根据指定条件过滤文档 { $match: { 状态: "completed", 总金额: { $gte: 1000 } } } ])
- 这个阶段通过过滤条件,只包括
状态
为 “completed” 且总金额
大于或等于 1000 的文档。
3.
$limit
阶段:db.orders.aggregate([ // 限制结果集中的文档数量 { $limit: 10 } ])
- 这个阶段将结果集限制为仅包含前 10 个文档。
4.
$skip
阶段:db.orders.aggregate([ // 在结果集中跳过指定数量的文档 { $skip: 5 } ])
- 这个阶段跳过结果集中的前 5 个文档。
5.
$unwind
阶段:db.orders.aggregate([ // 如果订单文档包含多个产品,将产品数组展开成多个文档 { $unwind: "$products" } ])
- 假设订单文档包含一个
products
数组,该阶段会生成多个文档,每个文档包含一个products
数组中的元素。
6.
$group
阶段:db.orders.aggregate([ // 根据指定字段分组文档,并计算总金额 { $group: { _id: "$customer_id", 总金额: { $sum: "$total_amount" } } } ])
- 这个阶段根据
customer_id
字段对文档进行分组,并计算每个分组的总金额。
7.
$sort
阶段:db.orders.aggregate([ // 根据指定字段降序排序结果集 { $sort: { total_amount: -1 } } ])
- 这个阶段根据
total_amount
字段降序排序结果集。
8.
$geoNear
阶段:db.locations.aggregate([ // 找到离指定地理位置最近的文档,并添加一个距离字段 { $geoNear: { near: { type: "Point", coordinates: [40, -73] }, distanceField: "距离", spherical: true } } ])
- 假设有一个包含地理位置信息的集合,该阶段找到离指定坐标
[40, -73]
最近的文档,并添加一个新的字段距离
表示距离。选项spherical: true
表示计算考虑地球曲率。
- 这个阶段通过投影指定的字段(
示例:
假设我们有一个存储订单信息的集合,可以使用聚合框架来计算每个客户的总销售额:
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$customer_id", totalSales: { $sum: "$total_amount" } } },
{ $sort: { totalSales: -1 } }
])
这个聚合管道包括了 $match
、$group
和 $sort
阶段,它过滤出已完成的订单,按客户ID分组,计算每个客户的总销售额,并最终按销售额降序排序。
这是不使用聚合操作的代码
// 第一步:查询已完成的订单
const completedOrders = db.orders.find({ status: "completed" });
// 第二步:根据客户ID对订单进行分组,并计算每个客户的总销售额
const salesByCustomer = {};
completedOrders.forEach(order => {
const customerId = order.customer_id;
const totalAmount = order.total_amount;
if (!salesByCustomer[customerId]) {
salesByCustomer[customerId] = 0;
}
salesByCustomer[customerId] += totalAmount;
});
// 第三步:将结果按总销售额降序排序
const sortedSales = Object.entries(salesByCustomer)
.sort(([, totalSales1], [, totalSales2]) => totalSales2 - totalSales1);
// 输出结果
console.log(sortedSales);
对比起来
优缺点 | 使用聚合框架 | 不使用聚合框架 |
---|---|---|
优势 | ||
简洁和高效 | 使用聚合操作可以在一个查询语句中完成多个数据处理步骤,提高了代码的简洁性和执行效率。 | 手动处理数据的代码可能更为直观,开发者可以更清晰地了解和控制数据处理的过程。 |
性能优化 | MongoDB的聚合框架可以充分利用数据库的索引和优化机制,执行效率较高,特别适用于大规模数据的处理。 | 手动处理数据可能导致性能问题,特别是在处理大规模数据时,无法充分利用数据库的优化机制。 |
可扩展性 | 聚合操作的管道可以根据需要灵活扩展,适用于处理各种不同的数据分析和转换需求。 | 在一些简单的数据处理场景下,手动处理数据可能更简单和易于实现。 |
劣势 | ||
学习曲线 | 对于初学者来说,可能需要一些时间来学习和理解聚合框架的操作符和用法。 | 手动处理数据的代码可能更直观,开发者可以更清晰地了解和控制数据处理的过程。 |
可读性 | 对于复杂的聚合管道,代码可能变得难以理解和调试,可读性相对较低。 | 复杂的数据处理需求可能需要编写大量手动代码,增加了代码复杂性和维护成本。 |
上表总结了使用聚合框架和不使用聚合框架的优缺点。具体选择应根据项目需求、数据规模、开发者熟悉度和性能要求来决定。在实践中,通常会根据具体场景选择最适合的方法。