feat: mongo联查一版-索引左匹配

This commit is contained in:
Guwan 2025-09-14 20:38:54 +08:00
parent 404d30bee9
commit 6d7504a4f0
2 changed files with 172 additions and 1 deletions

167
README.md Normal file
View File

@ -0,0 +1,167 @@
# MongoDB联查测试项目
一个简单的MongoDB多表联查测试项目演示如何使用Spring Boot + MongoDB进行复杂的聚合查询。
## 功能特性
- **5表联查**:购物记录、用户、地址、产品、分类的复杂关联查询
- **动态条件**:支持多种查询条件的动态组合
- **分页查询**使用MongoDB Facet操作实现高效分页
- **灵活排序**:支持多字段排序
## 技术栈
- Spring Boot 2.7.18
- Spring Data MongoDB
- Java 21
- Lombok
## 快速开始
### 1. 启动MongoDB
```bash
# 使用Docker
docker run -d -p 27017:27017 --name mongodb mongo:latest
# 或启动本地MongoDB
mongod --dbpath /path/to/your/db
```
### 2. 运行项目
```bash
mvn spring-boot:run
```
### 3. 测试接口
```bash
# 基本查询
curl "http://localhost:8080/test/search?phase=双十一&user=alice&page=0&size=10"
# 价格范围查询
curl "http://localhost:8080/test/search?minPrice=100&maxPrice=1000&page=0&size=10"
# 城市筛选
curl "http://localhost:8080/test/search?city=北京&category=电子产品&page=0&size=10"
```
## 数据模型
### 核心集合
- **shopping_record**: 购物记录(主表)
- **users**: 用户信息
- **address**: 地址信息
- **product**: 产品信息
- **categories**: 分类信息
- **shopping_phase**: 购物阶段
### 关联关系
```
shopping_record
├── userId -> users._id
├── productId -> product._id
├── shoppingPhaseId -> shopping_phase._id
└── users.addressId -> address._id
└── product.categoryId -> categories._id
```
## API接口
### 搜索购物记录
`GET /test/search`
**参数**:
- `phase`: 购物阶段名称
- `user`: 用户名模糊匹配
- `city`: 城市名称
- `category`: 分类名称
- `minPrice`: 最低价格
- `maxPrice`: 最高价格
- `page`: 页码从0开始
- `size`: 每页大小
- `sortField`: 排序字段
- `ascending`: 是否升序
**响应**:
```json
{
"success": true,
"data": [
{
"username": "alice",
"email": "alice@example.com",
"productName": "iPhone 15",
"price": 7999.00,
"categoryName": "电子产品"
}
],
"pagination": {
"page": 0,
"size": 10,
"total": 1,
"totalPages": 1,
"hasNext": false,
"hasPrevious": false
}
}
```
## 核心实现
### 聚合查询构建器
使用自定义的`MongoAggregationBuilder`构建复杂的聚合管道:
<augment_code_snippet path="src/main/java/org/example/mongosimple/service/ShoppingAggregationService.java" mode="EXCERPT">
````java
PageResult<ShoppingResult> result = MongoAggregationBuilder.fromWithCompatibility(mongoTemplate, "shopping_record")
// 关联所有需要的表
.lookupAndUnwind("shopping_phase", "shoppingPhaseId", "_id", "phase")
.lookupAndUnwind("users", "userId", "_id", "user")
.lookupAndUnwind("address", "user.addressId", "_id", "address")
.lookupAndUnwind("product", "productId", "_id", "product")
.lookupAndUnwind("categories", "product.categoryId", "_id", "category")
// 动态条件构建
.matchDynamic(criteria -> criteria
.whenNotEmpty(params.getPhaseName(), "phase.name")
.whenNotEmptyLike(params.getUsernamePattern(), "user.username")
.whenNotEmpty(params.getCity(), "address.city")
.whenRange(params.getMinPrice(), params.getMaxPrice(), "product.price")
)
// 投影和分页
.project(project -> project
.and("user.username").as("username")
.and("product.name").as("productName")
.and("product.price").as("price")
)
.sort(params.getSortField(), params.isAscending())
.executeWithPagination(ShoppingResult.class, params.getPage(), params.getSize());
````
</augment_code_snippet>
## 项目结构
```
src/main/java/org/example/mongosimple/
├── controller/ # REST控制器
├── service/ # 业务逻辑层
├── mongoObject/ # MongoDB文档实体
├── entity/ # 查询结果实体
└── prepareEntity/ # 查询构建工具类
```
## 开发说明
这是一个专注于MongoDB联查功能的测试项目去除了复杂的缓存、监控、导出等功能专注于核心的聚合查询实现。
适合用于:
- 学习MongoDB聚合查询
- 测试复杂的多表关联查询
- 验证查询性能和结果准确性
| 查询条件 | 是否命中复合索引 |
| -------------------------------------- | -------- |
| `{ user_id: ? }` | ✅(最左前缀) |
| `{ user_id: ?, shopping_phase_id: ? }` | ✅(完整索引) |
| `{ shopping_phase_id: ? }` | ❌(缺少最左) |

View File

@ -1,8 +1,11 @@
package org.example.mongosimple.mongoObject; package org.example.mongosimple.mongoObject;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Document;
@CompoundIndex(def = "{'shopping_phase_id': 1, 'user_id': 1}")
@Document(collection = "shopping_record") @Document(collection = "shopping_record")
public class ShoppingRecord { public class ShoppingRecord {
@Id @Id
@ -11,7 +14,8 @@ public class ShoppingRecord {
private String userId; private String userId;
private String productId; private String productId;
// unique = false默认) 普通索引允许重复值
@Indexed
private String shoppingPhaseId; private String shoppingPhaseId;
public ShoppingRecord(String userId, String productId, String shoppingPhaseId) { public ShoppingRecord(String userId, String productId, String shoppingPhaseId) {