MongoDB

文章目录

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中,你可以使用insertinsertOne命令插入新的文档。以下是一个简单的插入文档的命令和示例:

// 使用insertOne插入单个文档
db.mycollection.insertOne({
  name: "John Doe",
  age: 30,
  city: "New York"
});

这将在mycollection集合中插入一个新的文档。

2. 查询文档

使用find命令可以执行基本的查询操作。以下是一个简单的查询文档的命令和示例:

// 查询年龄为30的文档
db.mycollection.find({ age: 30 });

这将返回所有年龄为30的文档。

3. 更新文档

使用updateOneupdateMany命令可以更新文档的值。以下是一个基本的文档更新命令和示例:

// 更新名字为"John Doe"的文档,将年龄增加1
db.mycollection.updateOne(
  { name: "John Doe" },
  { $inc: { age: 1 } }
);

这将更新名字为"John Doe"的文档,将其年龄加1。

4. 删除文档

使用deleteOnedeleteMany命令可以删除文档。以下是一个删除文档的基本命令和示例:

// 删除名字为"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可以直接定位并检索符合条件的记录,而不需要扫描整个集合。这大大提高了查询性能,特别是在数据量庞大时。

说明
  1. 性能提升: 使用索引的查询通常比没有索引的查询更快,因为它允许数据库系统直接跳到可能包含符合条件的记录的地方。

  2. 资源消耗减少: 索引减少了需要扫描的数据量,从而减少了查询所需的系统资源。

  3. 适当选择索引字段: 在创建索引时,选择频繁用于查询的字段是重要的。在上述例子中,我们选择了age字段,但在实际应用中,选择合适的字段取决于数据的查询模式。

请注意,虽然索引提高了查询性能,但在某些情况下,它可能会导致写入性能略有下降。因此,在选择创建索引时,需根据应用的读写比例和查询模式进行权衡。

聚合

MongoDB的聚合框架是一个强大的工具,用于处理和分析文档数据。聚合框架提供了一系列的阶段,每个阶段都执行特定的操作,从而构建一个处理管道。这个管道包含了数据的转换和分析过程,最终生成所需的结果。下面是一些聚合框架的核心概念和一些常见的操作阶段:

聚合框架的核心概念:

  1. 聚合管道(Aggregation Pipeline):

    聚合操作是通过一个由多个阶段组成的管道来完成的。每个阶段都负责一部分处理工作,并将处理结果传递给下一个阶段。这些阶段按顺序执行,形成了一个聚合管道。

  2. 聚合阶段(Aggregation Stage):

    聚合管道由一个或多个聚合阶段组成。每个阶段代表一个特定的操作,例如过滤、投影、分组、排序等。常见的聚合阶段包括 $match$project$group$sort等。

  3. 表达式和操作符:

    在聚合框架中,可以使用各种表达式和操作符对数据进行处理。这包括数学操作符、逻辑操作符、日期操作符等,用于执行各种计算和转换操作。

    当在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提供了丰富的操作符和表达式,可以满足各种数据处理需求。具体的使用可以根据具体的数据结构和分析任务来选择和应用。

  4. 聚合函数: MongoDB提供了许多内置的聚合函数,如 $sum$avg$min$max 等,用于在聚合操作中执行计算。

常见的聚合阶段:

  1. 下面是每个聚合阶段的示例:

    当然!让我们为每个聚合阶段添加中文注释和说明:

    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的聚合框架可以充分利用数据库的索引和优化机制,执行效率较高,特别适用于大规模数据的处理。手动处理数据可能导致性能问题,特别是在处理大规模数据时,无法充分利用数据库的优化机制。
可扩展性聚合操作的管道可以根据需要灵活扩展,适用于处理各种不同的数据分析和转换需求。在一些简单的数据处理场景下,手动处理数据可能更简单和易于实现。
劣势
学习曲线对于初学者来说,可能需要一些时间来学习和理解聚合框架的操作符和用法。手动处理数据的代码可能更直观,开发者可以更清晰地了解和控制数据处理的过程。
可读性对于复杂的聚合管道,代码可能变得难以理解和调试,可读性相对较低。复杂的数据处理需求可能需要编写大量手动代码,增加了代码复杂性和维护成本。

上表总结了使用聚合框架和不使用聚合框架的优缺点。具体选择应根据项目需求、数据规模、开发者熟悉度和性能要求来决定。在实践中,通常会根据具体场景选择最适合的方法。

MapReduce

GridFS

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值