From 6d7504a4f0d7a8f721541ba08808909c6367c470 Mon Sep 17 00:00:00 2001 From: Guwan Date: Sun, 14 Sep 2025 20:38:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20mongo=E8=81=94=E6=9F=A5=E4=B8=80?= =?UTF-8?q?=E7=89=88-=E7=B4=A2=E5=BC=95=E5=B7=A6=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 167 ++++++++++++++++++ .../mongoObject/ShoppingRecord.java | 6 +- 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..57899d4 --- /dev/null +++ b/README.md @@ -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`构建复杂的聚合管道: + + +````java +PageResult 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()); +```` + + +## 项目结构 + +``` +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: ? }` | ❌(缺少最左) | diff --git a/src/main/java/org/example/mongosimple/mongoObject/ShoppingRecord.java b/src/main/java/org/example/mongosimple/mongoObject/ShoppingRecord.java index 30a389c..0e78393 100644 --- a/src/main/java/org/example/mongosimple/mongoObject/ShoppingRecord.java +++ b/src/main/java/org/example/mongosimple/mongoObject/ShoppingRecord.java @@ -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) {