Spring Data MongoDB:用Criteria实现含$expr的Mongo查询等价逻辑
Since Spring Data MongoDB 2.0.5 doesn't support the $expr operator directly in the Criteria API, we have two solid workarounds to replicate your existing MongoDB Shell logic. Let's break them down:
Option 1: Use BasicQuery for Native MongoDB Syntax
This approach lets you reuse your already-tested Shell query directly in Spring Data, avoiding the limitations of the Criteria API for this specific case.
Here's how to implement it:
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; import java.util.List; // Inject your MongoTemplate instance private final MongoTemplate mongoTemplate; // Define your parameters double overlapThreshold = 0.5; int startInterval = 10; int endInterval = 90; public List<Variant> findDocumentsWithOverlap() { // Build the native query string (matches your Shell logic) String nativeQuery = String.format( "{ $expr: { $gt: [ { $divide: [ { $subtract: [ { $min: [ \"$end\", %d ] }, { $max: [ \"$start\", %d ] } ] }, %d ] }, %f ] } }", endInterval, startInterval, (endInterval - startInterval), overlapThreshold ); // Create a BasicQuery with the native syntax Query query = new BasicQuery(nativeQuery); // Execute the query and map results to your entity class return mongoTemplate.find(query, Variant.class, "variants"); }
Pros: Directly reuses your validated Shell logic, minimal code changes.
Cons: Requires string formatting, which can be error-prone if you have many dynamic parameters (but manageable for this use case).
Option 2: Use Spring Data Aggregation Framework
This is a more type-safe, Spring Data-native approach using the Aggregation API, which supports all the arithmetic and comparison operators you need.
Here's the implementation:
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators; import org.springframework.data.mongodb.core.aggregation.ComparisonOperators; import org.springframework.data.mongodb.core.aggregation.Expressions; import java.util.List; // Inject your MongoTemplate instance private final MongoTemplate mongoTemplate; // Define your parameters double overlapThreshold = 0.5; int startInterval = 10; int endInterval = 90; public List<Variant> findDocumentsWithOverlapViaAggregation() { int intervalRange = endInterval - startInterval; // Build the match operation to filter by overlap rate var matchOperation = Aggregation.match( ComparisonOperators.Gt.valueOf( ArithmeticOperators.Divide.valueOf( ArithmeticOperators.Subtract.valueOf( Expressions.min(Expressions.field("end"), endInterval) ).subtract( Expressions.max(Expressions.field("start"), startInterval) ) ).divideBy(intervalRange) ).greaterThanValue(overlapThreshold) ); // Create the aggregation pipeline and execute var aggregation = Aggregation.newAggregation(matchOperation); return mongoTemplate.aggregate(aggregation, "variants", Variant.class).getMappedResults(); }
Pros: Type-safe, no string manipulation, aligns with Spring Data best practices.
Cons: Slightly more verbose than the native query approach, but easier to maintain for dynamic parameters.
Both approaches will correctly filter documents where the overlap rate with your target interval exceeds 50%, just like your original Shell query.
内容的提问来源于stack exchange,提问作者jizuel

