在 Java 11 中,我有一个函数可以使用 3 个不同的查询对同一集合进行多个 MongoDb .countDocuments(query)
调用。有没有一种方法可以将这三个调用聚合到一个请求中,这样 MongoDB 服务就不会被单独调用 3 次。我目前正在使用 Bson 和 com.mongodb.client.model.Filters.* 库来单独构建查询。然而,我还没有找到关于如何使用 BSON 执行此操作的良好资源,并且我愿意接受其他方法来完成此操作。
我的功能包括这样的东西..
import org.springframework.data.mongodb.core.MongoOperations;
private final MongoOperations mongoOperations;
public RequestCount Foo (){
Bson query1 = eq("field1", "foo" )
Bson query2 = eq("field2", "bar" )
Bson query3 = eq("field3", "raw" )
count1 = mongoOperations.getCollection("collection").countDocuments(query1);
count2 = mongoOperations.getCollection("collection").countDocuments(query2);
count3 = mongoOperations.getCollection("collection").countDocuments(query3);
return RequestCount(count1, count2, count3);
}
然后我将它们存储在一个标准 PoJo 对象中,其中包含 3 个私有(private)字段,例如...
public class RequestCount {
private Integer count1;
private Integer count2;
private Integer count3;
}
最佳答案
您可以组合 $facet和 $project一个聚合管道中的操作如下所示:
db.getCollection("fooBarDocument")
.aggregate([
{
$facet: {
"field1Foo": [{$match: {"field1": {$eq: "foo"}}}, {$count: "count"}],
"field2Bar": [{$match: {"field2": {$eq: "bar"}}}, {$count: "count"}],
"field3Raw": [{$match: {"field3": {$eq: "raw"}}}, {$count: "count"}],
}
},
{
$project: {
"count1": {$arrayElemAt: ["$field1Foo.count", 0]},
"count2": {$arrayElemAt: ["$field2Bar.count", 0]},
"count3": {$arrayElemAt: ["$field3Raw.count", 0]}
}
}
]
)
你可以在 Spring Data 中这样写:
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.ArrayOperators;
import org.springframework.data.mongodb.core.aggregation.FacetOperation;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.query.Query;
@Repository
// ...
public class MongoFooBarRepository implements FooBarRepository {
private static final String FIELD_1 = "field1";
private static final String FIELD_2 = "field2";
private static final String FIELD_3 = "field3";
private static final String FIELD_1_FOO = "field1Foo";
private static final String FIELD_2_BAR = "field2Bar";
private static final String FIELD_3_RAW = "field3Raw";
/// ...
private FooBarCounts countFooBarRawOccurrences() {
FacetOperation facet = facet(match(where(FIELD_1).is("foo")), count().as(COUNT)).as(FIELD_1_FOO)
.and(match(where(FIELD_2).is("bar")), count().as(COUNT)).as(FIELD_2_BAR)
.and(match(where(FIELD_3).is("raw")), count().as(COUNT)).as(FIELD_3_RAW);
ProjectionOperation projectionOperation = project()
.and(ArrayOperators.ArrayElemAt.arrayOf(FIELD_1_FOO + "." + COUNT).elementAt(0)).as(COUNT_1)
.and(ArrayOperators.ArrayElemAt.arrayOf(FIELD_2_BAR + "." + COUNT).elementAt(0)).as(COUNT_2)
.and(ArrayOperators.ArrayElemAt.arrayOf(FIELD_3_RAW + "." + COUNT).elementAt(0)).as(COUNT_3);
TypedAggregation<FooBarDocument> aggregation = Aggregation
.newAggregation(FooBarDocument.class, facet, projectionOperation);
return mongoTemplate.aggregate(aggregation, FooBarCounts.class).getMappedResults().get(0);
}
此外,如果给定字段的预期值没有出现一次,则要使结果返回 0
而不是 null
,您可以定义构造函数像这样:
public FooBarCounts(Integer count1, Integer count2, Integer count3) {
this.count1 = count1 == null ? 0 : count1;
this.count2 = count2 == null ? 0 : count2;
this.count3 = count3 == null ? 0 : count3;
}
我发现对回答此问题有用的其他答案:
- Multiple Counts with single query in mongodb
- Convert $facet mongo query to Spring Data (有一些编译问题,但指出了使用 FacetOperation 的正确方向)
奖金:
工作示例https://bitbucket.org/kasptom/stackoverflow-73339478-mongo-foobar-count
关于java - 如何将多个 countDocuments 调用优化为一个聚合调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73339478/