Appearance
4.2 查询文档 (Read)
概述
查询文档是MongoDB中最常用的操作之一,用于从集合中检索符合条件的数据。MongoDB提供了强大的查询功能,支持简单的条件查询、复杂的聚合查询、全文搜索等多种查询方式。本章节将详细介绍MongoDB文档查询的各种方法、查询操作符和性能优化技巧。
MongoDB的查询系统具有灵活性、高性能和丰富功能的特点。掌握查询操作对于构建高效的数据检索系统至关重要。
基本概念
查询方法
MongoDB提供以下查询方法:
- findOne():查询单个文档
- find():查询多个文档
- aggregate():聚合查询
- distinct():获取唯一值
- countDocuments():统计文档数量
查询语法
MongoDB查询使用以下语法:
- 简单查询:
{field: value} - 比较查询:
{field: {$operator: value}} - 逻辑查询:
{$operator: [{condition1}, {condition2}]} - 数组查询:
{field: {$operator: value}} - 嵌套查询:
{'field.nested': value}
查询选项
查询支持以下选项:
- projection:指定返回字段
- sort:排序结果
- limit:限制返回数量
- skip:跳过指定数量
- hint:使用指定索引
原理深度解析
单文档查询原理
使用findOne()方法查询单个文档:
php
<?php
// 单文档查询详解
class SingleDocumentQuery {
private $database;
public function __construct($databaseName) {
$client = new MongoDB\Client("mongodb://localhost:27017");
$this->database = $client->selectDatabase($databaseName);
}
public function findById($collectionName, $id) {
$collection = $this->database->selectCollection($collectionName);
try {
$document = $collection->findOne(['_id' => new MongoDB\BSON\ObjectId($id)]);
if ($document) {
return [
'found' => true,
'document' => $document
];
} else {
return [
'found' => false,
'message' => 'Document not found'
];
}
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findByField($collectionName, $field, $value) {
$collection = $this->database->selectCollection($collectionName);
try {
$document = $collection->findOne([$field => $value]);
if ($document) {
return [
'found' => true,
'document' => $document
];
} else {
return [
'found' => false,
'message' => 'Document not found'
];
}
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findByMultipleFields($collectionName, $conditions) {
$collection = $this->database->selectCollection($collectionName);
try {
$document = $collection->findOne($conditions);
if ($document) {
return [
'found' => true,
'document' => $document
];
} else {
return [
'found' => false,
'message' => 'Document not found'
];
}
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findWithProjection($collectionName, $filter, $projection) {
$collection = $this->database->selectCollection($collectionName);
try {
$document = $collection->findOne($filter, ['projection' => $projection]);
if ($document) {
return [
'found' => true,
'document' => $document,
'projection_applied' => true
];
} else {
return [
'found' => false,
'message' => 'Document not found'
];
}
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findWithSort($collectionName, $filter, $sort) {
$collection = $this->database->selectCollection($collectionName);
try {
$document = $collection->findOne($filter, ['sort' => $sort]);
if ($document) {
return [
'found' => true,
'document' => $document,
'sort_applied' => $sort
];
} else {
return [
'found' => false,
'message' => 'Document not found'
];
}
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
}
// 使用示例
$singleQuery = new SingleDocumentQuery('testdb');
// 按ID查询
$idResult = $singleQuery->findById('users', '507f1f77bcf86cd799439011');
print_r($idResult);
// 按字段查询
$fieldResult = $singleQuery->findByField('users', 'email', 'john@example.com');
print_r($fieldResult);
// 按多个字段查询
$conditions = [
'name' => 'John Doe',
'active' => true
];
$multipleResult = $singleQuery->findByMultipleFields('users', $conditions);
print_r($multipleResult);
// 带投影的查询
$projection = [
'name' => 1,
'email' => 1,
'age' => 1
];
$projectionResult = $singleQuery->findWithProjection('users', ['active' => true], $projection);
print_r($projectionResult);
// 带排序的查询
$sort = ['created_at' => -1];
$sortResult = $singleQuery->findWithSort('users', ['active' => true], $sort);
print_r($sortResult);
?>多文档查询原理
使用find()方法查询多个文档:
php
<?php
// 多文档查询详解
class MultipleDocumentQuery {
private $database;
public function __construct($databaseName) {
$client = new MongoDB\Client("mongodb://localhost:27017");
$this->database = $client->selectDatabase($databaseName);
}
public function findAll($collectionName, $filter = []) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find($filter);
$documents = iterator_to_array($cursor);
return [
'success' => true,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findWithLimit($collectionName, $filter, $limit) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find($filter, ['limit' => $limit]);
$documents = iterator_to_array($cursor);
return [
'success' => true,
'limit' => $limit,
'returned_count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findWithPagination($collectionName, $filter, $page, $pageSize) {
$collection = $this->database->selectCollection($collectionName);
try {
$skip = ($page - 1) * $pageSize;
$cursor = $collection->find($filter, [
'skip' => $skip,
'limit' => $pageSize,
'sort' => ['_id' => 1]
]);
$documents = iterator_to_array($cursor);
// 获取总数
$totalCount = $collection->countDocuments($filter);
$totalPages = ceil($totalCount / $pageSize);
return [
'success' => true,
'page' => $page,
'page_size' => $pageSize,
'total_count' => $totalCount,
'total_pages' => $totalPages,
'returned_count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findWithSort($collectionName, $filter, $sort, $options = []) {
$collection = $this->database->selectCollection($collectionName);
try {
$defaultOptions = ['sort' => $sort];
$queryOptions = array_merge($defaultOptions, $options);
$cursor = $collection->find($filter, $queryOptions);
$documents = iterator_to_array($cursor);
return [
'success' => true,
'sort' => $sort,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findWithProjection($collectionName, $filter, $projection, $options = []) {
$collection = $this->database->selectCollection($collectionName);
try {
$defaultOptions = ['projection' => $projection];
$queryOptions = array_merge($defaultOptions, $options);
$cursor = $collection->find($filter, $queryOptions);
$documents = iterator_to_array($cursor);
return [
'success' => true,
'projection' => $projection,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function findWithComplexOptions($collectionName, $filter, $options) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find($filter, $options);
$documents = iterator_to_array($cursor);
return [
'success' => true,
'options' => $options,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
}
// 使用示例
$multipleQuery = new MultipleDocumentQuery('testdb');
// 查询所有文档
$allResult = $multipleQuery->findAll('users', ['active' => true]);
print_r($allResult);
// 带限制的查询
$limitResult = $multipleQuery->findWithLimit('users', ['active' => true], 10);
print_r($limitResult);
// 分页查询
$paginationResult = $multipleQuery->findWithPagination('users', ['active' => true], 2, 10);
print_r($paginationResult);
// 带排序的查询
$sort = ['created_at' => -1];
$sortResult = $multipleQuery->findWithSort('users', ['active' => true], $sort);
print_r($sortResult);
// 带投影的查询
$projection = [
'name' => 1,
'email' => 1,
'age' => 1
];
$projectionResult = $multipleQuery->findWithProjection('users', ['active' => true], $projection);
print_r($projectionResult);
// 复杂选项查询
$complexOptions = [
'projection' => ['name' => 1, 'email' => 1],
'sort' => ['created_at' => -1],
'limit' => 5,
'skip' => 10
];
$complexResult = $multipleQuery->findWithComplexOptions('users', ['active' => true], $complexOptions);
print_r($complexResult);
?>比较查询操作符
使用比较操作符进行条件查询:
php
<?php
// 比较查询操作符详解
class ComparisonQueryOperators {
private $database;
public function __construct($databaseName) {
$client = new MongoDB\Client("mongodb://localhost:27017");
$this->database = $client->selectDatabase($databaseName);
}
public function queryWithGreaterThan($collectionName, $field, $value) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find([$field => ['$gt' => $value]]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$gt',
'field' => $field,
'value' => $value,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithLessThan($collectionName, $field, $value) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find([$field => ['$lt' => $value]]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$lt',
'field' => $field,
'value' => $value,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithRange($collectionName, $field, $minValue, $maxValue) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find([
$field => [
'$gte' => $minValue,
'$lte' => $maxValue
]
]);
$documents = iterator_to_array($cursor);
return [
'operator' => 'range',
'field' => $field,
'range' => [$minValue, $maxValue],
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithNotEqual($collectionName, $field, $value) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find([$field => ['$ne' => $value]]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$ne',
'field' => $field,
'value' => $value,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithIn($collectionName, $field, $values) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find([$field => ['$in' => $values]]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$in',
'field' => $field,
'values' => $values,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithNotIn($collectionName, $field, $values) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find([$field => ['$nin' => $values]]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$nin',
'field' => $field,
'values' => $values,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function demonstrateAllComparisonOperators($collectionName, $field, $testValue) {
$collection = $this->database->selectCollection($collectionName);
$operators = [
'$eq' => [$field => $testValue],
'$gt' => [$field => ['$gt' => $testValue]],
'$gte' => [$field => ['$gte' => $testValue]],
'$lt' => [$field => ['$lt' => $testValue]],
'$lte' => [$field => ['$lte' => $testValue]],
'$ne' => [$field => ['$ne' => $testValue]],
'$in' => [$field => ['$in' => [$testValue, $testValue + 1, $testValue + 2]]],
'$nin' => [$field => ['$nin' => [$testValue, $testValue + 1, $testValue + 2]]]
];
$results = [];
foreach ($operators as $operator => $filter) {
try {
$cursor = $collection->find($filter);
$documents = iterator_to_array($cursor);
$results[$operator] = [
'count' => count($documents),
'sample_count' => min(3, count($documents)),
'samples' => array_slice($documents, 0, 3)
];
} catch (Exception $e) {
$results[$operator] = [
'error' => $e->getMessage()
];
}
}
return [
'field' => $field,
'test_value' => $testValue,
'operator_results' => $results
];
}
}
// 使用示例
$comparisonQuery = new ComparisonQueryOperators('testdb');
// 大于查询
$gtResult = $comparisonQuery->queryWithGreaterThan('users', 'age', 30);
print_r($gtResult);
// 小于查询
$ltResult = $comparisonQuery->queryWithLessThan('users', 'age', 30);
print_r($ltResult);
// 范围查询
$rangeResult = $comparisonQuery->queryWithRange('users', 'age', 25, 35);
print_r($rangeResult);
// 不等于查询
$neResult = $comparisonQuery->queryWithNotEqual('users', 'status', 'inactive');
print_r($neResult);
// IN查询
$inResult = $comparisonQuery->queryWithIn('users', 'role', ['admin', 'moderator']);
print_r($inResult);
// NOT IN查询
$ninResult = $comparisonQuery->queryWithNotIn('users', 'status', ['deleted', 'banned']);
print_r($ninResult);
// 演示所有比较操作符
$allOperators = $comparisonQuery->demonstrateAllComparisonOperators('users', 'age', 30);
print_r($allOperators);
?>逻辑查询操作符
使用逻辑操作符组合多个条件:
php
<?php
// 逻辑查询操作符详解
class LogicalQueryOperators {
private $database;
public function __construct($databaseName) {
$client = new MongoDB\Client("mongodb://localhost:27017");
$this->database = $client->selectDatabase($databaseName);
}
public function queryWithAnd($collectionName, $conditions) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find(['$and' => $conditions]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$and',
'conditions' => $conditions,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithOr($collectionName, $conditions) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find(['$or' => $conditions]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$or',
'conditions' => $conditions,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithNot($collectionName, $condition) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find(['$not' => $condition]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$not',
'condition' => $condition,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithNor($collectionName, $conditions) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find(['$nor' => $conditions]);
$documents = iterator_to_array($cursor);
return [
'operator' => '$nor',
'conditions' => $conditions,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function queryWithComplexLogic($collectionName, $logicalStructure) {
$collection = $this->database->selectCollection($collectionName);
try {
$cursor = $collection->find($logicalStructure);
$documents = iterator_to_array($cursor);
return [
'logical_structure' => $logicalStructure,
'count' => count($documents),
'documents' => $documents
];
} catch (Exception $e) {
return [
'error' => 'Query failed',
'message' => $e->getMessage()
];
}
}
public function demonstrateLogicalCombinations($collectionName) {
$collection = $this->database->selectCollection($collectionName);
$combinations = [
'simple_and' => [
'$and' => [
['age' => ['$gte' => 25]],
['age' => ['$lte' => 35]],
['active' => true]
]
],
'simple_or' => [
'$or' => [
['role' => 'admin'],
['role' => 'moderator']
]
],
'nested_logic' => [
'$and' => [
[
'$or' => [
['age' => ['$lt' => 25]],
['age' => ['$gt' => 65]]
]
],
['active' => true]
]
],
'complex_combination' => [
'$and' => [
['active' => true],
[
'$or' => [
['role' => 'admin'],
[
'$and' => [
['role' => 'user'],
['age' => ['$gte' => 18]]
]
]
]
]
]
]
];
$results = [];
foreach ($combinations as $name => $filter) {
try {
$cursor = $collection->find($filter);
$documents = iterator_to_array($cursor);
$results[$name] = [
'filter' => $filter,
'count' => count($documents),
'sample_count' => min(3, count($documents)),
'samples' => array_slice($documents, 0, 3)
];
} catch (Exception $e) {
$results[$name] = [
'error' => $e->getMessage()
];
}
}
return $results;
}
}
// 使用示例
$logicalQuery = new LogicalQueryOperators('testdb');
// AND查询
$andConditions = [
['age' => ['$gte' => 25]],
['age' => ['$lte' => 35]],
['active' => true]
];
$andResult = $logicalQuery->queryWithAnd('users', $andConditions);
print_r($andResult);
// OR查询
$orConditions = [
['role' => 'admin'],
['role' => 'moderator']
];
$orResult = $logicalQuery->queryWithOr('users', $orConditions);
print_r($orResult);
// NOT查询
$notCondition = ['age' => ['$lt' => 18]];
$notResult = $logicalQuery->queryWithNot('users', $notCondition);
print_r($notResult);
// NOR查询
$norConditions = [
['status' => 'deleted'],
['status' => 'banned']
];
$norResult = $logicalQuery->queryWithNor('users', $norConditions);
print_r($norResult);
// 复杂逻辑查询
$complexLogic = [
'$and' => [
['active' => true],
[
'$or' => [
['role' => 'admin'],
[
'$and' => [
['role' => 'user'],
['age' => ['$gte' => 18]]
]
]
]
]
]
];
$complexResult = $logicalQuery->queryWithComplexLogic('users', $complexLogic);
print_r($complexResult);
// 演示逻辑组合
$combinations = $logicalQuery->demonstrateLogicalCombinations('users');
print_r($combinations);
?>常见错误与踩坑点
错误1:查询字段不存在
问题描述:查询不存在的字段导致返回空结果。
php
<?php
// 错误示例 - 查询不存在的字段
try {
$client = new MongoDB\Client("mongodb://localhost:27017");
$database = $client->selectDatabase("testdb");
$collection = $database->selectCollection('users');
// 错误:查询不存在的字段
$result = $collection->find(['non_existent_field' => 'value'])->toArray();
echo "Found " . count($result) . " documents\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 正确示例 - 检查字段是否存在
$client = new MongoDB\Client("mongodb://localhost:27017");
$database = $client->selectDatabase("testdb");
$collection = $database->selectCollection('users');
// 检查字段是否存在
$sampleDocument = $collection->findOne();
if ($sampleDocument && isset($sampleDocument['email'])) {
$result = $collection->find(['email' => 'john@example.com'])->toArray();
echo "Found " . count($result) . " documents\n";
} else {
echo "Field 'email' does not exist in the collection\n";
}
// 或者使用$exists操作符
$result = $collection->find(['email' => ['$exists' => true]])->toArray();
echo "Found " . count($result) . " documents with email field\n";
?>错误2:类型不匹配查询
问题描述:查询时数据类型不匹配导致无法找到文档。
php
<?php
// 错误示例 - 类型不匹配查询
try {
$client = new MongoDB\Client("mongodb://localhost:27017");
$database = $client->selectDatabase("testdb");
$collection = $database->selectCollection('users');
// 假设age字段存储为整数
// 错误:使用字符串查询整数字段
$result = $collection->find(['age' => '30'])->toArray();
echo "Found " . count($result) . " documents\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 正确示例 - 使用正确的类型
$client = new MongoDB\Client("mongodb://localhost:27017");
$database = $client->selectDatabase("testdb");
$collection = $database->selectCollection('users');
// 使用整数查询
$result = $collection->find(['age' => 30])->toArray();
echo "Found " . count($result) . " documents\n";
// 或者使用类型转换
$result = $collection->find(['age' => ['$toString' => '30']])->toArray();
echo "Found " . count($result) . " documents with type conversion\n";
?>错误3:投影字段错误
问题描述:投影设置错误导致返回意外的结果。
php
<?php
// 错误示例 - 投影字段错误
try {
$client = new MongoDB\Client("mongodb://localhost:27017");
$database = $client->selectDatabase("testdb");
$collection = $database->selectCollection('users');
// 错误:同时使用包含和排除投影
$result = $collection->find(
['active' => true],
['projection' => ['name' => 1, 'email' => 0]]
)->toArray();
print_r($result);
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 正确示例 - 正确的投影设置
$client = new MongoDB\Client("mongodb://localhost:27017");
$database = $client->selectDatabase("testdb");
$collection = $database->selectCollection('users');
// 只包含指定字段
$result1 = $collection->find(
['active' => true],
['projection' => ['name' => 1, 'email' => 1, 'age' => 1]]
)->toArray();
echo "Included fields: " . count($result1) . " documents\n";
// 排除指定字段
$result2 = $collection->find(
['active' => true],
['projection' => ['password' => 0, 'metadata' => 0]]
)->toArray();
echo "Excluded fields: " . count($result2) . " documents\n";
// 使用_id的特殊处理
$result3 = $collection->find(
['active' => true],
['projection' => ['_id' => 0, 'name' => 1, 'email' => 1]]
)->toArray();
echo "Excluded _id: " . count($result3) . " documents\n";
?>常见应用场景
场景1:用户搜索系统
实现多条件用户搜索功能:
php
<?php
// 用户搜索系统
class UserSearchSystem {
private $database;
public function __construct($databaseName) {
$client = new MongoDB\Client("mongodb://localhost:27017");
$this->database = $client->selectDatabase($databaseName);
}
public function searchUsers($searchCriteria, $pagination = []) {
$collection = $this->database->selectCollection('users');
// 构建查询条件
$filter = $this->buildSearchFilter($searchCriteria);
// 设置投影
$projection = $this->buildProjection($searchCriteria);
// 设置排序
$sort = $this->buildSort($searchCriteria);
try {
// 分页参数
$page = $pagination['page'] ?? 1;
$pageSize = $pagination['page_size'] ?? 20;
$skip = ($page - 1) * $pageSize;
$options = [
'projection' => $projection,
'sort' => $sort,
'skip' => $skip,
'limit' => $pageSize
];
$cursor = $collection->find($filter, $options);
$users = iterator_to_array($cursor);
// 获取总数
$totalCount = $collection->countDocuments($filter);
$totalPages = ceil($totalCount / $pageSize);
return [
'success' => true,
'users' => $users,
'pagination' => [
'page' => $page,
'page_size' => $pageSize,
'total_count' => $totalCount,
'total_pages' => $totalPages,
'has_next' => $page < $totalPages,
'has_prev' => $page > 1
]
];
} catch (Exception $e) {
return [
'success' => false,
'error' => 'Search failed',
'message' => $e->getMessage()
];
}
}
private function buildSearchFilter($criteria) {
$filter = [];
// 激活状态
if (isset($criteria['active'])) {
$filter['active'] = $criteria['active'];
}
// 用户名搜索
if (!empty($criteria['name'])) {
$filter['name'] = new MongoDB\BSON\Regex($criteria['name'], 'i');
}
// 邮箱搜索
if (!empty($criteria['email'])) {
$filter['email'] = new MongoDB\BSON\Regex($criteria['email'], 'i');
}
// 年龄范围
if (isset($criteria['age_min']) || isset($criteria['age_max'])) {
$ageFilter = [];
if (isset($criteria['age_min'])) {
$ageFilter['$gte'] = $criteria['age_min'];
}
if (isset($criteria['age_max'])) {
$ageFilter['$lte'] = $criteria['age_max'];
}
$filter['age'] = $ageFilter;
}
// 角色筛选
if (!empty($criteria['roles'])) {
$filter['role'] = ['$in' => $criteria['roles']];
}
// 注册日期范围
if (isset($criteria['registered_from']) || isset($criteria['registered_to'])) {
$dateFilter = [];
if (isset($criteria['registered_from'])) {
$dateFilter['$gte'] = new MongoDB\BSON\UTCDateTime($criteria['registered_from']);
}
if (isset($criteria['registered_to'])) {
$dateFilter['$lte'] = new MongoDB\BSON\UTCDateTime($criteria['registered_to']);
}
$filter['created_at'] = $dateFilter;
}
return $filter;
}
private function buildProjection($criteria) {
$projection = [
'name' => 1,
'email' => 1,
'age' => 1,
'role' => 1,
'created_at' => 1
];
// 根据权限添加更多字段
if ($criteria['include_profile'] ?? false) {
$projection['profile'] = 1;
}
if ($criteria['include_metadata'] ?? false) {
$projection['metadata'] = 1;
}
return $projection;
}
private function buildSort($criteria) {
$sort = [];
// 默认按创建时间倒序
$sortField = $criteria['sort_field'] ?? 'created_at';
$sortOrder = $criteria['sort_order'] ?? -1;
$sort[$sortField] = $sortOrder;
return $sort;
}
public function searchUsersWithAggregation($searchCriteria, $pagination = []) {
$collection = $this->database->selectCollection('users');
// 构建聚合管道
$pipeline = [];
// 匹配阶段
$matchStage = $this->buildSearchFilter($searchCriteria);
if (!empty($matchStage)) {
$pipeline[] = ['$match' => $matchStage];
}
// 排序阶段
$sort = $this->buildSort($searchCriteria);
$pipeline[] = ['$sort' => $sort];
// 分页阶段
$page = $pagination['page'] ?? 1;
$pageSize = $pagination['page_size'] ?? 20;
$skip = ($page - 1) * $pageSize;
$pipeline[] = ['$skip' => $skip];
$pipeline[] = ['$limit' => $pageSize];
// 投影阶段
$projection = $this->buildProjection($searchCriteria);
$pipeline[] = ['$project' => $projection];
try {
$cursor = $collection->aggregate($pipeline);
$users = iterator_to_array($cursor);
return [
'success' => true,
'users' => $users,
'count' => count($users)
];
} catch (Exception $e) {
return [
'success' => false,
'error' => 'Aggregation search failed',
'message' => $e->getMessage()
];
}
}
}
// 使用示例
$userSearch = new UserSearchSystem('user_db');
// 基本搜索
$searchCriteria = [
'name' => 'John',
'age_min' => 25,
'age_max' => 40,
'roles' => ['user', 'admin']
];
$pagination = [
'page' => 1,
'page_size' => 10
];
$searchResult = $userSearch->searchUsers($searchCriteria, $pagination);
print_r($searchResult);
// 高级搜索
$advancedCriteria = [
'name' => 'Jane',
'email' => 'example.com',
'active' => true,
'registered_from' => strtotime('2024-01-01') * 1000,
'registered_to' => time() * 1000,
'include_profile' => true,
'sort_field' => 'created_at',
'sort_order' => -1
];
$advancedResult = $userSearch->searchUsers($advancedCriteria, $pagination);
print_r($advancedResult);
// 聚合搜索
$aggregationResult = $userSearch->searchUsersWithAggregation($searchCriteria, $pagination);
print_r($aggregationResult);
?>常见问题答疑
问题1:如何优化查询性能?
回答:通过索引、投影、限制结果等方式优化查询性能:
php
<?php
// 查询性能优化助手
class QueryPerformanceOptimizer {
private $database;
public function __construct($databaseName) {
$client = new MongoDB\Client("mongodb://localhost:27017");
$this->database = $client->selectDatabase($databaseName);
}
public function analyzeQueryPerformance($collectionName, $filter, $options = []) {
$collection = $this->database->selectCollection($collectionName);
// 执行查询
$startTime = microtime(true);
$cursor = $collection->find($filter, $options);
$documents = iterator_to_array($cursor);
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
// 分析结果
$analysis = [
'query_time_ms' => round($executionTime * 1000, 4),
'document_count' => count($documents),
'filter_complexity' => $this->analyzeFilterComplexity($filter),
'options_used' => array_keys($options),
'recommendations' => []
];
// 生成优化建议
if ($executionTime > 0.1) {
$analysis['recommendations'][] = [
'type' => 'performance',
'message' => 'Query execution time is high',
'suggestion' => 'Consider adding indexes for queried fields'
];
}
if (count($documents) > 1000 && !isset($options['limit'])) {
$analysis['recommendations'][] = [
'type' => 'pagination',
'message' => 'Large result set returned',
'suggestion' => 'Implement pagination with limit and skip'
];
}
if (!isset($options['projection'])) {
$analysis['recommendations'][] = [
'type' => 'network',
'message' => 'Full documents returned',
'suggestion' => 'Use projection to limit returned fields'
];
}
return $analysis;
}
private function analyzeFilterComplexity($filter) {
$complexity = 0;
if (isset($filter['$and'])) {
$complexity += count($filter['$and']);
}
if (isset($filter['$or'])) {
$complexity += count($filter['$or']) * 2;
}
foreach ($filter as $key => $value) {
if (is_array($value) && isset($value['$in'])) {
$complexity += count($value['$in']);
}
}
return $complexity;
}
}
// 使用示例
$optimizer = new QueryPerformanceOptimizer('testdb');
// 分析查询性能
$filter = [
'active' => true,
'age' => ['$gte' => 25, '$lte' => 35],
'role' => ['$in' => ['user', 'admin']]
];
$options = [
'sort' => ['created_at' => -1],
'limit' => 100
];
$performanceAnalysis = $optimizer->analyzeQueryPerformance('users', $filter, $options);
print_r($performanceAnalysis);
?>实战练习
练习1:实现分页查询系统
实现一个完整的分页查询系统:
php
<?php
// 分页查询系统
class PaginationQuerySystem {
private $database;
public function __construct($databaseName) {
$client = new MongoDB\Client("mongodb://localhost:27017");
$this->database = $client->selectDatabase($databaseName);
}
public function paginate($collectionName, $filter, $options = []) {
$collection = $this->database->selectCollection($collectionName);
$page = $options['page'] ?? 1;
$pageSize = $options['page_size'] ?? 20;
$sort = $options['sort'] ?? ['_id' => 1];
$projection = $options['projection'] ?? [];
try {
// 获取总数
$totalCount = $collection->countDocuments($filter);
// 计算分页信息
$totalPages = ceil($totalCount / $pageSize);
$skip = ($page - 1) * $pageSize;
// 构建查询选项
$queryOptions = [
'sort' => $sort,
'skip' => $skip,
'limit' => $pageSize
];
if (!empty($projection)) {
$queryOptions['projection'] = $projection;
}
// 执行查询
$cursor = $collection->find($filter, $queryOptions);
$documents = iterator_to_array($cursor);
return [
'success' => true,
'data' => $documents,
'pagination' => [
'page' => $page,
'page_size' => $pageSize,
'total_count' => $totalCount,
'total_pages' => $totalPages,
'has_next' => $page < $totalPages,
'has_prev' => $page > 1,
'next_page' => $page < $totalPages ? $page + 1 : null,
'prev_page' => $page > 1 ? $page - 1 : null
]
];
} catch (Exception $e) {
return [
'success' => false,
'error' => 'Pagination failed',
'message' => $e->getMessage()
];
}
}
public function getPaginationInfo($collectionName, $filter, $pageSize) {
$collection = $this->database->selectCollection($collectionName);
try {
$totalCount = $collection->countDocuments($filter);
$totalPages = ceil($totalCount / $pageSize);
return [
'total_count' => $totalCount,
'page_size' => $pageSize,
'total_pages' => $totalPages,
'last_page' => $totalPages
];
} catch (Exception $e) {
return [
'error' => 'Failed to get pagination info',
'message' => $e->getMessage()
];
}
}
}
// 使用示例
$paginationSystem = new PaginationQuerySystem('testdb');
// 分页查询
$filter = ['active' => true];
$options = [
'page' => 2,
'page_size' => 10,
'sort' => ['created_at' => -1],
'projection' => ['name' => 1, 'email' => 1, 'age' => 1]
];
$paginationResult = $paginationSystem->paginate('users', $filter, $options);
print_r($paginationResult);
// 获取分页信息
$paginationInfo = $paginationSystem->getPaginationInfo('users', $filter, 10);
print_r($paginationInfo);
?>知识点总结
核心概念
- 查询方法:
findOne()、find()、aggregate() - 查询语法:简单查询、比较查询、逻辑查询
- 查询选项:投影、排序、限制、跳过
- 查询性能:索引优化、结果限制、字段投影
查询操作符
- 比较操作符:
$eq、$gt、$gte、$lt、$lte、$ne、$in、$nin - 逻辑操作符:
$and、$or、$not、$nor - 元素操作符:
$exists、$type - 数组操作符:
$all、$elemMatch、$size
最佳实践
- 索引优化:为常用查询字段创建索引
- 结果限制:使用
limit()限制返回数量 - 字段投影:只返回需要的字段
- 分页查询:使用
skip()和limit()实现分页
常见场景
- 用户搜索:多条件组合查询
- 数据分页:skip和limit实现分页
- 范围查询:使用比较操作符
- 逻辑组合:使用逻辑操作符组合条件
拓展参考资料
官方文档
- MongoDB查询文档:https://docs.mongodb.com/manual/tutorial/query-documents/
- 查询操作符:https://docs.mongodb.com/manual/reference/operator/query/
- 查询优化:https://docs.mongodb.com/manual/tutorial/optimize-query-performance-with-indexes-and-projection/
推荐阅读
- 《MongoDB查询性能优化》
- 《MongoDB索引设计与优化》
- 《MongoDB聚合查询实战》
在线资源
- MongoDB University查询课程
- MongoDB官方博客查询优化文章
- Stack Overflow MongoDB查询相关问题
