java - 如何将多个 countDocuments 调用优化为一个聚合调用

标签 java spring mongodb spring-boot criteria

在 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;
    }

我发现对回答此问题有用的其他答案:


奖金:

工作示例https://bitbucket.org/kasptom/stackoverflow-73339478-mongo-foobar-count

关于java - 如何将多个 countDocuments 调用优化为一个聚合调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73339478/

相关文章:

java - RequestMapping 占位符在 JUnit 测试中不起作用

node.js - 使用 $PUSH 进行更新在 Mongoose + Express + Node 中不起作用

mongodb - 如何将数据导入 mongodb 容器并创建镜像

java - 在 fragment 中重用 webView 等单例 View

java - 参数声明中的三个点是什么意思?

java - 通过 http 寻找 api/协议(protocol)

Java JNA WinAPI 函数使 JVM 无声地崩溃

java - Spring - 带有 AbstractWizardFormController 的多个命令类

spring - @RestControllerAdvice 中许多项目的 basePacakage

mongodb - 在我的 MongoDB 子文档中查找具有一定数量字段的结果