掘金 后端 ( ) • 2024-04-18 09:56

Rust实践开发-20个高级MongoDB技术及实例,直接让你变成高级MongoDB工程师

大家好,梦兽编程。这次带来的是Rust操作MongoDB的系列教程,如果你想与我进行交流。可以微信搜索梦兽编程回复111即可与Rust的小伙伴一起谈论Web开发相关内容。

在之前的分享中,我们已经可以熟悉的掌握比且使用MongoDB进行简单的数据处理,但遇到一下复杂的情况,还是会感觉到非常吃力。今天梦兽将给大家带来20个**高级MongoDB技术及实例。**让大伙快速修仙路上一步登天。嘻嘻要个三连不过分吧?

聚合管道

聚合管道是 MongoDB 中的一项强大功能,允许您直接在数据库中执行复杂的数据转换和分析。它由一系列处理数据流中的文档的阶段组成,使您能够以各种方式过滤、分组、排序和重塑数据。

你可以理解成组合查询

我们看一个案例:

db.orders.aggregate([
  { $match: { status: "completed" } },
  { $unwind: "$items" },
  {
    $group: {
      _id: "$items.product_id",
      total_quantity: { $sum: "$items.quantity" },
      total_revenue: { $sum: { $multiply: ["$items.quantity", "$items.price"] } }
    }
  },
  { $sort: { total_revenue: -1 } },
  { $limit: 5 }
])
  • 「$match:目标是筛选出所有状态为"completed"的订单。」
  • **$unwind:**将每个订单中的items数组中的每个元素拆分成独立的文档。
  • 「$group」:按items.product_id对文档进行分组,并计算每个产品的总数量和总收入。
  • **$sort:**根据total_revenue字段的值降序排序文档。
  • 「$limit」:限制结果只返回总销售收入最高的前5个产品。

索引

索引是优化所有数据库查询性能的一个重要方面。通过在相关字段上创建适当的索引,您可以显着提高数据检索的速度并减少查询执行时间。


db.customers.createIndex({ email: 1 }, { unique: true })
db.orders.createIndex({ customer_id: 1, order_date: -1 })

在第一个示例中,我们在 customers 集合的 email 字段上创建唯一索引。该索引确保不允许出现重复的电子邮件地址,并能够根据电子邮件字段进行高效的查找和查询。

在第二个示例中,我们在 orders 集合的 customer_id 和 order_date 字段上创建复合索引。该索引可用于高效查询特定客户的订单,并按客户ID升序排序和订单日期降序排列,当进行这两个字段查询时,会大大缩小检索范围。

「地理空间查询」

MongoDB 为地理空间数据和查询提供本机支持,使您能够高效地存储和查询基于位置的数据。这对于处理地图、基于位置的服务或涉及地理坐标的任何数据的应用程序特别有用。

db.locations.createIndex({ coordinates: "2dsphere" })

db.locations.find({
  coordinates: {
    $nearSphere: {
      $geometry: {
        type: "Point",
        coordinates: [-73.935242, 40.730610]
      },
      $maxDistance: 5000
    }
  }
})

locations集合的coordinates字段上创建一个2dsphere类型的地理空间索引。

"2dsphere"索引类型适用于存储地理坐标(例如经纬度)并执行基于球面距离的查询。确认您的应用场景需要这种类型的索引,比如地图应用、位置服务等。

查询locations集合中距离指定点(经度-73.935242,纬度40.730610)5000米范围内的所有文档。

  • $nearSphere操作符用于执行基于球面【地球是一个球】距离的查询,它需要一个地理空间索引。确认coordinates字段已经按照"2dsphere"类型正确索引。
  • $geometry对象中的type字段指定了查询点的类型,这里是"Point",表示一个具体的地理位置点。确认coordinates数组中的值是正确的经纬度值。
  • $maxDistance字段用于限制查询结果的范围,这里设置为5000米。确认这个值符合您的查询需求。

全文搜索

MongoDB 的文本搜索功能允许您对字符串字段执行全文搜索,使您能够根据关键字或短语查找相关文档。

这个功能有点类似Mysql数据库中的link查询符合。

db.articles.createIndex({ content: "text" })
# 在articles集合的content字段上创建一个文本索引。

db.articles.find(
  { $text: { $search: "mongodb query optimization" } },
  { score: { $meta: "textScore" } }
)
.sort({ score: { $meta: "textScore" } })

查询articles集合中文本内容包含“mongodb query optimization”关键词的文档,并根据相关性对结果进行排序。

score字段在投影中使用{ $meta: "textScore" }来请求文档的相关性评分。通过显示相关性评分,用户可以了解哪些结果是最符合他们搜索意图的。这有助于提高用户对搜索功能的满意度和整体体验。「相关性评分是一个介于1到1000之间的数字,其中1表示最低相关性,1000表示最高相关性。」

更换流(「Change Streams」

更换流(「Change Streams」)不知道如何翻译,我就叫它更换流吧。MongoDB 中的一项强大功能,可让您监控集合中的实时数据更改并做出反应。这对于构建事件驱动的应用程序、实施实时分析或跨不同系统同步数据特别有用。

const pipeline = [
  { $match: { "operationType": { $in: ["insert", "update", "replace"] } } }
]

const changeStream = db.orders.watch(pipeline)

changeStream.on("change", next => {
  console.log("Change occurred:", next)
  // Perform actions based on the change event
})

当orders文档中的operationType字段发生新增,更新,替换操作时,我们可以使用类似javascript中的事件侦听器进行监听,去执行相关的事件操作。

变更流在实时订单跟踪、维护审核日志或跨分布式系统同步数据等场景中特别有用。

事务

早期的MongoDB是不支持事务,但在4.0的后已经支持这个特性。引入了对多文档事务的支持,提供了一种确保跨多个操作的数据完整性和一致性的方法。事务保证事务中的所有操作都被视为单个原子性,从而确保所有操作要么成功,要么全都不应用。

const session = db.getMongo().startSession()
# 需要先获取mongo的事务session对象

try {
  session.startTransaction()
  # 开启事务

  const orderId = db.orders.insertOne({ customer_id: 123, items: [...] }, { session })
  db.inventory.updateMany({ product_id: { $in: [...] } }, { $inc: { quantity: -1 } }, { session })

  session.commitTransaction()
  console.log("Transaction committed successfully. Order ID:", orderId.insertedId)
} catch (error) {
  session.abortTransaction()
  console.error("Transaction aborted due to error:", error)
}

# 再end之间过程可以做到院子操作
session.endSession()

分片

分片是 MongoDB 中的一种水平扩展(分布式)技术,允许您基于分片键将数据分布在多个分片(分区)上。这使您能够水平扩展数据库,随着数据的增长而提高其容量和性能。

sh.enableSharding("mydb")
# 启用对名为mydb的数据库的分片支持。
sh.shardCollection("mydb.orders", { order_date: 1, _id: 1 })
# 对mydb数据库中的orders集合进行分片,分片键定义为 order_date 和 _id 字段的复合索引,其中 order_date 为主分片键, _id 为辅助分片键。

通过此配置,MongoDB 将根据 order_date 字段将 orders 集合分布在多个分片上,每个分片包含一系列订单日期。 _id 字段被添加到分片键中,以确保具有相同 order_date 值的文档的唯一分布。

「上限集合」

上限集合是 MongoDB 中固定大小的集合,用于维护插入顺序并支持高吞吐量操作。它们对于日志记录、缓存或维护数据滚动窗口等场景很有用。

db.createCollection("logs", { capped: true, size: 1000000, max: 1000 })

db.logs.insert({ message: "Log entry 1", timestamp: new Date() })
db.logs.insert({ message: "Log entry 2", timestamp: new Date() })
// ...

一旦集合达到最大大小或文档限制,最旧的文档将被自动删除,以便为新插入腾出空间,从而维护最新日志条目的滚动窗口。

这也成为了MongoDB早期成为日志系统的由来,数据自由,还不用写一套定时任务去清除没用的日志文件。

部分索引

MongoDB 中的部分索引允许您根据指定的过滤条件在文档子集上创建索引。这对于优化对特定数据子集的查询并减少总体索引大小和维护开销非常有用。

db.orders.createIndex(
  { status: 1, order_date: -1 },
  { partialFilterExpression: { status: "completed" } }
)

该索引包括 status 和 order_date 字段,但仅适用于 status 字段等于“completed”的文档。此部分索引对于优化频繁过滤 status 字段的查询特别有用,因为索引将仅包含状态为“completed”的文档,从而减少索引大小并提高查询性能。

「Collations」

这在处理多语言数据或根据特定语言规则排序和比较字符串时尤其重要。

db.createCollection("products", {
  collation: {
    locale: "en_US",
    strength: 2
  }
})

db.products.insert({ name: "Café" })
db.products.insert({ name: "cafe" })

db.products.find().sort({ name: 1 })

locale 选项指定要使用的语言规则(在本例中为英语-美国), strength 选项确定比较级别(2表示不区分大小写的比较)。

当我们执行 db.products.find().sort({ name: 1 }) 时,文档将根据指定的排序规则以不区分大小写的顺序进行排序,确保字符串根据英语语言规则进行正确的比较和排序。

数据集验证

如果你是浏览器客户端开发者,JavaScript里有一个功能叫做表单校验,MongoDB基于类似JSON的数据格式,也提供了类似的功能。

db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email"],
      properties: {
        name: {
          bsonType: "string",
          description: "must be a string and is required"
        },
        email: {
          bsonType: "string",
          pattern: "@mongodb.com$",
          description: "must be a string matching the regular expression pattern and is required"
        },
        age: {
          bsonType: "int",
          minimum: 18,
          description: "must be an integer and greater than or equal to 18"
        }
      }
    }
  }
})

「GridFS」

GridFS是MongoDB中用于存储和检索大文件和二进制数据的规范。不过一般我们不会用,如果要搜索文件的话,我们一般不会使用数据库,而是转向使用文件服务器。

使用和一般的文件服务器差不多,也是有一个桶的概念,然后往桶里面存文件类型。

const bucket = new GridFSBucket(db, { bucketName: "photos" })

const metadata = {
  contentType: "image/jpeg",
  metadata: { description: "Photo of a sunset" }
}

const uploadStream = bucket.openUploadStream("sunset.jpg", metadata)

fs.createReadStream("/path/to/sunset.jpg").pipe(uploadStream)

uploadStream.on("finish", () => {
  console.log("File uploaded successfully!")
})

副本集

副本集是通过在不同服务器或机器上维护数据的多个副本(副本)来在 MongoDB 中实现高可用性和冗余的一种方法。如果主服务器发生故障,辅助副本之一可以自动被选为新的主服务器,从而确保对数据的不间断访问。

# Configure replica set members
rs.initiate({
  _id: "myReplicaSet",
  members: [
    { _id: 0, host: "host1:27017" },
    { _id: 1, host: "host2:27017" },
    { _id: 2, host: "host3:27017", arbiterOnly: true }
  ]
})

# Check replica set status
rs.status()

arbiterOnly: true:作为仲裁者节点,它不参与数据的复制和写操作,仅用于投票以支持副本集的读写操作。

「Read Preferences」

Read Preferences是MongoDB官方提供集群操作偏好,这对于优化读取性能、确保数据一致性或跨多个服务器分配读取负载非常有用。

const client = new MongoClient("mongodb://host1,host2,host3/?replicaSet=myReplicaSet")

client.readPreference("primary")
client.readPreference("secondary")
client.readPreference("nearest")

您可以根据你的业务选择本次操作要发送到那个节点中:

  • primary 读取操作都将发送到当前的主节点(primary)
  • secondary 所有的读取操作都将发送到当前的从节点(secondary)
  • nearest 客户端将根据服务器的网络延迟来选择最近的节点进行读取操作

「弹性搜索集成」

Elasticsearch提供了强大的搜索和聚合功能,如果你想使用将MongoDB中的文档用Elasticsearch进行查询,可以使用这个功能。

「在启用Elasticsearch集成之前,确保MongoDB和Elasticsearch集群之间的网络连接是可靠的。」

db.adminCommand({ setParameter: 1, internalElasticsearchClusterNodes: "host1:9200,host2:9200" })
# 在products集合上创建一个全文索引,该索引将对所有字段进行文本搜索优化,并为特定字段(name和description)设置不同的权重。
db.products.createIndex({ "$**": "text" }, { weights: { name: 10, description: 5 } })

db.products.find({
  $text: { $search: "mongodb database" }
}, {
  score: { $meta: "textScore" }
}).sort({ score: { $meta: "textScore" } })

给钱就是爷环境

以下服务都是MongoDB提供给企业用户的增值服务,了解一下即可。反正中国的抠抠索索的老板怎么可能给你买这种服务。

「在线存储」

MongoDB Atlas 是 MongoDB 的完全托管云服务,提供在线存档存储功能,允许您将历史数据从操作数据库卸载到单独的、经济高效的存储层。这有助于降低实时数据库的存储成本并提高性能,同时仍然允许您在需要时访问和查询存档数据。

use mydb
db.runCommand({
  createOnlineArchivingOps: "orders",
  startArchivingAfter: new ISODate("2022-01-01T00:00:00Z"),
  stopArchivingAfter: new ISODate("2023-01-01T00:00:00Z")
})

// Query the online archive
db.orders.aggregate([
  { $match: { order_date: { $gte: new ISODate("2022-01-01"), $lt: new ISODate("2023-01-01") } } },
  // ... additional pipeline stages
])

「MongoDB Charts」

MongoDB Charts 是 MongoDB Atlas 提供的完全托管的数据可视化服务。它允许您直接从 MongoDB 数据创建丰富的交互式图表和仪表板,无需单独的数据仓库或商业智能工具。

const datasource = {
  name: "Orders",
  databaseName: "mydb",
  collectionName: "orders",
  queryString: "{ $match: { status: 'completed' } }",
  queryFields: [ "customer_id", "total_amount", "order_date" ]
}

const chart = new Charts.ChartBuilder()
  .withDataSource(datasource)
  .withType("line")
  .withOptions({
    title: "Daily Revenue",
    dateField: "order_date",
    valueField: "total_amount",
    groupByFields: [ "customer_id" ],
    dateRange: {
      startDate: new ISODate("2023-01-01T00:00:00Z"),
      endDate: new ISODate("2023-03-31T23:59:59Z")
    }
  })
  .build()

// Render the chart
chart.render()

「MongoDB Realm」

MongoDB Realm 是一个无服务器平台,允许您使用 MongoDB Atlas 作为数据层来构建现代的数据驱动应用程序。它提供了一套用于构建、部署和管理云原生应用程序的服务和工具,包括函数、触发器、用户身份验证和数据同步。

exports = function(arg) {
  const orders = context.services.get("mongodb-atlas").db("mydb").collection("orders");
  const customer = arg.customer;

  return orders.find({ customer_id: customer }).toArray();
};

exports = function(changeEvent) {
  const orders = context.services.get("mongodb-atlas").db("mydb").collection("orders");
  const updatedOrder = changeEvent.updateDescription.updatedFields;

  if (updatedOrder.status === "completed") {
    console.log(`Order ${updatedOrder._id} has been completed.`);
  }
};

「MongoDB Atlas Data Lake MongoDB Atlas」

MongoDB Atlas Data Lake 是一项完全托管的服务,允许您在 MongoDB Atlas 集群之上创建数据湖。它提供了一个集中存储库,用于存储和分析结构化和非结构化数据,从而实现高级分析、机器学习和数据处理工作负载。

db.createCollection("logs", {
  timeseries: {
    timeField: "timestamp",
    metaField: "metadata",
    granularity: "seconds"
  },
  dataTier: "cloud.data_lake.preview"
})

db.logs.insertMany([
  { timestamp: new Date(), message: "Log entry 1", metadata: { source: "app1" } },
  { timestamp: new Date(), message: "Log entry 2", metadata: { source: "app2" } },
  // ...
])

db.logs.find({
  metadata: { source: "app1" },
  timestamp: { $gte: ISODate("2023-01-01T00:00:00Z"), $lt: ISODate("2023-02-01T00:00:00Z") }
})

请记住,学习和应用高级 MongoDB 技术是一个持续的旅程。随着新特性和功能的推出,必须了解最新的发展和最佳实践。拥抱 MongoDB 的强大功能,尝试这些先进技术,并继续探索新方法来充分利用这个多功能数据库的潜力。

感谢阅读,祝编程愉快!

如果您喜欢这个博客,请与可能觉得有用的其他人分享。你也可以跟上我,了解更多关于JavaScript、React、Rust、Golang、Next.js、MongoDB和Web开发的信息。

本文使用 markdown.com.cn 排版