feat: mongo联查一版-索引左匹配
This commit is contained in:
parent
404d30bee9
commit
6d7504a4f0
|
@ -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: ? }` | ❌(缺少最左) |
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue