Compare commits

...

2 Commits

Author SHA1 Message Date
Guwan 6d7504a4f0 feat: mongo联查一版-索引左匹配 2025-09-14 20:38:54 +08:00
Guwan 404d30bee9 feat: mongo联查一版-清楚无用代码 2025-09-14 19:43:11 +08:00
5 changed files with 176 additions and 18 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

@ -3,21 +3,16 @@ package org.example.mongosimple.controller;
import lombok.RequiredArgsConstructor;
import org.example.mongosimple.entity.ShoppingResult;
import org.example.mongosimple.prepareEntity.MongoAggregationBuilder;
import org.example.mongosimple.prepareEntity.PageResult;
import org.example.mongosimple.prepareEntity.ShoppingSearchParams;
import org.example.mongosimple.service.ShoppingAggregationService;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**

View File

@ -28,13 +28,13 @@ public class DataInitializer implements CommandLineRunner {
// System.out.println("开始初始化测试数据...");
// 初始化用户数据
// initUsers();
// init();
// System.out.println("测试数据初始化完成!");
}
private void initUsers() {
private void init() {
// System.out.println("初始化用户数据...");
Address address = new Address();
@ -79,7 +79,7 @@ public class DataInitializer implements CommandLineRunner {
mongoTemplate.save(product1);
mongoTemplate.save(product2);
// 来自的dezhou的用户 在618期间买的 价格位于 5000 - 6000 的电子产品z
// 来自的dezhou的用户 在618期间买的 价格位于 5000 - 6000 的电子产品
ShoppingRecord shoppingRecord = new ShoppingRecord("68c4fabe669d40228340eaa9", "68c4fcd9f53fc172ab23f4ac", "68c4fb3a370a3428ec124244");
ShoppingRecord shoppingRecord1 = new ShoppingRecord("68c4fabe669d40228340eaab", "68c4fcd9f53fc172ab23f4ac", "68c4fb3a370a3428ec124244");

View File

@ -1,8 +1,11 @@
package org.example.mongosimple.mongoObject;
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;
@CompoundIndex(def = "{'shopping_phase_id': 1, 'user_id': 1}")
@Document(collection = "shopping_record")
public class ShoppingRecord {
@Id
@ -11,7 +14,8 @@ public class ShoppingRecord {
private String userId;
private String productId;
// unique = false默认) 普通索引允许重复值
@Indexed
private String shoppingPhaseId;
public ShoppingRecord(String userId, String productId, String shoppingPhaseId) {

View File

@ -529,15 +529,7 @@ public class MongoAggregationBuilder {
* 自动添加字段展平投影
*/
public static MongoAggregationBuilder fromWithCompatibility(MongoTemplate mongoTemplate, String sourceCollection) {
MongoAggregationBuilder builder = new MongoAggregationBuilder(mongoTemplate, sourceCollection);
// 检查是否需要兼容性处理
if (!SpringBootCompatibilityHelper.supportsNestedFieldReferences()) {
System.out.println("检测到Spring Boot 2环境启用兼容性模式");
// 在后续的lookup操作后自动添加字段展平
}
return builder;
return new MongoAggregationBuilder(mongoTemplate, sourceCollection);
}
/**