Skip to content

查询语法入门

MongoDB 不使用 SQL 作为主要查询接口。mongosh 和驱动通过命令及 BSON 文档表达查询,字段名是数据,$gt$set$group 等 Operator 表达操作。下面以 MySQL 习惯对照 MongoDB Query API。


示例文档

javascript
use notebook

db.users.insertOne({
  username: "alice",
  enabled: true,
  balance: Decimal128("100.00"),
  tags: ["user", "vip"],
  profile: { city: "Hefei", level: 3 },
  createdAt: new Date()
})

use 只切换当前 Database,Database 和 Collection 通常在首次写入时创建。生产环境可先显式创建 Collection、Validator 和 Index,避免错误名称或错误类型因首次写入被固化。


CRUD 对照

目标MySQLMongoDB
插入一行INSERT INTOinsertOne(document)
批量插入多组 VALUESinsertMany(documents)
查询SELECT ... WHEREfind(filter, projection)
更新UPDATE ... SETupdateOne/updateMany(filter, update)
删除DELETE ... WHEREdeleteOne/deleteMany(filter)
主键自定义 Primary Key_id,缺省生成 ObjectId
javascript
db.users.find(
  { enabled: true, balance: { $gte: Decimal128("50.00") } },
  { username: 1, balance: 1, _id: 0 }
).sort({ balance: -1 }).limit(20)

相当于:

sql
SELECT username, balance
FROM users
WHERE enabled = TRUE AND balance >= 50.00
ORDER BY balance DESC
LIMIT 20;

条件操作符

SQLMongoDB Filter
age = 18{ age: 18 }
age <> 18{ age: { $ne: 18 } }
age >= 18{ age: { $gte: 18 } }
status IN (...){ status: { $in: [...] } }
a = 1 AND b = 2{ a: 1, b: 2 }
a = 1 OR b = 2{ $or: [{ a: 1 }, { b: 2 }] }
field IS NULL{ field: null },同时匹配 null 和缺失
field IS NOT NULL需结合 $ne$exists 明确缺失语义
LIKE 'abc%'{ name: /^abc/ },索引能力取决于正则形式与 Collation

MongoDB 区分 Field 缺失与 BSON Null,但 { field: null } 会同时匹配两者。需要精确语义时使用 $exists$type 等条件组合。


嵌套对象和数组

javascript
db.users.find({ "profile.city": "Hefei" })
db.users.find({ tags: "vip" })
db.users.find({ tags: { $all: ["user", "vip"] } })

对象数组中要求同一个元素同时满足多个条件时使用 $elemMatch

javascript
db.orders.find({
  items: {
    $elemMatch: {
      productId: 1001,
      quantity: { $gte: 2 }
    }
  }
})

更新操作

javascript
db.users.updateOne(
  { username: "alice" },
  {
    $set: { enabled: true, "profile.city": "Shanghai" },
    $inc: { loginCount: 1 },
    $addToSet: { tags: "active" },
    $currentDate: { updatedAt: true }
  }
)

updateOne 默认不会插入新文档,加入 { upsert: true } 才表示没有匹配时创建。与 MySQL ON DUPLICATE KEY UPDATE 类似,可靠 Upsert 仍需要 Unique Index 防止并发下产生重复业务键。

不要把普通对象直接作为 Update 参数来表达 $set;不同 API 中它可能表示替换整个文档或直接报错,应明确使用更新操作符或 replaceOne


聚合与关联

javascript
db.orders.aggregate([
  { $match: { status: "paid" } },
  { $group: { _id: "$userId", total: { $sum: "$amount" } } },
  { $sort: { total: -1 } },
  { $limit: 20 }
])

SQL 对照:

sql
SELECT user_id, SUM(amount) AS total
FROM orders
WHERE status = 'paid'
GROUP BY user_id
ORDER BY total DESC
LIMIT 20;

关联使用 $lookup

javascript
db.orders.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "user"
    }
  },
  { $unwind: "$user" }
])

如果核心请求持续需要多层 $lookup,应重新检查数据是否应嵌入或冗余,而不是只优化关联语法。


分页

javascript
db.users.find({ enabled: true })
  .sort({ createdAt: -1, _id: -1 })
  .skip(40)
  .limit(20)

skip 与 SQL OFFSET 类似,深分页会扫描并丢弃前序结果。Keyset Pagination 使用上一页最后一个排序键继续查询:

javascript
db.users.find({
  enabled: true,
  $or: [
    { createdAt: { $lt: lastCreatedAt } },
    { createdAt: lastCreatedAt, _id: { $lt: lastId } }
  ]
}).sort({ createdAt: -1, _id: -1 }).limit(20)

需要建立与 Filter 和 Sort 匹配的复合索引。


MySQL 使用习惯迁移

MySQL 习惯MongoDB 调整
先按实体拆表,再通过 JOIN 组合先确定聚合边界,再选择嵌入或引用
Database/Table/RowDatabase/Collection/Document
自增整数主键默认 ObjectId,也可使用业务 _id,不建议集中式自增热点
列类型由 DDL 强制BSON 类型随值保存,关键结构用 Schema Validation 约束
NULL 与列存在区分 null 和 Field Missing
UPDATE SET count = count + 1使用 $inc 原子更新
GROUP BY使用 Aggregation $group
JOIN嵌入、引用查询或 $lookup
EXPLAIN使用 explain("executionStats")
事务是默认建模工具优先单文档原子性,必要时使用多文档事务

参考资料