MongoDB查询操作详解:基本查询与高级查询
一、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.city
和address.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数据库中获取所需的数据。