MongoDB初学者教程:从零开始掌握MongoDB(第三阶段:查询进阶)
欢迎进入MongoDB学习的第三阶段:查询进阶!在第一阶段,我们掌握了MongoDB的基础概念和环境搭建;在第二阶,我们学会了CRUD基本操作(增删改查)。现在,我们要更进一步,探索MongoDB的复杂查询和聚合框架,让你的数据查询能力从“简单翻找”升级到“精准挖掘”!
本阶段将带你学习:
- 复杂查询操作:用条件、逻辑和数组查询,精准定位数据。
- 聚合框架入门:像流水线一样处理数据,实现统计和分析。
我们会继续用mydb
数据库和users
集合,通过MongoDB命令、MongoDB Compass和Java代码实现操作。本阶段约4000字,包含详细代码示例和生活化比喻,确保零基础的你也能轻松上手!
第三阶段:查询进阶
一、复杂查询操作
在第二阶段,我们用find()
和findOne()
做了简单的查询,比如查找name
为“小明”的用户。现在,我们要学习更强大的查询方式,包括条件查询、逻辑查询和数组查询,让你的查询像“智能搜索”一样精准。
假设我们的users
集合有以下数据(可以通过第二阶段的插入操作创建):
[ { "_id": ObjectId("671234567890123456789012"), "name": "小明", "age": 20, "city": "北京", "hobbies": ["读书", "运动"],
"scores": [90, 85, 88]
},
{
"_id": ObjectId("671234567890123456789013"),
"name": "小红",
"age": 22,
"city": "上海",
"hobbies": ["旅行", "音乐"],
"scores": [95, 92, 90]
},
{
"_id": ObjectId("671234567890123456789014"),
"name": "小刚",
"age": 25,
"city": "广州",
"hobbies": ["运动", "游戏"],
"scores": [80, 85, 82]
}
]
1. 条件查询(eq, gt 等操作符)
条件查询使用操作符(如$eq
、$gt
)指定查询条件,就像在文件夹里找“年龄大于20”或“城市等于上海”的便签。
用MongoDB命令
-
精确匹配($eq) :查找城市为“上海”的用户:
db.users.find({"city": {"$eq": "上海"}}).pretty()
简写(
$eq
可省略):db.users.find({"city": "上海"}).pretty()
-
大于($gt) :查找年龄大于20的用户:
db.users.find({"age": {"$gt": 20}}).pretty()
输出:
{ "_id": ObjectId("671234567890123456789013"), "name": "小红", "age": 22, "city": "上海", "hobbies": ["旅行", "音乐"], "scores": [95, 92, 90] }, { "_id": ObjectId("671234567890123456789014"), "name": "小刚", "age": 25, "city": "广州", "hobbies": ["运动", "游戏"], "scores": [80, 85, 82] }
-
其他操作符:
$lt
:小于$gte
:大于等于$lte
:小于等于$ne
:不等于$in
:在指定值列表中,例如{"city": {"$in": ["北京", "上海"]}}
用Compass
- 在
users
集合的“Filter”栏输入查询条件,比如{"age": {"$gt": 20}}
。 - 点击“Find”,查看结果。
用Java驱动
在Java中,使用Filters
类构造查询条件:
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import org.bson.Document;
public class ComplexQuerySample {
private final MongoCollection<Document> users;
public ComplexQuerySample(MongoClient client) {
MongoDatabase database = client.getDatabase("mydb");
users = database.getCollection("users");
}
public void conditionQuery() {
// 查找城市为上海的用户
FindIterable<Document> result1 = users.find(Filters.eq("city", "上海"));
System.out.println("Users in 上海:");
for (Document doc : result1) {
System.out.println(doc.toJson());
}
// 查找年龄大于20的用户
FindIterable<Document> result2 = users.find(Filters.gt("age", 20));
System.out.println("Users with age > 20:");
for (Document doc : result2) {
System.out.println(doc.toJson());
}
}
public static void main(String[] args) {
MongoClient client = MongoDBConnection.connect(); // 复用第二阶段的连接类
ComplexQuerySample querySample = new ComplexQuerySample(client);
querySample.conditionQuery();
client.close();
}
}
生活化比喻:条件查询就像在文件夹里找便签,$gt
是“找年龄比20大的”,$eq
是“找城市写着上海的”。
2. 逻辑查询(and, or 组合)
逻辑查询用$and
、$or
组合多个条件,就像说“我要找年龄大于20 并且 城市是上海的人”。
用MongoDB命令
-
AND 查询:查找年龄大于20且城市为上海的用户:
db.users.find({ "$and": [ {"age": {"$gt": 20}}, {"city": "上海"} ] }).pretty()
简写(多个条件默认是
$and
):db.users.find({ "age": {"$gt": 20}, "city": "上海" }).pretty()
-
OR 查询:查找城市为北京或上海的用户:
db.users.find({ "$or": [ {"city": "北京"}, {"city": "上海"} ] }).pretty()
用Compass
在“Filter”栏输入:
- AND:
{"age": {"$gt": 20}, "city": "上海"}
- OR:
{"$or": [{"city": "北京"}, {"city": "上海"}]}
用Java驱动
public void logicQuery() {
// AND 查询:年龄 > 20 且城市为上海
FindIterable<Document> result1 = users.find(
Filters.and(
Filters.gt("age", 20),
Filters.eq("city", "上海")
)
);
System.out.println("Users with age > 20 and city = 上海:");
for (Document doc : result1) {
System.out.println(doc.toJson());
}
// OR 查询:城市为北京或上海
FindIterable<Document> result2 = users.find(
Filters.or(
Filters.eq("city", "北京"),
Filters.eq("city", "上海")
)
);
System.out.println("Users in 北京 or 上海:");
for (Document doc : result2) {
System.out.println(doc.toJson());
}
}
生活化比喻:逻辑查询就像在文件夹里找便签,$and
是“既要这个又要那个”,$or
是“这个或那个都行”。
3. 数组查询(all, elemMatch)
数组查询针对文档中的数组字段(如hobbies
、scores
),可以查找包含特定元素或满足条件的文档。
用MongoDB命令
-
匹配所有元素($all) :查找
hobbies
包含“读书”和“运动”的用户:db.users.find({ "hobbies": {"$all": ["读书", "运动"]} }).pretty()
-
匹配数组元素(elemMatch) :查找
scores
中有分数大于90的文档:db.users.find({ "scores": {"$elemMatch": {"$gt": 90}} }).pretty()
用Compass
$all
:{"hobbies": {"$all": ["读书", "运动"]}}
$elemMatch
:{"scores": {"$elemMatch": {"$gt": 90}}}
用Java驱动
public void arrayQuery() {
// 查找 hobbies 包含“读书”和“运动”的用户
FindIterable<Document> result1 = users.find(
Filters.all("hobbies", Arrays.asList("读书", "运动"))
);
System.out.println("Users with hobbies 读书 and 运动:");
for (Document doc : result1) {
-System.out.println(doc.toJson());
}
// 查找 scores 中有分数 > 90 的用户
FindIterable<Document> result2 = users.find(
Filters.elemMatch("scores", Filters.gt("scores", 90))
);
System.out.println("Users with scores > 90:");
for (Document doc : result2) {
System.out.println(doc.toJson());
}
}
生活化比喻:数组查询就像检查便签上的清单,$all
是“清单必须包含这些项目”,$elemMatch
是“清单里至少有一个项目满足条件”。
二、聚合框架入门
MongoDB的聚合框架(Aggregation Framework)是一个强大的数据处理工具,像一条流水线,把数据经过多个阶段(stage)处理,比如过滤、分组、统计。聚合框架适合复杂的分析任务,比如计算平均值、按城市分组等。
1. 聚合框架基础(match, group, project 阶段)
聚合框架的核心是管道(pipeline) ,每个阶段对数据进行一次处理。常用阶段包括:
- $match:过滤数据,类似
find()
。 - $group:按字段分组,计算统计值(如总数、平均值)。
- $project:选择输出的字段,类似SQL的
SELECT
。
用MongoDB命令
假设我们要统计每个城市的用户数量,并计算平均年龄:
db.users.aggregate([
// 阶段1:过滤年龄大于18的用户
{"$match": {"age": {"$gt": 18}}},
// 阶段2:按城市分组,计算人数和平均年龄
{
"$group": {
"_id": "$city",
"userCount": {"$sum": 1},
"avgAge": {"$avg": "$age"}
}
},
// 阶段3:只输出城市和人数
{
"$project": {
"city": "$_id",
"userCount": 1,
"_id": 0
}
}
]).pretty()
输出:
[ {"city": "北京", "userCount": 1}, {"city": "上海", "userCount": 1}, {"city": "广州", "userCount": 1}]
解释:
$match
:筛选年龄>18的用户。$group
:按city
分组,userCount
计数,avgAge
算平均年龄。$project
:只输出city
和userCount
,隐藏_id
。
用Compass
-
在
users
集合界面,切换到“Aggregations”标签。 -
添加阶段(Stage):
- Stage 1:
$match
,输入{"age": {"$gt": 18}}
。 - Stage 2:
$group
,输入{"_id": "$city", "userCount": {"$sum": 1}, "avgAge": {"$avg": "$age"}}
。 - Stage 3:
$project
,输入{"city": "$_id", "userCount": 1, "_id": 0}
。
- Stage 1:
-
查看结果。
用Java驱动
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Projections;
public void aggregationQuery() {
// 聚合查询
List<Document> results = users.aggregate(Arrays.asList(
// 阶段1:$match
Aggregates.match(Filters.gt("age", 18)),
// 阶段2:$group
Aggregates.group("$city",
Accumulators.sum("userCount", 1),
Accumulators.avg("avgAge", "$age")
),
// 阶段3:$project
Aggregates.project(
Projections.fields(
Projections.excludeId(),
Projections.computed("city", "$_id"),
Projections.include("userCount")
)
)
)).into(new ArrayList<>());
System.out.println("Aggregation results:");
for (Document doc : results) {
System.out.println(doc.toJson());
}
}
2. 简单统计查询实现
让我们实现另一个统计查询:按城市统计用户的平均成绩(scores
字段的平均值)。
用MongoDB命令
db.users.aggregate([
{
"$group": {
"_id": "$city",
"avgScore": {"$avg": {"$avg": "$scores"}}
}
},
{
"$project": {
"city": "$_id",
"avgScore": 1,
"_id": 0
}
}
]).pretty()
输出:
[ {"city": "北京", "avgScore": 87.66666666666667}, {"city": "上海", "avgScore": 92.33333333333333}, {"city": "广州", "avgScore": 82.33333333333333}]
用Java驱动
public void scoreAggregation() {
List<Document> results = users.aggregate(Arrays.asList(
Aggregates.group("$city",
Accumulators.avg("avgScore", new Document("$avg", "$scores"))
),
Aggregates.project(
Projections.fields(
Projections.excludeId(),
Projections.computed("city", "$_id"),
Projections.include("avgScore")
)
)
)).into(new ArrayList<>());
System.out.println("Score aggregation results:");
for (Document doc : results) {
System.out.println(doc.toJson());
}
}
生活化比喻:聚合框架像一条数据处理流水线,$match
是筛选原料,$group
是分组打包,$project
是挑选展示的成品。
下一步是什么?
恭喜你完成了MongoDB的第三阶段学习!你现在能用条件、逻辑和数组查询精准定位数据,还能用聚合框架进行复杂统计分析。这些技能就像给你的“智能文件夹”装上了高级搜索和报表功能!
后续阶段我们将深入:
- 第四阶段:进阶与优化(索引、性能调优、分布式部署,约4000字)
- 第五阶段:实战项目(开发一个完整的Web应用,约5000字)
这些阶段将基于前三阶段的代码,逐步构建一个真实项目,比如一个用户管理系统。继续努力,你已经离MongoDB高手不远了!