MongoDB多嵌入式文档查询构建:Spring Boot Mongo API通知系统需求
首先我得明确你的需求:你要找订阅文档,要么完全没有criteria数组(或者数组为空),要么**criteria数组里的所有嵌入式文档都匹配指定的键值条件**,对吧?结合你的文档结构,我分两种场景给你具体实现方案,从MongoDB原生查询到Spring Boot Mongo API的代码都有。
一、先理清楚文档结构
假设你的订阅文档结构是这样的(补全后):
{ "_id": "xxx", "name": "订单通知订阅", "criteria": [ { "key": "order.companyId", "value": "ABC" }, { "key": "order.status", "value": "NEW" } ] }
还有的文档可能没有criteria字段,或者criteria是[]空数组。
二、MongoDB原生查询实现
1. 查询无criteria或criteria为空的文档
这个很直接,用$or组合两个条件:
db.subscriptions.find({ $or: [ { criteria: { $exists: false } }, // 字段不存在 { criteria: { $size: 0 } } // 数组为空 ] })
2. 查询criteria完全匹配指定条件的文档
如果你要找criteria数组恰好包含所有指定键值对的文档(比如必须同时有order.companyId=ABC和order.status=NEW这两个条件,且没有其他条件),可以用$all+$size组合:
// 假设指定的目标条件是这两个 const targetCriteria = [ { key: "order.companyId", value: "ABC" }, { key: "order.status", value: "NEW" } ]; db.subscriptions.find({ criteria: { $all: targetCriteria, // 数组包含所有目标条目 $size: targetCriteria.length // 数组大小和目标条目数一致(确保没有额外条件) } })
3. 合并两种情况(无criteria 或 匹配指定条件)
把上面两个条件用$or组合起来就是最终的查询:
const targetCriteria = [ { key: "order.companyId", value: "ABC" }, { key: "order.status", value: "NEW" } ]; db.subscriptions.find({ $or: [ { criteria: { $exists: false } }, { criteria: { $size: 0 } }, { criteria: { $all: targetCriteria, $size: targetCriteria.length } } ] })
三、Spring Boot Mongo API 实现
先定义你的实体类,对应MongoDB的文档结构:
import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.util.List; @Document(collection = "subscriptions") public class Subscription { @Id private String id; // 其他业务字段,比如订阅名称、接收人等 private String subscriptionName; private List<CriteriaEntry> criteria; // 内部类对应嵌入式的criteria条目 public static class CriteriaEntry { private String key; private String value; // 构造器、getter、setter public CriteriaEntry() {} public CriteriaEntry(String key, String value) { this.key = key; this.value = value; } // 省略getter和setter } // 省略其他字段的getter和setter }
接下来用MongoTemplate实现查询(Spring Boot默认已经配置好MongoTemplate,直接注入即可):
1. 实现合并场景的查询方法
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Component; import java.util.List; @Component public class SubscriptionRepository { private final MongoTemplate mongoTemplate; // 构造注入 public SubscriptionRepository(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } /** * 查询符合条件的订阅:要么无criteria,要么criteria完全匹配指定条件 * @param targetCriteria 指定的匹配条件列表 * @return 符合条件的订阅列表 */ public List<Subscription> findEligibleSubscriptions(List<Subscription.CriteriaEntry> targetCriteria) { // 条件1:无criteria或criteria为空 Criteria noCriteriaCondition = new Criteria().orOperator( Criteria.where("criteria").exists(false), Criteria.where("criteria").size(0) ); // 条件2:criteria完全匹配指定条件 Criteria matchCriteriaCondition = Criteria.where("criteria") .all(targetCriteria) .size(targetCriteria.size()); // 合并两个条件 Criteria finalCondition = new Criteria().orOperator(noCriteriaCondition, matchCriteriaCondition); // 构建查询并执行 Query query = new Query(finalCondition); return mongoTemplate.find(query, Subscription.class); } }
2. 使用示例
比如你要找匹配order.companyId=ABC和order.status=NEW的订阅,或者无criteria的订阅:
// 构建目标条件 List<Subscription.CriteriaEntry> target = List.of( new Subscription.CriteriaEntry("order.companyId", "ABC"), new Subscription.CriteriaEntry("order.status", "NEW") ); // 查询 List<Subscription> eligibleSubs = subscriptionRepository.findEligibleSubscriptions(target);
四、进阶场景:动态匹配事件属性
如果你的需求是订阅的criteria中的每个key对应事件的字段路径,需要验证事件中该路径的值等于订阅的value(比如事件有order.companyId=ABC,则订阅中所有key=order.companyId的条目value必须是ABC),这种情况需要用MongoDB的表达式查询,Spring Boot中可以用$expr来实现:
import org.bson.Document; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import java.util.Map; public List<Subscription> findSubscriptionsMatchingEvent(Map<String, Object> eventProps) { // 构建$expr表达式:检查每个criteria的value等于事件中对应key的值 Document exprDoc = Document.parse(""" { $allElementsTrue: { $map: { input: "$criteria", as: "crit", in: { $eq: [ "$$crit.value", "$$EVENT.$$crit.key" ] } } } } """); // 绑定事件变量 Query query = new Query(); query.addCriteria( new Criteria().orOperator( Criteria.where("criteria").exists(false), Criteria.where("criteria").size(0), Criteria.where("").is(exprDoc) ) ); // 注入事件变量到查询中 query.withVariables(Map.of("EVENT", eventProps)); return mongoTemplate.find(query, Subscription.class); }
使用示例:
// 模拟事件属性 Map<String, Object> event = Map.of( "order.companyId", "ABC", "order.status", "NEW" ); List<Subscription> matchingSubs = subscriptionRepository.findSubscriptionsMatchingEvent(event);
这个方法会自动验证订阅的每个criteria条目是否和事件属性匹配,同时包含无criteria的订阅。
内容的提问来源于stack exchange,提问作者Brad Mace

