您在使用 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 查询结构。
为什么这个方案有效
-
正确的范围查询
在同一字段的
Criteria
上链式调用.lte()
和.gte()
,确保条件被正确分组到一个对象中,符合 MongoDB 的语法要求。 -
逻辑运算符的正确使用
使用
.orOperator
组合broadcastTime
和planBroadcastTime
的条件,保持了预期的 OR 逻辑,避免生成无效结构。 -
避免冲突
修复后的查询结构消除了格式错误,Spring Data MongoDB 能够正确处理所有条件,不会触发 "second 'null' criteria" 错误。
注意事项
-
测试验证
应用修复后,建议使用不同的输入组合(例如,提供
startTime
和endTime
、仅提供startTime
、不同fallback
值)测试查询,确保结果符合预期。 -
空值处理
确保
StringUtil.isNotEmptyString
对null
和空字符串的处理符合预期,以避免意外添加条件。
总结
InvalidMongoDbApiUsageException
错误源于在同一字段的范围查询中误用 .andOperator
,导致无效的 MongoDB 查询结构。通过在每个字段的 Criteria
上直接链式调用 .lte()
和 .gte()
,并使用 .orOperator
组合不同字段的条件,可以构建正确的查询。使用上述修复后的代码即可解决问题。