MongoDB查询条件深度解析
MongoDB查询基础
在MongoDB中,查询操作是最为核心的功能之一,用于从集合(collection)中检索符合特定条件的文档(document)。最基本的查询语法使用find()
方法,其最简单的形式不包含任何条件,会返回集合中的所有文档。例如,假设我们有一个名为users
的集合,存储用户信息:
// 连接到MongoDB
const { MongoClient } = require('mongodb');
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
async function findAllUsers() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findAllUsers();
上述代码通过find({})
查询users
集合中的所有文档,并将结果打印出来。
简单条件查询
比较运算符
- 等于($eq):默认情况下,
find()
方法中的条件如果是简单的键值对,就表示$eq
操作。例如,要查找年龄为25岁的用户:
async function findUserByAge() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ age: 25 });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserByAge();
这与使用显式的$eq
运算符效果相同:
async function findUserByAgeWithEq() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ age: { $eq: 25 } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserByAgeWithEq();
- 大于($gt)、大于等于($gte)、小于($lt)、小于等于($lte):这些运算符用于数值比较。比如,查找年龄大于30岁的用户:
async function findUserOlderThan30() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ age: { $gt: 30 } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserOlderThan30();
要查找年龄大于等于20岁且小于等于30岁的用户,可以组合使用$gte
和$lte
:
async function findUserInAgeRange() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ age: { $gte: 20, $lte: 30 } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserInAgeRange();
- 不等于($ne):用于查找与指定值不相等的文档。例如,查找年龄不等于25岁的用户:
async function findUserNot25() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ age: { $ne: 25 } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserNot25();
逻辑运算符
- 与($and):
$and
运算符用于连接多个条件,只有当所有条件都满足时,文档才会被返回。假设users
集合中的文档还包含city
字段,要查找年龄大于25岁且居住在“Beijing”的用户:
async function findUserInBeijingAndOlder() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({
$and: [
{ age: { $gt: 25 } },
{ city: 'Beijing' }
]
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserInBeijingAndOlder();
- 或($or):
$or
运算符连接多个条件,只要有一个条件满足,文档就会被返回。比如,查找年龄大于30岁或者居住在“Shanghai”的用户:
async function findUserOlderOrInShanghai() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({
$or: [
{ age: { $gt: 30 } },
{ city: 'Shanghai' }
]
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserOlderOrInShanghai();
- 非($not):
$not
运算符用于对单个条件取反。例如,查找年龄不大于25岁的用户(等价于年龄小于等于25岁):
async function findUserNotOlderThan25() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ age: { $not: { $gt: 25 } } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserNotOlderThan25();
- 与非($nor):
$nor
运算符连接多个条件,只有当所有条件都不满足时,文档才会被返回。比如,查找年龄不大于25岁且不住在“Beijing”的用户:
async function findUserNotInBeijingAndNotOlder() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({
$nor: [
{ age: { $gt: 25 } },
{ city: 'Beijing' }
]
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserNotInBeijingAndNotOlder();
字段存在性查询
$exists运算符
$exists
运算符用于判断文档中是否存在某个字段。例如,要查找users
集合中存在email
字段的用户:
async function findUserWithEmail() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ email: { $exists: true } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWithEmail();
要查找不存在phone
字段的用户,可以将$exists
的值设为false
:
async function findUserWithoutPhone() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ phone: { $exists: false } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWithoutPhone();
数组相关查询
数组包含元素查询
- 简单包含(直接匹配):如果集合中的文档包含数组字段,例如
users
集合中的hobbies
字段是一个数组,要查找爱好中有“reading”的用户:
async function findUserWithReadingHobby() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ hobbies: 'reading' });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWithReadingHobby();
- $in运算符:
$in
运算符用于查询数组字段中包含多个值中的任意一个的文档。比如,查找爱好中有“reading”或者“swimming”的用户:
async function findUserWithCertainHobbies() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ hobbies: { $in: ['reading', 'swimming'] } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWithCertainHobbies();
数组元素个数查询
$size
运算符用于查询数组字段具有特定元素个数的文档。例如,查找hobbies
数组中有3个爱好的用户:
async function findUserWith3Hobbies() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ hobbies: { $size: 3 } });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWith3Hobbies();
数组中对象的查询
假设users
集合中的文档addresses
字段是一个包含对象的数组,每个对象包含city
和street
字段。要查找住在“Beijing”的用户的地址:
async function findUserInBeijingAddresses() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ "addresses.city": 'Beijing' });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserInBeijingAddresses();
如果要更精确地查询数组中对象的多个条件,比如住在“Beijing”且街道是“Wangfujing Street”的地址:
async function findUserInBeijingWangfujing() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({
"addresses": {
$elemMatch: {
city: 'Beijing',
street: 'Wangfujing Street'
}
}
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserInBeijingWangfujing();
这里的$elemMatch
用于匹配数组中满足所有指定条件的元素。
正则表达式查询
MongoDB支持使用正则表达式进行文本查询。例如,要查找name
字段以“J”开头的用户:
async function findUserWithNameStartingWithJ() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ name: /^J/ });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWithNameStartingWithJ();
如果要查找name
字段包含“on”的用户:
async function findUserWithNameContainingOn() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ name: /on/ });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWithNameContainingOn();
正则表达式在MongoDB查询中非常强大,可以进行复杂的文本匹配,结合i
标志可以进行不区分大小写的查询。例如,查找name
字段包含“jo”不区分大小写的用户:
async function findUserWithNameContainingJoIgnoreCase() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({ name: /jo/i });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserWithNameContainingJoIgnoreCase();
地理空间查询
MongoDB对地理空间数据的查询提供了很好的支持,特别是对于存储位置信息的应用场景。假设我们有一个restaurants
集合,每个文档包含一个location
字段,存储餐厅的经纬度坐标(以GeoJSON格式)。
基于距离的查询
- $near运算符:
$near
用于查找距离某个点一定范围内的文档。首先,需要确保location
字段建立了2dsphere索引:
async function create2dsphereIndex() {
try {
await client.connect();
const database = client.db('test');
const restaurants = database.collection('restaurants');
await restaurants.createIndex({ location: "2dsphere" });
console.log('Index created successfully');
} finally {
await client.close();
}
}
create2dsphereIndex();
然后,假设我们要查找距离坐标[longitude, latitude]
10公里内的餐厅:
async function findNearbyRestaurants() {
try {
await client.connect();
const database = client.db('test');
const restaurants = database.collection('restaurants');
const center = [-73.9857, 40.7588];
const cursor = restaurants.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: center
},
$maxDistance: 10000 // 10公里,单位为米
}
}
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findNearbyRestaurants();
- $geoWithin运算符:
$geoWithin
可以用于查询在一个给定几何形状内的文档。例如,查询在一个多边形内的餐厅:
async function findRestaurantsInPolygon() {
try {
await client.connect();
const database = client.db('test');
const restaurants = database.collection('restaurants');
const polygon = {
type: "Polygon",
coordinates: [
[
[-73.99, 40.74],
[-73.97, 40.74],
[-73.97, 40.76],
[-73.99, 40.76],
[-73.99, 40.74]
]
]
};
const cursor = restaurants.find({
location: {
$geoWithin: {
$geometry: polygon
}
}
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findRestaurantsInPolygon();
文本搜索
MongoDB支持文本搜索,这对于处理大量文本数据非常有用。首先,需要在要搜索的字段上创建文本索引。假设articles
集合包含title
和content
字段,我们要对这两个字段进行文本搜索:
async function createTextIndex() {
try {
await client.connect();
const database = client.db('test');
const articles = database.collection('articles');
await articles.createIndex({ title: "text", content: "text" });
console.log('Text index created successfully');
} finally {
await client.close();
}
}
createTextIndex();
然后,可以使用$text
运算符进行文本搜索。例如,搜索标题或内容中包含“mongodb”的文章:
async function searchArticles() {
try {
await client.connect();
const database = client.db('test');
const articles = database.collection('articles');
const cursor = articles.find({
$text: {
$search: "mongodb"
}
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
searchArticles();
文本搜索还支持一些选项,比如$language
用于指定语言,$caseSensitive
用于指定是否区分大小写等。例如,进行不区分大小写的法语文章搜索:
async function searchFrenchArticles() {
try {
await client.connect();
const database = client.db('test');
const articles = database.collection('articles');
const cursor = articles.find({
$text: {
$search: "mongodb",
$language: "fr",
$caseSensitive: false
}
});
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
searchFrenchArticles();
聚合查询中的查询条件
聚合查询(aggregation)在MongoDB中用于对数据进行复杂的处理和分析。在聚合管道(aggregation pipeline)中,$match
阶段可以用于筛选文档,其语法与find()
方法中的查询条件类似。例如,假设orders
集合存储订单信息,每个订单包含amount
(金额)和status
(状态)字段。要统计金额大于100且状态为“completed”的订单数量:
async function countCompletedOrders() {
try {
await client.connect();
const database = client.db('test');
const orders = database.collection('orders');
const pipeline = [
{
$match: {
amount: { $gt: 100 },
status: 'completed'
}
},
{
$count: 'total'
}
];
const result = await orders.aggregate(pipeline).toArray();
console.log(result);
} finally {
await client.close();
}
}
countCompletedOrders();
在聚合中,$match
阶段可以放在管道的不同位置,合理安排其位置可以提高查询效率。例如,如果数据量很大,先使用$match
筛选出一部分数据,再进行后续的聚合操作,会减少处理的数据量,从而提升性能。
查询优化
- 索引的使用:合理创建索引是优化查询的关键。通过
explain()
方法可以查看查询的执行计划,了解索引的使用情况。例如,对于前面查找年龄大于30岁的用户的查询:
async function explainQuery() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const result = await users.find({ age: { $gt: 30 } }).explain('executionStats');
console.log(result);
} finally {
await client.close();
}
}
explainQuery();
如果查询没有使用索引,可以考虑在age
字段上创建索引:
async function createAgeIndex() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
await users.createIndex({ age: 1 });
console.log('Age index created successfully');
} finally {
await client.close();
}
}
createAgeIndex();
- 减少返回字段:只返回需要的字段可以减少网络传输和处理的数据量。例如,只返回
users
集合中用户的name
和age
字段:
async function findUserLimitedFields() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const cursor = users.find({}, { name: 1, age: 1, _id: 0 });
const results = await cursor.toArray();
console.log(results);
} finally {
await client.close();
}
}
findUserLimitedFields();
这里_id
字段默认会返回,如果不需要可以显式设置为0。
- 批量操作:对于多次查询操作,可以考虑合并为批量操作,减少与数据库的交互次数。例如,使用
bulkWrite()
方法批量插入和更新文档。假设我们有多个用户数据要插入users
集合:
async function bulkInsertUsers() {
try {
await client.connect();
const database = client.db('test');
const users = database.collection('users');
const operations = [
{ insertOne: { document: { name: 'User1', age: 28 } } },
{ insertOne: { document: { name: 'User2', age: 30 } } }
];
const result = await users.bulkWrite(operations);
console.log(result);
} finally {
await client.close();
}
}
bulkInsertUsers();
通过深入理解和灵活运用上述MongoDB查询条件,开发人员可以高效地从数据库中检索和处理所需的数据,满足各种复杂的业务需求。在实际应用中,还需要根据数据量、查询频率等因素进行优化,以确保系统的高性能和稳定性。