解决 MongoDB 查询中的 `InvalidMongoDbApiUsageException` 错误

您在使用 Spring Data MongoDB 时遇到了 InvalidMongoDbApiUsageException 异常,错误信息如下:

"由于 com.mongodb.BasicDocument 的限制,您无法添加第二个 'null' 条件。查询已经包含 '{ "or" : { "broadcastTime" : { "lte" : { "date" : "2025-04-21T16:00:00Z"}}, "and" : \[{ "broadcastTime" : { "gte" : { "date" : "2025-04-20T16:00:00Z"}}}}, { "planBroadcastTime" : { "lte" : { "date" : "2025-04-21T16:00:00Z"}}, "and" : { "planBroadcastTime" : { "gte" : { "$date" : "2025-04-20T16:00:00Z"}}}}]}'"

这个错误是由使用 Criteria API 构建 MongoDB 查询时的错误用法引起的。以下是问题的分析和解决方案。


问题分析

假设您的代码类似于以下形式,用于根据时间范围和回退条件查询数据:

java 复制代码
Query query = new Query();
if (liveRoomReq.getStartTime() != null && liveRoomReq.getEndTime() != null) {
    Criteria timeCriteria = new Criteria().orOperator(
            Criteria.where("broadcastTime").lte(liveRoomReq.getEndTime())
                    .andOperator(Criteria.where("broadcastTime").gte(liveRoomReq.getStartTime())),
            Criteria.where("planBroadcastTime").lte(liveRoomReq.getEndTime())
                    .andOperator(Criteria.where("planBroadcastTime").gte(liveRoomReq.getStartTime()))
    );
    query.addCriteria(timeCriteria);
}

上述代码尝试构建一个查询,要求 broadcastTime 或 planBroadcastTime 在指定的 startTime 和 endTime 范围内。但错误的使用方式导致了无效的 MongoDB 查询结构。

生成的错误查询

根据错误信息,生成的 MongoDB 查询如下:

json 复制代码
{
  "$or": [
    {
      "broadcastTime": { "$lte": { "$date": "2025-04-21T16:00:00Z" } },
      "$and": [ { "broadcastTime": { "$gte": { "$date": "2025-04-20T16:00:00Z" } } } ]
    },
    {
      "planBroadcastTime": { "$lte": { "$date": "2025-04-21T16:00:00Z" } },
      "$and": [ { "planBroadcastTime": { "$gte": { "$date": "2025-04-20T16:00:00Z" } } } ]
    }
  ]
}

这种结构是无效的,因为 MongoDB 不允许在同一字段的条件中混合使用字段运算符(如 $lte)和逻辑运算符(如 $and)。正确的范围查询应该将 $lte 和 $gte 组合在同一个字段的对象中。

错误原因

问题出在 .andOperator 的误用上:

  • 在 Spring Data MongoDB 中,.andOperator 用于将多个不同字段的条件以 AND 逻辑组合。
  • 对于同一字段的范围查询(如 broadcastTime 需要同时满足 <= endTime 和 >= startTime),应该在单个 Criteria 上直接链式调用 .lte() 和 .gte()。

错误的用法生成了不符合 MongoDB 语法的查询结构,导致 Spring Data MongoDB 在处理后续条件时抛出异常。


解决方案

修复方法是调整 Criteria 的构建方式,在同一字段的条件上直接使用链式调用,而不是使用 .andOperator。以下是更正后的代码:

java 复制代码
Query query = new Query();
if (liveRoomReq.getStartTime() != null && liveRoomReq.getEndTime() != null) {
    Criteria broadcastTimeCriteria = Criteria.where("broadcastTime")
            .lte(liveRoomReq.getEndTime())
            .gte(liveRoomReq.getStartTime());
    Criteria planBroadcastTimeCriteria = Criteria.where("planBroadcastTime")
            .lte(liveRoomReq.getEndTime())
            .gte(liveRoomReq.getStartTime());
    Criteria timeCriteria = new Criteria().orOperator(broadcastTimeCriteria, planBroadcastTimeCriteria);
    query.addCriteria(timeCriteria);
} else if (liveRoomReq.getStartTime() != null) {
    Criteria broadcastTimeCriteria = Criteria.where("broadcastTime")
            .gte(liveRoomReq.getStartTime());
    Criteria planBroadcastTimeCriteria = Criteria.where("planBroadcastTime")
            .gte(liveRoomReq.getStartTime());
    Criteria timeCriteria = new Criteria().orOperator(broadcastTimeCriteria, planBroadcastTimeCriteria);
    query.addCriteria(timeCriteria);
}
// 添加回退条件(示例)
if (StringUtil.isNotEmptyString(liveRoomReq.getFallback())) {
    if ("0".equals(liveRoomReq.getFallback())) {
        Criteria fallbackCriteria = new Criteria().orOperator(
                Criteria.where("fallback").in(liveRoomReq.getFallback()),
                Criteria.where("fallback").exists(false)
        );
        query.addCriteria(fallbackCriteria);
    } else {
        query.addCriteria(Criteria.where("fallback").is(liveRoomReq.getFallback()));
    }
}
修复后的查询

对于 startTime 和 endTime 都提供的情况,生成的 MongoDB 查询如下:

json 复制代码
{
  "$or": [
    { "broadcastTime": { "$lte": "2025-04-21T16:00:00Z", "$gte": "2025-04-20T16:00:00Z" } },
    { "planBroadcastTime": { "$lte": "2025-04-21T16:00:00Z", "$gte": "2025-04-20T16:00:00Z" } }
  ]
}

如果还添加了 fallback 条件(例如 fallback = "0"),最终查询可能是:

json 复制代码
{
  "$and": [
    {
      "$or": [
        { "broadcastTime": { "$lte": "2025-04-21T16:00:00Z", "$gte": "2025-04-20T16:00:00Z" } },
        { "planBroadcastTime": { "$lte": "2025-04-21T16:00:00Z", "$gte": "2025-04-20T16:00:00Z" } }
      ]
    },
    {
      "$or": [
        { "fallback": { "$in": ["0"] } },
        { "fallback": { "$exists": false } }
      ]
    }
  ]
}

这是一个有效的 MongoDB 查询结构。


为什么这个方案有效

  1. 正确的范围查询

    在同一字段的 Criteria 上链式调用 .lte() 和 .gte(),确保条件被正确分组到一个对象中,符合 MongoDB 的语法要求。

  2. 逻辑运算符的正确使用

    使用 .orOperator 组合 broadcastTime 和 planBroadcastTime 的条件,保持了预期的 OR 逻辑,避免生成无效结构。

  3. 避免冲突

    修复后的查询结构消除了格式错误,Spring Data MongoDB 能够正确处理所有条件,不会触发 "second 'null' criteria" 错误。


注意事项

  • 测试验证

    应用修复后,建议使用不同的输入组合(例如,提供 startTime 和 endTime、仅提供 startTime、不同 fallback 值)测试查询,确保结果符合预期。

  • 空值处理

    确保 StringUtil.isNotEmptyString 对 null 和空字符串的处理符合预期,以避免意外添加条件。


总结

InvalidMongoDbApiUsageException 错误源于在同一字段的范围查询中误用 .andOperator,导致无效的 MongoDB 查询结构。通过在每个字段的 Criteria 上直接链式调用 .lte() 和 .gte(),并使用 .orOperator 组合不同字段的条件,可以构建正确的查询。使用上述修复后的代码即可解决问题。

相关推荐
SL-staff9 小时前
技术实践:将Excel自动升级为可搜索的知识节点(JVS平台实现)
mongodb·elasticsearch·知识图谱·jvs平台·语义解析·企业文档管理·excel结构化
ClouGence1 天前
数据库迁移工具 CloudCanal v6.5.0.0 发布:新增 TDSQL PostgreSQL 多条链路,支持 MongoDB 双向同步
数据库·mysql·mongodb
浪子明X1 天前
MongoDB 迁移方案评审:用双写窗口和回放样本控制切换风险
数据库·mongodb
蜗牛互联网6 天前
MongoDB Atlas Agent Engine之后,如何用版本门禁防止陈旧写入
java·数据库·人工智能·后端·mongodb
天空'之城10 天前
单片机基础核心知识点汇总(四十六)
数据库·单片机·mongodb
Dragon_qu·x11 天前
MongoDB 高可用集群部署
运维·数据库·mongodb·k8s·helm
MayBaymax19 天前
MongoDB 索引与事务
java·数据库·mongodb
&不羁之风&19 天前
从 MySQL 到 MongoDB:80 万系统日志(sys_log)平滑迁移完整实战(含 Windows 环境 mongoimport 安装与避坑)
mysql·mongodb
那人如此可好21 天前
MongoDB常用命令大全
mongodb·db
MayBaymax21 天前
MongoDB 基础概念
java·数据库·mongodb