MongoDB入门学习教程,从入门到精通,MongoDB查询(4)

查询

1. find 简介

1.1 find() 基本语法

语法

javascript 复制代码
db.collection.find(
  <filter>,
  <projection>
)

参数说明

  • filter:查询条件(可选),指定要检索的文档条件
  • projection:投影(可选),指定返回的字段

返回值

返回一个游标(cursor),包含所有匹配的文档。

基本示例

javascript 复制代码
// 查询所有文档
db.users.find()

// 带条件的查询
db.users.find({ name: "张三" })

// 带投影的查询(只返回指定字段)
db.users.find({}, { name: 1, age: 1, _id: 0 })

// 格式化的输出(更易读)
db.users.find().pretty()

1.2 findOne() 方法

语法

javascript 复制代码
db.collection.findOne(
  <filter>,
  <projection>
)

特点

  • 返回第一个匹配的文档(不是游标)
  • 如果没找到,返回 null

示例

javascript 复制代码
// 返回第一个用户
var user = db.users.findOne()
print(user.name)

// 带条件的查询
var user = db.users.findOne({ age: { $gt: 18 } })

2. 查询条件

2.1 比较操作符

操作符 说明 示例
$eq 等于(equal) { age: { $eq: 25 } }{ age: 25 }
$ne 不等于(not equal) { age: { $ne: 25 } }
$gt 大于(greater than) { age: { $gt: 25 } }
$gte 大于等于(greater than or equal) { age: { $gte: 25 } }
$lt 小于(less than) { age: { $lt: 25 } }
$lte 小于等于(less than or equal) { age: { $lte: 25 } }
$in 在数组中 { age: { $in: [25, 30, 35] } }
$nin 不在数组中 { age: { $nin: [25, 30, 35] } }

示例

javascript 复制代码
// 查询年龄大于等于18且小于30的用户
db.users.find({ age: { $gte: 18, $lte: 30 } })

// 查询状态为 active 或 pending 的用户
db.users.find({ status: { $in: ["active", "pending"] } })

// 查询年龄不是25、30、35的用户
db.users.find({ age: { $nin: [25, 30, 35] } })

2.2 逻辑操作符

操作符 说明 示例
$and 逻辑与 { $and: [ { age: { $gt: 18 } }, { status: "active" } ] }
$or 逻辑或 { $or: [ { age: { $lt: 18 } }, { status: "inactive" } ] }
$not 逻辑非 { age: { $not: { $gt: 18 } } }
$nor 既不也不 { $nor: [ { age: { $gt: 60 } }, { status: "inactive" } ] }

示例

javascript 复制代码
// $and 示例(多个条件默认是隐式 AND)
db.users.find({ age: { $gt: 18 }, status: "active" })

// 显式 $and
db.users.find({
  $and: [
    { age: { $gt: 18 } },
    { age: { $lt: 60 } },
    { status: "active" }
  ]
})

// $or 示例
db.users.find({
  $or: [
    { age: { $lt: 18 } },
    { age: { $gt: 60 } }
  ]
})

// $nor 示例(年龄不在18-60之间且状态不是 inactive)
db.users.find({
  $nor: [
    { age: { $gt: 18, $lt: 60 } },
    { status: "inactive" }
  ]
})

2.3 元素操作符

操作符 说明 示例
$exists 字段是否存在 { email: { $exists: true } }
$type 字段的类型 { age: { $type: "int" } }

示例

javascript 复制代码
// 查询有 email 字段的用户
db.users.find({ email: { $exists: true } })

// 查询 email 字段不存在的用户
db.users.find({ email: { $exists: false } })

// 查询 age 字段类型为整数的用户
db.users.find({ age: { $type: "int" } })

// 查询 age 字段类型为数字(整数或浮点数)的用户
db.users.find({ age: { $type: "number" } })

2.4 正则表达式查询

语法

javascript 复制代码
{ field: /pattern/ }
{ field: { $regex: "pattern", $options: "i" } }

常用选项

  • i:不区分大小写
  • m:多行匹配
  • x:忽略空白字符
  • s:点号(.)匹配所有字符(包括换行)

示例

javascript 复制代码
// 查询名字以"张"开头的用户
db.users.find({ name: /^张/ })

// 查询邮箱包含"example.com"的用户(不区分大小写)
db.users.find({ email: { $regex: "example\\.com", $options: "i" } })

// 使用正则表达式对象
db.users.find({ name: { $regex: "^张", $options: "i" } })

3. 特定类型的查询

3.1 数组查询

示例数据

javascript 复制代码
db.articles.insertMany([
  { title: "MongoDB 教程", tags: ["mongodb", "database", "nosql"], comments: [
    { user: "张三", text: "很好" },
    { user: "李四", text: "不错" }
  ]},
  { title: "Python 教程", tags: ["python", "programming"], comments: [
    { user: "王五", text: "实用" }
  ]}
])
3.1.1 精确匹配整个数组
javascript 复制代码
// 匹配 tags 完全等于 ["mongodb", "database"] 的文档
db.articles.find({ tags: ["mongodb", "database"] })
3.1.2 匹配数组中的任意元素
javascript 复制代码
// 匹配 tags 包含 "mongodb" 的文档
db.articles.find({ tags: "mongodb" })

// 匹配 tags 包含 "mongodb" 或 "python" 的文档
db.articles.find({ tags: { $in: ["mongodb", "python"] } })
3.1.3 数组操作符
操作符 说明 示例
$all 包含所有指定元素 { tags: { $all: ["mongodb", "database"] } }
$size 数组长度 { tags: { $size: 3 } }
$elemMatch 匹配数组中的元素 { comments: { $elemMatch: { user: "张三" } } }

示例

javascript 复制代码
// 同时包含 mongodb 和 database 标签
db.articles.find({ tags: { $all: ["mongodb", "database"] } })

// 恰好有2个标签的文章
db.articles.find({ tags: { $size: 2 } })

// 查询张三评论过的文章
db.articles.find({
  comments: {
    $elemMatch: { user: "张三", text: { $exists: true } }
  }
})

3.2 嵌套文档查询

示例数据

javascript 复制代码
db.users.insertMany([
  {
    name: "张三",
    address: {
      city: "北京",
      street: "长安街",
      zip: "100000"
    }
  },
  {
    name: "李四",
    address: {
      city: "上海",
      street: "南京路",
      zip: "200000"
    }
  }
])

查询方式

javascript 复制代码
// 点号表示法查询嵌套字段
db.users.find({ "address.city": "北京" })

// 精确匹配整个嵌套文档(顺序和字段必须完全一致)
db.users.find({
  address: {
    city: "北京",
    street: "长安街",
    zip: "100000"
  }
})

// 使用 $elemMatch 查询嵌套文档数组
db.articles.find({
  comments: {
    $elemMatch: { user: "张三", text: /好/ }
  }
})

3.3 查询 Null 值

注意事项

  • null 查询会同时匹配字段值为 null 的文档和字段不存在的文档
javascript 复制代码
// 查询 age 为 null 的文档(包括没有 age 字段的文档)
db.users.find({ age: null })

// 只查询 age 字段存在且为 null 的文档
db.users.find({ age: { $type: 10 } })  // BSON 类型 10 是 null
// 或使用
db.users.find({ age: { $exists: true, $eq: null } })

4. $where 查询

4.1 基本概念

$where 允许使用 JavaScript 表达式或函数进行查询,提供了最大灵活性,但性能较差(需要执行 JavaScript 引擎)。

语法

javascript 复制代码
db.collection.find({ $where: <JavaScript expression> })
db.collection.find(<query>, { $where: <function> })

4.2 使用示例

javascript 复制代码
// 查询 name 长度大于 2 的用户
db.users.find({ $where: "this.name.length > 2" })

// 使用函数形式
db.users.find({
  $where: function() {
    return this.name.length > 2 && this.age > 18
  }
})

// 比较两个字段
db.products.find({ $where: "this.price > this.cost" })

// 复杂业务逻辑
db.orders.find({
  $where: function() {
    // 只返回总金额大于 1000 且包含特定商品的订单
    var total = this.items.reduce((sum, item) => sum + item.price * item.qty, 0)
    var hasLaptop = this.items.some(item => item.name === "笔记本")
    return total > 1000 && hasLaptop
  }
})

4.3 注意事项与性能优化

方面 说明
性能 $where 无法使用索引,每个文档都需要执行 JavaScript
安全性 避免注入攻击,不要直接拼接用户输入
替代方案 尽可能使用普通查询操作符($gt$regex 等)代替 $where
限制 在分片集群中,$where 查询会在所有分片执行

推荐做法

javascript 复制代码
// 不推荐(无法使用索引)
db.users.find({ $where: "this.age > 18" })

// 推荐(可以使用索引)
db.users.find({ age: { $gt: 18 } })

5. 游标

5.1 游标的基本概念

定义find() 方法返回的不是查询结果本身,而是一个游标(cursor),它指向结果集的指针。

游标特点

  • 惰性执行:只有在需要时才从数据库获取数据
  • 可迭代:支持在 shell 中自动迭代
  • 可链式调用:支持多个方法连续调用

5.2 游标方法

5.2.1 limit() - 限制返回数量
javascript 复制代码
// 只返回前5条文档
db.users.find().limit(5)

// 限制返回数量为10条
db.products.find({ price: { $gt: 100 } }).limit(10)
5.2.2 skip() - 跳过文档
javascript 复制代码
// 跳过前10条,返回之后的文档
db.users.find().skip(10)

// 分页查询(每页20条,第3页)
db.users.find().skip(40).limit(20)
5.2.3 sort() - 排序

语法

javascript 复制代码
db.collection.find().sort({ field1: 1, field2: -1 })
// 1 表示升序,-1 表示降序

示例

javascript 复制代码
// 按年龄升序排序
db.users.find().sort({ age: 1 })

// 多字段排序:先按年龄降序,再按姓名升序
db.users.find().sort({ age: -1, name: 1 })

// 使用索引的排序更高效
db.users.createIndex({ age: 1 })
db.users.find().sort({ age: 1 }).limit(10)  // 可以使用索引
5.2.4 count() - 计数
javascript 复制代码
// 统计所有文档数
db.users.find().count()

// 统计满足条件的文档数
db.users.find({ age: { $gt: 18 } }).count()

// 更精确的计数(考虑 skip 和 limit)
db.users.find({ age: { $gt: 18 } }).skip(10).limit(20).count(true)

// 推荐使用 countDocuments(更准确)
db.users.countDocuments({ age: { $gt: 18 } })

5.3 游标迭代方法

5.3.1 forEach() - 遍历游标
javascript 复制代码
db.users.find().forEach(function(doc) {
  print("用户: " + doc.name + ", 年龄: " + doc.age)
})
5.3.2 toArray() - 转换为数组
javascript 复制代码
// 将游标转换为数组(适用于结果集较小的场景)
var users = db.users.find().limit(100).toArray()
print(users[0].name)
5.3.3 next()hasNext()
javascript 复制代码
var cursor = db.users.find()
while (cursor.hasNext()) {
  var doc = cursor.next()
  print(doc.name)
}

5.4 游标生命周期与配置

特性 说明
默认超时 游标默认在 10 分钟无活动后自动关闭
无超时游标 db.users.find().noCursorTimeout() 可禁用超时(需手动关闭)
批量获取 默认每批返回 101 条文档或 1MB 数据
批大小调整 db.users.find().batchSize(50)

示例

javascript 复制代码
// 创建无超时游标(用于长时间迭代)
var cursor = db.users.find().noCursorTimeout()

// 手动关闭游标
cursor.close()

// 设置批大小为 100
db.users.find().batchSize(100)

5.5 投影(Projection)

投影用于控制返回的字段,减少网络传输量

javascript 复制代码
// 只返回 name 和 age 字段(排除 _id)
db.users.find({}, { name: 1, age: 1, _id: 0 })

// 排除某些字段(返回所有字段除了 password)
db.users.find({}, { password: 0 })

// 嵌套字段投影
db.users.find({}, { "address.city": 1, name: 1 })

// 数组切片(返回前3个元素)
db.articles.find({}, { comments: { $slice: 3 } })

// 数组切片(跳过前2个,返回3个)
db.articles.find({}, { comments: { $slice: [2, 3] } })

6. 综合示例与最佳实践

6.1 分页查询实现

javascript 复制代码
// 分页查询函数
function paginate(collection, page, pageSize, filter = {}, sort = {}) {
  var skip = (page - 1) * pageSize
  return collection
    .find(filter)
    .sort(sort)
    .skip(skip)
    .limit(pageSize)
    .toArray()
}

// 使用示例:查询第3页,每页10条,按年龄降序
var users = paginate(db.users, 3, 10, { status: "active" }, { age: -1 })

6.2 复杂查询示例

javascript 复制代码
// 查询活跃用户中,年龄在18-30岁之间,且邮箱已验证,按注册时间倒序
db.users.find({
  status: "active",
  age: { $gte: 18, $lte: 30 },
  emailVerified: true,
  createdAt: { $exists: true }
}).sort({ createdAt: -1 }).limit(20)

// 查询订单中,包含特定商品且总金额大于500的订单
db.orders.find({
  items: { $elemMatch: { productId: "P1001", quantity: { $gt: 0 } } },
  totalAmount: { $gt: 500 }
})

6.3 查询优化建议

优化点 说明 示例
使用索引 为常用查询字段创建索引 db.users.createIndex({ age: 1, status: 1 })
限制返回字段 使用投影减少数据传输 db.users.find({}, { name: 1 })
避免 $where 优先使用查询操作符 { age: { $gt: 18 } } 代替 $where
使用 explain() 分析查询性能 db.users.find({ age: 18 }).explain("executionStats")
合理使用 limit 避免返回大量数据 db.logs.find().sort({ time: -1 }).limit(100)

总结

本章详细介绍了 MongoDB 的查询功能,涵盖:

  • find 方法的基本使用和投影
  • 多种查询条件:比较、逻辑、元素、正则表达式
  • 特定类型查询:数组、嵌套文档、null 值的处理
  • $where 查询的灵活性与性能权衡
  • 游标操作:limit、skip、sort、count 以及迭代方法

掌握这些查询技术是高效使用 MongoDB 的基础,合理的查询设计和索引优化能显著提升应用性能。

相关推荐
weixin_409383122 小时前
godot等轴视角tilemaplayer的学习 isocheric的素材xy大小怎么调
学习·游戏引擎·godot
LSL666_2 小时前
MybatisPlus条件构造器(上)
java·数据库·mysql·mybatisplus
2201_754864783 小时前
学习日记(2026年3月23日)
学习
U-52184F693 小时前
深入理解“隐式共享”与“写时复制”:从性能魔法到内存深坑
java·数据库·算法
程序猿ZhangSir3 小时前
详解了解 Redis IO多路复用底层原理,Select,poll,epoll三者的区别?
数据库·redis·缓存
U-52184F693 小时前
深度解析:从 Qt 的 Q_D 宏说起,C++ 工业级 SDK 是如何保证 ABI 稳定性的
数据库·c++·qt
Gauss松鼠会3 小时前
【GaussDB】LLVM技术在GaussDB等数据库中的应用
大数据·数据库·架构·数据库开发·gaussdb·llvm
IMPYLH3 小时前
Linux 的 dir 命令
linux·运维·服务器·数据库
wfsm4 小时前
mysql事务
数据库·mysql