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

MongoDB Query转Criteria查询抛出重复$or表达式异常如何解决

问题背景

需要实现的MongoDB原生查询逻辑如下:

{$and:
[
      {$or:
      [
           {"itemNbr": { $in: :#{#itemNbrs} }},
           {"deptNbr": { $in: :#{#deptNbrs} }}
      ]},
      {"allocationPlanType": :#{#allocationPlanType}},
      {$or:
      [
           {"overallNeedQuantity": { $gte: :#{#showAllocationPlan} }},
           {"overallPlannerQuantity": { $gte: :#{#showAllocationPlan} }}
      ]},
      {$or:
      [
           {"itemDesc1": { $eq: :#{#itemName} }},
           {"itemDesc2": { $gte: :#{#itemName} }}
      ]}
]}

初始编写的Spring Data MongoDB Criteria实现代码:

List<Criteria> andExpression = new ArrayList<>();
if (!CollectionUtils.isEmpty(itemNbrs)) {
            itemNbrQuery = Criteria.where("itemNbr").in(itemNbrs);
            andExpression.add(itemNbrQuery);
        }

 andExpression.add(Criteria.where("allocationPlanType").is(allocationPlanType));
    if(Objects.nonNull(itemName))
    {
        itemNameQuery=criteria.orOperator(
                Criteria.where("itemDesc1").is(itemName),
                Criteria.where("itemDesc2").is(itemName)
        );

        andExpression.add(itemNameQuery);
    }
  if (Objects.isNull(choice)) {
            Criteria noFltersQuery = criteria.orOperator(
                    Criteria.where("overallNeedQuantity").gte(showAllocationPlan),
                    Criteria.where("overallPlannerQuantity").gte(showAllocationPlan)
            );
            andExpression.add(noFltersQuery);
            
    }else {
        Criteria noFltersQuery = 
        Criteria.where("overallPlannerQuantity").gte(showAllocationPlan);
        andExpression.add(noFltersQuery);
    }
    query.with(pageableRequest);
    query.addCriteria(criteria.andOperator(andExpression.toArray(new Criteria[andExpression.size()])));

运行时抛出异常:

Due to limitations of the org.bson.Document, you can't add a second '$or' expression specified as '$or : [Document{{overallNeedQuantity=Document{{$gte=1}}}}, Document{{overallPlannerQuantity=Document{{$gte=1}}}}]'. Criteria already contains '$or : [Document{{itemDesc1=COMP CURR PREK WORKBOOK}}, Document{{itemDesc2=COMP CURR PREK WORKBOOK}}]'.

报错原因
  • 核心问题是复用了同一个criteria实例多次调用orOperator()方法。Spring Data MongoDB的Criteria对象在调用操作符方法时,会直接把操作符挂载到当前Criteria对应的BSON文档节点下,而同一个BSON文档不允许存在多个同名的$or键,因此添加第二个$or条件时直接抛出异常。
  • 初始代码还存在两个逻辑偏差:一是原生查询第一个$or块包含deptNbr的in判断,初始实现未覆盖该逻辑;二是itemDesc2的匹配规则原生是$gte,初始代码错写为is精确匹配。
修复方案

每个独立的$or条件块都必须使用全新的空Criteria实例调用orOperator()生成,不要复用全局criteria实例生成多个同级操作符,所有条件组装完成后统一传入andOperator即可。
修复后的完整代码:

List<Criteria> andExpression = new ArrayList<>();

// 组装第一个$or块:itemNbr或deptNbr匹配
List<Criteria> firstOrConditions = new ArrayList<>();
if (!CollectionUtils.isEmpty(itemNbrs)) {
    firstOrConditions.add(Criteria.where("itemNbr").in(itemNbrs));
}
if (!CollectionUtils.isEmpty(deptNbrs)) {
    firstOrConditions.add(Criteria.where("deptNbr").in(deptNbrs));
}
// 两个条件至少存在一个时,才加入and条件组
if (!firstOrConditions.isEmpty()) {
    andExpression.add(new Criteria().orOperator(firstOrConditions.toArray(new Criteria[0])));
}

// 精确匹配allocationPlanType
andExpression.add(Criteria.where("allocationPlanType").is(allocationPlanType));

// 组装itemName匹配的$or块
if(Objects.nonNull(itemName)) {
    Criteria itemNameQuery = new Criteria().orOperator(
            Criteria.where("itemDesc1").is(itemName),
            Criteria.where("itemDesc2").gte(itemName) // 修正为gte,与原生查询逻辑一致
    );
    andExpression.add(itemNameQuery);
}

// 组装数量过滤的$or块
if (Objects.isNull(choice)) {
    Criteria quantityFilterQuery = new Criteria().orOperator(
            Criteria.where("overallNeedQuantity").gte(showAllocationPlan),
            Criteria.where("overallPlannerQuantity").gte(showAllocationPlan)
    );
    andExpression.add(quantityFilterQuery);
} else {
    andExpression.add(Criteria.where("overallPlannerQuantity").gte(showAllocationPlan));
}

query.with(pageableRequest);
// 最终用新的criteria实例组装所有and条件
query.addCriteria(new Criteria().andOperator(andExpression.toArray(new Criteria[0])));
关键注意点
  • 只要查询中存在多个平级的$or/$and/$nor等操作符,每个操作符块都必须用独立的new Criteria()实例生成,禁止在同一个Criteria实例上重复调用同类型操作符方法。
  • 组装条件时如果某个分支的条件可能为空(比如itemNbrs和deptNbrs都为空的场景),需要做空判断避免生成空的操作符块导致查询逻辑异常。

内容的提问来源于stack exchange,提问作者SUVAM ROY

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:15:44