You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB多嵌入式文档查询构建:Spring Boot Mongo API通知系统需求

解决MongoDB嵌入式数组匹配或不存在的查询问题(Spring Boot实现)

首先我得明确你的需求:你要找订阅文档,要么完全没有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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:57:13