Java研学-MongoDB(三)

发布于:2025-07-08 ⋅ 阅读:(15) ⋅ 点赞:(0)

三 文档相关

7 文档统计查询

  ① 语法:

// 精确统计文档数 慢 准
dahuang> db.xiaohuang.countDocuments({条件})
4
// 粗略统计文档数 快 大致准
dahuang> db.xiaohuang.estimatedDocumentCount({条件})
4

  ② 例子:

// 精确统计文档数 name为奔波儿灞
dahuang> db.xiaohuang.countDocuments({name:"奔波儿灞"})
2

8 文档分页查询

  ① 语法:skip() 通常用于跳过前面已经显示过的文档,以便获取后续的文档。limit() 用于指定每页显示的文档数量。

// 第一页(每页10条)
db.xiaohuang.find().limit(10)
 
// 第二页(跳过前10条)
db.xiaohuang.find().skip(10).limit(10)
 
// 第三页
db.xiaohuang.find().skip(20).limit(10)

// 对于非常大的集合,skip() 可能会很慢,考虑使用基于游标的分页方法

  ② 例子:

dahuang> db.xiaohuang.find().skip(2).limit(2)
[
  {
    _id: ObjectId('6854db7ff50f58c8c26c4bd2'),
    name: '奔波儿灞',
    job: 'CEO'
  },
  {
    _id: ObjectId('6854db7ff50f58c8c26c4bd3'),
    name: '灞波儿奔',
    job: 'CEO'
  }
]

9 文档排序查询

  ① 语法:sort()方法用于对查询结果进行排序。可指定一个或多个字段进行排序,并使用 1(升序)或 -1(降序)来定义排序方式。

db.xiaohuang.find().sort({userid: 1})  // 升序
db.xiaohuang.find().sort({userid: -1}) // 降序

  ② 例子:

// 首先对 userid 进行降序排列,然后对 job进行升序排列:
db.xiaohuang.find().sort({userid: -1, job: 1})

// 对userid降序排列,跳过前10条记录,然后返回接下来的10条记录
db.xiaohuang.find()
    .sort({userid: -1})
    .skip(10)
    .limit(10)

  注意:skip() limit() sort()一起执行时,执行顺序为,先sort()后skip()最后limit(),和命令编写顺序无关。

10 文档正则查询

  ① 语法:在//中书写正则表达式即可

db.xiaohuang.find({ field: /正则表达式/ })
// 或
db.xiaohuang.find({ name: { $regex: '奔' } })

  ② 例子:

// i:不区分大小写
// 不区分大小写查询"开水"
db.xiaohuang.find({ name: //i })

11 文档比较查询

  ① 语法:在//中书写正则表达式即可

// 大于
db.xiaohuang.find({ "field": { $gt: value } })  // field > value

// 小于
db.xiaohuang.find({ "field": { $lt: value } })  // field < value

// 大于等于
db.xiaohuang.find({ "field": { $gte: value } }) // field >= value

// 小于等于
db.xiaohuang.find({ "field": { $lte: value } }) // field <= value

// 不等于
db.xiaohuang.find({ "field": { $ne: value } })  // field != value

  ② 例子:

// 比较整数时,可以使用 NumberInt(700) 明确指定整数类型
// 查询评论点赞数量大于700的记录
db.xiaohuang.find({ "likenum": { $gt: NumberInt(700) } })

12 文档包含查询

  ① 语法:操作符的值必须是一个数组,即使只有一个元素。

// $in 操作符用于匹配字段值在指定数组中的文档。
db.xiaohuang.find({ userid: { $in: ["10086", "10010"] } })
// $nin 操作符用于匹配字段值不在指定数组中的文档。
db.xiaohuang.find({ userid: { $nin: ["10086", "10010"] } })
// $all:匹配包含数组中所有指定元素的文档。
db.xiaohuang.find({ tags: { $all: ["mongodb", "database"] } })
// $elemMatch:用于匹配数组元素满足多个条件的文档。
db.xiaohuang.find({ scores: { $elemMatch: { $gte: 80, $lt: 85 } } })

13 文档条件连接查询

  ① 语法:操作符的值必须是一个数组,即使只有一个元素。

// $and 操作符用于同时满足多个条件的查询(相当于 SQL 中的 AND)。
// 查询评论集合中 likenum 大于等于 700 并且小于 2000 的文档
db.xiaohuang.find({
  $and: [
    { likenum: { $gte: NumberInt(700) } },
    { likenum: { $lt: NumberInt(2000) } }
  ]
})

// $or 操作符用于满足任意一个条件的查询(相当于 SQL 中的 OR)。
// 查询评论集合中 userid 为 10010,或者点赞数小于 1000 的文档记录
db.xiaohuang.find({
  $or: [
    { userid: "10010" },
    { likenum: { $lt: 1000 } }
  ]
})

// MongoDB 会自动将多个条件组合为 $and 查询,因此显式使用 $and 通常是不必要的。
db.xiaohuang.find({
  likenum: { $gte: NumberInt(700), $lt: NumberInt(2000) }
})

// $nor:匹配不满足任何指定条件的文档。
db.xiaohuang.find({
  $nor: [
    { price: 1.99 },
    { sale: true }
  ]
})

// $not:匹配不满足指定条件的文档。
db.xiaohuang.find({
  price: { $not: { $gt: 1.99 } }
})

四 索引相关

1 介绍

  在 MongoDB 中,索引是提高查询效率的关键工具。如果没有索引,MongoDB 在处理查询时会进行全集合扫描,就像是在一本厚厚的电话簿中逐页翻找特定信息,这种方式在数据量庞大时效率极低。

  而索引就像电话簿的目录,它以 B-Tree(平衡树)数据结构存储特定字段或一组字段的值,并按这些值排序,使得 MongoDB 可以快速定位到满足查询条件的文档,而无需扫描整个集合,从而显著减少查询时间,特别是在处理大数据集时。索引不仅支持高效的相等匹配,还能优化范围查询(如 >、< 等)和排序操作。

  然而,索引也有其代价,它会占用额外的存储空间,并且在每次插入、更新或删除文档时,MongoDB 都需要更新相应的索引,这可能会略微降低写入操作的性能。因此,在为频繁查询的字段、排序字段或范围查询字段创建索引时,需要权衡查询性能的提升与存储和写入性能的潜在影响。

2 索引类型

  ① 单字段索引:
  允许用户在文档的单个字段上创建索引,以支持更高效的查询和排序操作。单字段索引可以是升序(1)或降序(-1),但索引键的排序顺序通常不会对查询性能产生显著影响,因为 MongoDB 可以在任何方向上遍历索引。
  ② 例子

// 假设有一个名为 scores 的集合,其中包含以下文档:
{ score: 30 },
{ score: 75 },
{ score: 18 },
{ score: 45 }

// 为 score 字段创建一个升序索引(1)或降序(-1)
db.scores.createIndex({ score: 1 })
db.scores.createIndex({ score: -1 })

  ③ 复合索引:
  复合索引是 MongoDB 中支持多个字段的索引类型。它允许用户为文档中的多个字段创建一个索引,以提高涉及这些字段的查询性能。复合索引的字段顺序非常重要,因为它决定了索引的排序和使用方式。
  ④ 例子
  复合索引首先按 userid 字段进行升序排序,然后在每个 userid 的值内,再按 score 字段进行降序排序。这意味着索引将优先根据 userid 进行排序和过滤,然后在相同 userid 的文档中根据 score 进行排序。

// 假设有一个名为 scores 的集合,其中包含以下文档:
{ userid: "cxk", score: 30 },
{ userid: "dhh", score: 45 },
{ userid: "cxk", score: 75 },
{ userid: "xyz", score: 55 },
{ userid: "cxk", score: 30 },
{ userid: "xyz", score: 90 }


// 为 userid 和 score 字段创建一个复合索引
db.scores.createIndex({ userid: 1, score: -1 })

3 查看索引

  ① 语法:getIndexes()方法可查看集合中所有已创建的索引。该方法会返回一个包含索引信息的数组。

db.xiaohuang.getIndexes()

  ② 例子:

// 每个 MongoDB 集合都会为主键自动创建索引,用于唯一标识每个文档。
dahuang> db.xiaohuang.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_', ns: 'dahuang.xiaohuang' } ]

v: 2:表示索引的版本号
key: { _id: 1 }:表示索引的键是 _id 字段,并且是升序排序,默认加的
name: '_id_':索引的名称,默认情况下 _id 字段的索引名称为 _id_
ns: 'dahuang.xiaohuang':表示索引所属(存放)的命名空间,格式为 database.collection,这里是 dahuang.xiaohuang

4 创建索引

  ① 语法:createIndex()为常用查询字段创建索引,可以显著提高查询速度。升序索引,使用 1;对于降序索引,使用 -1。
  在大型集合上创建索引会影响性能,尤其是在前台创建索引时。高选择性字段(即字段值唯一性高的字段)更适合作为索引字段。过多的索引会增加存储开销并降低写入性能,需根据查询需求合理设计索引。

db.xiaohuang.createIndex(keys, options)

  ② 参数:为常用查询字段创建索引,可以显著提高查询速度。升序索引,使用 1;对于降序索引,使用 -1。

keys:类型:文档(document)指定索引的字段及其排序方向。升序索引,使用 1;降序索引,使用 -1{ username: 1 }  // 升序索引
{ score: -1 }    // 降序索引
MongoDB 支持多种索引类型,包括单字段索引、复合索引、文本索引、地理空间索引和哈希索引。
options:类型:文档(document)可选参数,用于控制索引的创建行为。常见选项包括:
background:布尔值,表示是否在后台创建索引(默认 false)。
unique:布尔值,表示是否创建唯一索引(默认 false)。
name:字符串,指定索引的名称。
expireAfterSeconds:数值,用于 TTL 索引,指定文档过期时间(以秒为单位)。
sparse:布尔值,表示是否创建稀疏索引(默认 false)。

  ③ 例子:

// 为 username 字段创建升序索引
db.xiaohuang.createIndex({ username: 1 })
// 为 username 升序和 score 降序创建复合索引:
db.xiaohuang.createIndex({ username: 1, score: -1 })
// 为 email 字段创建唯一索引,确保每个 email 值都是唯一的:
db.xiaohuang.createIndex({ email: 1 }, { unique: true })
// 在后台创建索引,以避免阻塞其他操作:
db.xiaohuang.createIndex({ username: 1 }, { background: true })
// 为 createdAt 字段创建 TTL 索引,使文档在 3600 秒(1 小时)后自动删除:
db.xiaohuang.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })

4 移除索引

  ① 语法:dropIndex()方法可通过索引名称或索引规范文档来指定要删除的索引。

db.xiaohuang.dropIndex(index)

  ② 例子:

// 通过索引规范文档删除索引
// 删除 xiaohuang集合中 job字段上的升序索引:
dahuang> db.xiaohuang.dropIndex({job:1})
// 执行后,MongoDB 会返回一个结果文档,显示删除前集合中的索引数量和操作结果:
{ nIndexesWas: 2, ok: 1 }
// 这表示在删除该索引之前,集合中有 2 个索引,操作成功。
// 通过索引名称删除索引
dahuang> db.xiaohuang.dropIndex("job_1")
{ nIndexesWas: 2, ok: 1 }
// 删除所有索引,id索引不会被删除
dahuang> db.xiaohuang.dropIndexes()

5 索引执行计划

  ① 语法:explain()方法用于分析查询的执行计划,可以查看查询是否使用了索引、查询的执行时间、扫描的文档数量等信息。

// query:查询条件。
// options:可选参数,用于指定查询的选项。
// explain(options):指定 explain 方法的选项,以控制输出的详细程度。通常空着即可
db.xiaohuang.find(query, options).explain(options)

  ② 例子:explain()方法的输出包含多个部分,其中 queryPlanner 是最常用的部分之一。

// 无索引
dahuang> db.xiaohuang.find({name:"奔波儿灞"}).explain()
{
  queryPlanner: {
    plannerVersion: 1, // 查询计划的版本。
    namespace: 'dahuang.xiaohuang', // 查询的集合名称和数据库名称。
    indexFilterSet: false, // 是否使用了索引过滤器。
    parsedQuery: { name: { '$eq': '奔波儿灞' } }, // 解析后的查询条件。
    winningPlan: { // 查询优化器选择的执行计划。
      stage: 'COLLSCAN', // 扫描方式,例如 COLLSCAN(全集合扫描)或 IXSCAN(索引扫描)。
      filter: { name: { '$eq': '奔波儿灞' } },
      direction: 'forward'
    },
    rejectedPlans: []
  },
  serverInfo: {
    host: 'MSI',
    port: 27017,
    version: '4.0.5',
    gitVersion: '3739429dd92b92d1b0ab120911a23d50bf03c412'
  },
  ok: 1
}
// 有索引
dahuang> db.xiaohuang.find({job:"CEO"}).explain()
{
  queryPlanner: {
    plannerVersion: 1,
    namespace: 'dahuang.xiaohuang',
    indexFilterSet: false,
    parsedQuery: { job: { '$eq': 'CEO' } },
    winningPlan: {
      stage: 'FETCH', // 抓取,通过IXSCAN获取集合信息,再进行FETCH抓取
      inputStage: {
        stage: 'IXSCAN',
        keyPattern: { job: 1 },
        indexName: 'job_1',
        isMultiKey: false,
        multiKeyPaths: { job: [] },
        isUnique: false,
        isSparse: false,
        isPartial: false,
        indexVersion: 2,
        direction: 'forward',
        indexBounds: { job: [ '["CEO", "CEO"]' ] }
      }
    },
    rejectedPlans: []
  },
  serverInfo: {
    host: 'MSI',
    port: 27017,
    version: '4.0.5',
    gitVersion: '3739429dd92b92d1b0ab120911a23d50bf03c412'
  },
  ok: 1
}

6 索引覆盖查询

  ① 介绍:覆盖查询是一种高效的查询类型,它发生在查询条件和投影(返回的字段)都完全由索引字段覆盖的情况下。
  这意味着 MongoDB 可以直接从索引中获取所需的数据,而无需访问集合中的实际文档。这种查询方式通常非常高效,因为它避免了文档的读取和内存中的处理。
  ② 例子

dahuang> db.xiaohuang.find({job:"CEO"},{job:1,_id:0}).explain()
{
  queryPlanner: {
    plannerVersion: 1,
    namespace: 'dahuang.xiaohuang',
    indexFilterSet: false,
    parsedQuery: { job: { '$eq': 'CEO' } },
    winningPlan: {
      stage: 'PROJECTION',
      transformBy: { job: 1, _id: 0 },
      inputStage: {
        stage: 'IXSCAN',
        keyPattern: { job: 1 },
        indexName: 'job_1',
        isMultiKey: false,
        multiKeyPaths: { job: [] },
        isUnique: false,
        isSparse: false,
        isPartial: false,
        indexVersion: 2,
        direction: 'forward',
        indexBounds: { job: [ '["CEO", "CEO"]' ] }
      }
    },
    rejectedPlans: []
  },
  serverInfo: {
    host: 'MSI',
    port: 27017,
    version: '4.0.5',
    gitVersion: '3739429dd92b92d1b0ab120911a23d50bf03c412'
  },
  ok: 1
}

网站公告

今日签到

点亮在社区的每一天
去签到