MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

MongoDB查询操作详解:基本查询与高级查询

2021-01-304.6k 阅读

一、MongoDB基本查询

1.1 简单查询文档

在MongoDB中,最基本的查询操作就是获取集合中的文档。我们可以使用find()方法来实现。假设我们有一个名为students的集合,其中存储了学生的信息,每个文档包含name(姓名)、age(年龄)和score(分数)字段。

首先,连接到MongoDB数据库并选择相应的集合:

const { MongoClient } = require('mongodb');
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询所有文档
        const cursor = students.find({});
        const allStudents = await cursor.toArray();
        console.log(allStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

在上述代码中,students.find({})表示查询students集合中的所有文档。find()方法的参数是一个查询条件,这里传入一个空对象{},表示没有任何限制条件,即返回集合中的所有文档。cursor.toArray()将查询结果转换为数组并返回。

1.2 指定查询条件

如果我们只想查询年龄大于某个值的学生,可以在find()方法的参数中指定条件。例如,查询年龄大于18岁的学生:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询年龄大于18岁的学生
        const query = { age: { $gt: 18 } };
        const cursor = students.find(query);
        const olderStudents = await cursor.toArray();
        console.log(olderStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

在这个例子中,query对象中的age: { $gt: 18 }表示查询条件,$gt是MongoDB的比较操作符,代表“大于”。所以这个查询会返回所有年龄大于18岁的学生文档。

1.3 比较操作符

MongoDB提供了一系列的比较操作符,除了前面提到的$gt(大于),还有:

  • $lt:小于。例如,{ age: { $lt: 20 } }表示查询年龄小于20岁的学生。
  • $gte:大于等于。例如,{ score: { $gte: 60 } }表示查询分数大于等于60分的学生。
  • $lte:小于等于。例如,{ score: { $lte: 100 } }表示查询分数小于等于100分的学生。
  • $eq:等于。例如,{ name: { $eq: 'Alice' } }表示查询名字为Alice的学生。
  • $ne:不等于。例如,{ age: { $ne: 20 } }表示查询年龄不等于20岁的学生。

示例代码查询分数小于60分的学生:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询分数小于60分的学生
        const query = { score: { $lt: 60 } };
        const cursor = students.find(query);
        const lowScoreStudents = await cursor.toArray();
        console.log(lowScoreStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

1.4 逻辑操作符

MongoDB还支持逻辑操作符,用于组合多个查询条件。

  • $and:逻辑与,用于连接多个条件,只有所有条件都满足时才返回文档。例如,查询年龄大于18岁且分数大于60分的学生:
async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询年龄大于18岁且分数大于60分的学生
        const query = {
            $and: [
                { age: { $gt: 18 } },
                { score: { $gt: 60 } }
            ]
        };
        const cursor = students.find(query);
        const qualifiedStudents = await cursor.toArray();
        console.log(qualifiedStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

在这个例子中,$and数组中包含两个条件,只有同时满足这两个条件的文档才会被返回。

  • $or:逻辑或,连接多个条件,只要有一个条件满足就返回文档。例如,查询年龄小于18岁或者分数小于60分的学生:
async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询年龄小于18岁或者分数小于60分的学生
        const query = {
            $or: [
                { age: { $lt: 18 } },
                { score: { $lt: 60 } }
            ]
        };
        const cursor = students.find(query);
        const unqualifiedStudents = await cursor.toArray();
        console.log(unqualifiedStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里$or数组中的两个条件,只要满足其中一个,对应的文档就会被返回。

  • $not:逻辑非,对一个条件取反。例如,查询年龄不大于18岁的学生(即年龄小于等于18岁):
async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询年龄不大于18岁的学生
        const query = { age: { $not: { $gt: 18 } } };
        const cursor = students.find(query);
        const youngerStudents = await cursor.toArray();
        console.log(youngerStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里$not{ $gt: 18 }这个条件取反,所以最终查询的是年龄小于等于18岁的学生。

二、MongoDB高级查询

2.1 正则表达式查询

在MongoDB中,可以使用正则表达式进行模糊查询。例如,查询名字以A开头的学生:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询名字以A开头的学生
        const query = { name: /^A/ };
        const cursor = students.find(query);
        const aNamedStudents = await cursor.toArray();
        console.log(aNamedStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

在这个例子中,/^A/是一个正则表达式,^表示匹配字符串的开始位置,所以这个正则表达式会匹配名字以A开头的字符串。

如果要查询名字中包含li的学生,可以使用如下正则表达式:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询名字中包含li的学生
        const query = { name: /li/ };
        const cursor = students.find(query);
        const liInNameStudents = await cursor.toArray();
        console.log(liInNameStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里/li/表示匹配包含li的字符串,不区分大小写。如果要区分大小写,可以使用/li/i,其中i是修饰符,表示不区分大小写。

2.2 数组查询

假设students集合中的文档结构如下,每个学生可以有多个爱好,存储在hobbies数组中:

{
    "name": "Alice",
    "age": 20,
    "score": 85,
    "hobbies": ["reading", "swimming", "painting"]
}

要查询有“reading”爱好的学生,可以这样写查询:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询有reading爱好的学生
        const query = { hobbies: "reading" };
        const cursor = students.find(query);
        const readingHobbyStudents = await cursor.toArray();
        console.log(readingHobbyStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里直接在查询条件中指定hobbies数组包含“reading”即可。

如果要查询有多个特定爱好的学生,比如同时有“reading”和“swimming”爱好,可以使用$all操作符:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询同时有reading和swimming爱好的学生
        const query = { hobbies: { $all: ["reading", "swimming"] } };
        const cursor = students.find(query);
        const multiHobbyStudents = await cursor.toArray();
        console.log(multiHobbyStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

$all操作符要求数组中必须包含指定的所有元素才满足条件。

要查询爱好数组中元素个数大于2的学生,可以使用$size操作符:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询爱好数组中元素个数大于2的学生
        const query = { hobbies: { $size: { $gt: 2 } } };
        const cursor = students.find(query);
        const manyHobbiesStudents = await cursor.toArray();
        console.log(manyHobbiesStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里$size操作符用于获取数组的大小,结合$gt比较操作符,查询出爱好数组元素个数大于2的学生。

2.3 嵌套文档查询

假设students集合中的文档有更复杂的嵌套结构,例如每个学生有一个address字段,address又是一个包含city(城市)和street(街道)的文档:

{
    "name": "Bob",
    "age": 22,
    "score": 78,
    "address": {
        "city": "New York",
        "street": "Main St"
    }
}

要查询地址在“New York”的学生,可以这样写查询:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询地址在New York的学生
        const query = { "address.city": "New York" };
        const cursor = students.find(query);
        const nycStudents = await cursor.toArray();
        console.log(nycStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里使用点号(.)表示法来指定嵌套文档中的字段。"address.city"表示address文档中的city字段。

如果要查询地址在“New York”且街道是“Main St”的学生,可以组合多个条件:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 查询地址在New York且街道是Main St的学生
        const query = {
            "address.city": "New York",
            "address.street": "Main St"
        };
        const cursor = students.find(query);
        const specificAddressStudents = await cursor.toArray();
        console.log(specificAddressStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里在查询条件中同时指定了address.cityaddress.street的条件,只有同时满足这两个条件的文档才会被返回。

2.4 投影查询

投影查询用于指定返回文档中需要包含哪些字段,以及排除哪些字段。例如,我们只希望查询学生的名字和年龄,而不返回分数和其他字段,可以这样写:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 只查询名字和年龄
        const projection = { name: 1, age: 1, _id: 0 };
        const cursor = students.find({}, projection);
        const nameAndAgeStudents = await cursor.toArray();
        console.log(nameAndAgeStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

projection对象中,值为1表示包含该字段,值为0表示排除该字段。需要注意的是,_id字段默认是包含的,如果不想返回_id字段,需要显式设置为0。

如果只想排除分数字段,其他字段都返回,可以这样写:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 排除分数字段
        const projection = { score: 0 };
        const cursor = students.find({}, projection);
        const withoutScoreStudents = await cursor.toArray();
        console.log(withoutScoreStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里只设置了score字段为0,其他未指定的字段都会被包含在返回结果中。

2.5 排序查询

可以使用sort()方法对查询结果进行排序。例如,按照学生的年龄从小到大排序:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 按照年龄从小到大排序
        const sort = { age: 1 };
        const cursor = students.find({}).sort(sort);
        const sortedStudents = await cursor.toArray();
        console.log(sortedStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

sort对象中,值为1表示升序排序,值为 -1 表示降序排序。如果要按照分数从高到低排序,可以这样写:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 按照分数从高到低排序
        const sort = { score: -1 };
        const cursor = students.find({}).sort(sort);
        const highToLowScoreStudents = await cursor.toArray();
        console.log(highToLowScoreStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

也可以按照多个字段排序,例如先按照年龄从小到大排序,年龄相同的再按照分数从高到低排序:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 先按年龄从小到大,年龄相同按分数从高到低排序
        const sort = { age: 1, score: -1 };
        const cursor = students.find({}).sort(sort);
        const multiSortedStudents = await cursor.toArray();
        console.log(multiSortedStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里sort对象中按照字段的顺序依次进行排序。

2.6 限制查询结果数量

使用limit()方法可以限制返回的文档数量。例如,只返回前5个学生:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 只返回前5个学生
        const cursor = students.find({}).limit(5);
        const firstFiveStudents = await cursor.toArray();
        console.log(firstFiveStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

limit(5)表示只返回5个文档。如果要跳过前面的一些文档,从第3个文档开始返回,可以使用skip()方法结合limit()方法。例如,返回第3到第7个学生:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        // 返回第3到第7个学生
        const cursor = students.find({}).skip(2).limit(5);
        const middleStudents = await cursor.toArray();
        console.log(middleStudents);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里skip(2)表示跳过前面2个文档,limit(5)表示从第3个文档开始返回5个文档,所以最终返回第3到第7个学生的文档。

2.7 聚合查询

聚合查询是MongoDB中非常强大的功能,它可以对数据进行复杂的处理和分析。聚合操作使用aggregate()方法。

例如,计算学生的平均分数:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        const pipeline = [
            {
                $group: {
                    _id: null,
                    averageScore: { $avg: "$score" }
                }
            }
        ];
        const result = await students.aggregate(pipeline).toArray();
        console.log(result);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

在这个聚合管道中,$group操作符用于对文档进行分组,_id: null表示将所有文档分为一组。$avg是聚合操作符,用于计算平均值,"$score"表示对score字段进行计算。最终结果是一个包含平均分数的文档。

如果要按照年龄分组,计算每个年龄组的学生平均分数,可以这样写:

async function run() {
    try {
        await client.connect();
        const database = client.db('school');
        const students = database.collection('students');

        const pipeline = [
            {
                $group: {
                    _id: "$age",
                    averageScore: { $avg: "$score" }
                }
            }
        ];
        const result = await students.aggregate(pipeline).toArray();
        console.log(result);
    } finally {
        await client.close();
    }
}
run().catch(console.dir);

这里_id: "$age"表示按照age字段进行分组,然后分别计算每个年龄组的平均分数。

聚合操作还支持其他很多操作符,如$sum(求和)、$min(求最小值)、$max(求最大值)等,可以根据具体需求组合使用这些操作符来完成复杂的数据分析任务。

通过以上对MongoDB基本查询和高级查询的详细介绍,相信你已经对MongoDB的查询操作有了深入的理解。在实际应用中,可以根据具体的业务需求灵活运用这些查询技巧,高效地从MongoDB数据库中获取所需的数据。