MongoDB初学者教程:从零开始掌握MongoDB(第三阶段:查询进阶)

20 阅读5分钟

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
  1. users集合的“Filter”栏输入查询条件,比如{"age": {"$gt": 20}}
  2. 点击“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)

数组查询针对文档中的数组字段(如hobbiesscores),可以查找包含特定元素或满足条件的文档。

用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:只输出cityuserCount,隐藏_id
用Compass
  1. users集合界面,切换到“Aggregations”标签。

  2. 添加阶段(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}
  3. 查看结果。

用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高手不远了!