Amazon DocumentDB 基础查询 #

一、查询方法概述 #

1.1 查询方法 #

text
查询方法:
├── find - 查询多个文档
├── findOne - 查询单个文档
├── countDocuments - 计数
├── distinct - 去重
└── aggregate - 聚合查询

1.2 查询结构 #

javascript
// 基本查询结构
db.collection.find(
  { /* 查询条件 */ },
  { /* 投影 */ }
)

// 完整查询链
db.users.find({ status: "active" })
  .projection({ name: 1, email: 1 })
  .sort({ createdAt: -1 })
  .skip(0)
  .limit(10)

二、find - 基本查询 #

2.1 查询所有文档 #

javascript
// 查询所有文档
db.users.find({})

// 格式化输出
db.users.find({}).pretty()

// 统计数量
db.users.find({}).count()

2.2 等值查询 #

javascript
// 单字段等值查询
db.users.find({ name: "张三" })

// 多字段等值查询(AND关系)
db.users.find({ 
  name: "张三",
  status: "active"
})

// 嵌套字段查询
db.users.find({ "address.city": "北京" })

// 数组元素查询
db.users.find({ tags: "编程" })

2.3 查询字段存在性 #

javascript
// 检查字段是否存在
db.users.find({ email: { $exists: true } })

// 检查字段不存在
db.users.find({ phone: { $exists: false } })

// 检查字段类型
db.users.find({ age: { $type: "int" } })

三、查询操作符 #

3.1 比较操作符 #

javascript
// 等于
db.products.find({ price: { $eq: 100 } })

// 不等于
db.products.find({ status: { $ne: "deleted" } })

// 大于
db.products.find({ price: { $gt: 100 } })

// 大于等于
db.products.find({ price: { $gte: 100 } })

// 小于
db.products.find({ price: { $lt: 100 } })

// 小于等于
db.products.find({ price: { $lte: 100 } })

// 在列表中
db.users.find({ role: { $in: ["admin", "editor"] } })

// 不在列表中
db.users.find({ status: { $nin: ["deleted", "banned"] } })

3.2 逻辑操作符 #

javascript
// AND操作
db.users.find({
  $and: [
    { age: { $gte: 18 } },
    { status: "active" }
  ]
})

// 简写形式(逗号分隔)
db.users.find({
  age: { $gte: 18 },
  status: "active"
})

// OR操作
db.users.find({
  $or: [
    { role: "admin" },
    { role: "superuser" }
  ]
})

// NOT操作
db.users.find({
  age: { $not: { $lt: 18 } }
})

// NOR操作(都不满足)
db.users.find({
  $nor: [
    { status: "deleted" },
    { status: "banned" }
  ]
})

3.3 元素操作符 #

javascript
// 字段存在
db.users.find({ email: { $exists: true } })

// 字段类型
db.users.find({ 
  age: { $type: ["int", "long"] } 
})

3.4 数组操作符 #

javascript
// 数组包含元素
db.users.find({ tags: "编程" })

// 数组包含所有元素
db.users.find({ 
  tags: { $all: ["编程", "阅读"] } 
})

// 数组长度
db.users.find({ 
  tags: { $size: 3 } 
})

// 数组元素匹配
db.users.find({
  scores: { $elemMatch: { $gte: 80, $lt: 90 } }
})

// 嵌套数组查询
db.users.find({
  education: { 
    $elemMatch: { 
      school: "清华大学",
      degree: "本科"
    } 
  }
})

3.5 正则表达式 #

javascript
// 正则匹配
db.users.find({ 
  email: { $regex: /@example\.com$/ } 
})

// 不区分大小写
db.users.find({ 
  name: { $regex: /张/i } 
})

// 使用$options
db.users.find({ 
  name: { $regex: "张", $options: "i" } 
})

// 复杂正则
db.users.find({
  phone: { $regex: /^1[3-9]\d{9}$/ }
})

四、投影 #

4.1 包含字段 #

javascript
// 只返回指定字段
db.users.find(
  {},
  { name: 1, email: 1 }
)

// 输出
{
  "_id": ObjectId("..."),
  "name": "张三",
  "email": "zhangsan@example.com"
}

4.2 排除字段 #

javascript
// 排除指定字段
db.users.find(
  {},
  { password: 0, createdAt: 0 }
)

4.3 嵌套字段投影 #

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

// 投影数组元素
db.users.find(
  {},
  { "education.0": 1 }  // 只返回第一个教育经历
)

4.4 数组切片 #

javascript
// 返回数组前N个元素
db.posts.find(
  {},
  { comments: { $slice: 5 } }
)

// 跳过前M个,返回后N个
db.posts.find(
  {},
  { comments: { $slice: [10, 5] } }
)

// 返回数组最后N个元素
db.posts.find(
  {},
  { comments: { $slice: -5 } }
)

4.5 元素匹配投影 #

javascript
// 返回匹配条件的数组元素
db.students.find(
  { name: "张三" },
  { 
    grades: { 
      $elemMatch: { subject: "数学" } 
    } 
  }
)

4.6 条件投影 #

javascript
// 使用$条件投影
db.users.find(
  { "grades.subject": "数学" },
  { "grades.$": 1 }
)

五、排序 #

5.1 单字段排序 #

javascript
// 升序
db.users.find({}).sort({ name: 1 })

// 降序
db.users.find({}).sort({ createdAt: -1 })

5.2 多字段排序 #

javascript
// 多字段排序(先按status升序,再按createdAt降序)
db.users.find({}).sort({ 
  status: 1, 
  createdAt: -1 
})

5.3 嵌套字段排序 #

javascript
// 排序嵌套字段
db.users.find({}).sort({ "address.city": 1 })

5.4 排序限制 #

text
排序注意事项:
├── 排序字段应有索引
├── 大数据量排序可能超时
├── 内存排序限制100MB
└── 超过限制需要使用allowDiskUse

六、分页 #

6.1 skip/limit分页 #

javascript
// 基本分页
const page = 1;
const pageSize = 10;

db.users.find({})
  .skip((page - 1) * pageSize)
  .limit(pageSize)

// 第1页
db.users.find({}).skip(0).limit(10)

// 第2页
db.users.find({}).skip(10).limit(10)

// 第3页
db.users.find({}).skip(20).limit(10)

6.2 游标分页(推荐) #

javascript
// 使用最后一条记录的ID作为游标
function findWithCursor(collection, filter, lastId, limit = 10) {
  const query = lastId 
    ? { ...filter, _id: { $gt: lastId } }
    : filter;
  
  return collection.find(query)
    .sort({ _id: 1 })
    .limit(limit)
    .toArray();
}

// 使用示例
let lastId = null;
const pageSize = 10;

// 第一页
const page1 = await findWithCursor(db.users, {}, lastId, pageSize);
lastId = page1[page1.length - 1]._id;

// 第二页
const page2 = await findWithCursor(db.users, {}, lastId, pageSize);
lastId = page2[page2.length - 1]._id;

6.3 范围分页 #

javascript
// 使用时间戳范围分页
function findByTimeRange(collection, startTime, endTime, limit = 100) {
  return collection.find({
    createdAt: {
      $gte: startTime,
      $lt: endTime
    }
  })
  .sort({ createdAt: 1 })
  .limit(limit)
  .toArray();
}

6.4 分页性能对比 #

text
分页方式对比:
├── skip/limit
│   ├── 简单易用
│   ├── 深度分页性能差
│   └── 适合小数据量
│
└── 游标分页
    ├── 性能稳定
    ├── 不适合随机跳页
    └── 适合大数据量

七、计数 #

7.1 countDocuments #

javascript
// 精确计数
db.users.countDocuments({})

// 条件计数
db.users.countDocuments({ status: "active" })

// 复杂条件计数
db.users.countDocuments({
  createdAt: { $gte: new Date("2024-01-01") },
  status: { $in: ["active", "pending"] }
})

7.2 estimatedDocumentCount #

javascript
// 估算计数(更快但不精确)
db.users.estimatedDocumentCount()

八、去重 #

8.1 基本去重 #

javascript
// 获取字段唯一值
db.users.distinct("city")

// 输出
["北京", "上海", "广州", "深圳"]

8.2 条件去重 #

javascript
// 条件去重
db.users.distinct("role", { status: "active" })

8.3 嵌套字段去重 #

javascript
// 嵌套字段去重
db.users.distinct("address.city")

九、查询优化 #

9.1 使用explain #

javascript
// 查看查询计划
db.users.find({ email: "test@example.com" }).explain()

// 查看执行统计
db.users.find({ email: "test@example.com" }).explain("executionStats")

// 关键指标
{
  "executionStats": {
    "totalDocsExamined": 1,      // 扫描文档数
    "totalKeysExamined": 1,      // 扫描索引键数
    "executionTimeMillis": 0,    // 执行时间
    "indexUsed": "email_1"       // 使用的索引
  }
}

9.2 索引使用检查 #

javascript
// 检查是否使用索引
const explain = db.users.find({ email: "test@example.com" }).explain();
const winningPlan = explain.queryPlanner.winningPlan;

if (winningPlan.stage === "COLLSCAN") {
  print("警告:使用了全表扫描");
} else if (winningPlan.stage === "IXSCAN") {
  print("使用了索引: " + winningPlan.indexName);
}

9.3 查询优化建议 #

text
优化建议:
├── 为常用查询字段创建索引
├── 使用索引覆盖查询
├── 避免使用$or(考虑$in)
├── 限制返回字段数量
├── 使用游标分页代替skip
├── 避免大数组查询
└── 使用explain分析查询

十、查询模式 #

10.1 模糊搜索 #

javascript
// 前缀匹配
db.users.find({ name: { $regex: /^张/ } })

// 后缀匹配
db.users.find({ email: { $regex: /@example\.com$/ } })

// 包含匹配
db.users.find({ name: { $regex: /三/ } })

// 多字段搜索
db.users.find({
  $or: [
    { name: { $regex: /张/ } },
    { email: { $regex: /张/ } }
  ]
})

10.2 范围查询 #

javascript
// 日期范围
db.orders.find({
  createdAt: {
    $gte: new Date("2024-01-01"),
    $lt: new Date("2024-02-01")
  }
})

// 数值范围
db.products.find({
  price: {
    $gte: 100,
    $lte: 500
  }
})

10.3 空值处理 #

javascript
// 查找null或不存在
db.users.find({ phone: null })

// 只查找null值
db.users.find({ phone: { $type: "null" } })

// 查找不存在或null
db.users.find({ 
  $or: [
    { phone: null },
    { phone: { $exists: false } }
  ]
})

十一、实际应用示例 #

11.1 用户搜索 #

javascript
// 综合用户搜索
function searchUsers(options) {
  const query = {};
  
  // 关键词搜索
  if (options.keyword) {
    query.$or = [
      { name: { $regex: options.keyword, $options: "i" } },
      { email: { $regex: options.keyword, $options: "i" } }
    ];
  }
  
  // 状态筛选
  if (options.status) {
    query.status = options.status;
  }
  
  // 角色筛选
  if (options.role) {
    query.role = options.role;
  }
  
  // 日期范围
  if (options.startDate || options.endDate) {
    query.createdAt = {};
    if (options.startDate) {
      query.createdAt.$gte = new Date(options.startDate);
    }
    if (options.endDate) {
      query.createdAt.$lte = new Date(options.endDate);
    }
  }
  
  return db.users.find(query)
    .sort({ createdAt: -1 })
    .skip((options.page - 1) * options.pageSize)
    .limit(options.pageSize)
    .toArray();
}

11.2 产品筛选 #

javascript
// 产品筛选查询
function filterProducts(filters) {
  const query = {};
  
  // 分类
  if (filters.category) {
    query.category = filters.category;
  }
  
  // 价格范围
  if (filters.minPrice || filters.maxPrice) {
    query.price = {};
    if (filters.minPrice) query.price.$gte = filters.minPrice;
    if (filters.maxPrice) query.price.$lte = filters.maxPrice;
  }
  
  // 标签
  if (filters.tags && filters.tags.length > 0) {
    query.tags = { $all: filters.tags };
  }
  
  // 库存
  if (filters.inStock) {
    query.stock = { $gt: 0 };
  }
  
  return db.products.find(query)
    .sort({ sales: -1, createdAt: -1 })
    .toArray();
}

十二、总结 #

12.1 查询操作符速查 #

操作符 用途
$eq, $ne 等于/不等于
$gt, $gte, $lt, $lte 比较
$in, $nin 包含/不包含
$and, $or, $not 逻辑
$exists 字段存在
$regex 正则匹配
$all, $elemMatch, $size 数组

12.2 最佳实践总结 #

text
查询最佳实践:
├── 使用索引优化查询
├── 使用explain分析性能
├── 合理使用投影减少数据传输
├── 使用游标分页处理大数据
├── 避免深度嵌套查询
└── 监控慢查询

下一步,让我们学习高级查询!

最后更新:2026-03-27