Spring Data-MongoDB-汇总如何投影已分组的参考文档的所有字段

问题描述 投票:0回答:1

我有2个文档,主题和评论。每个主题都有很多评论,文档如下所示:

@Document
public class Topic {
    @Id
    private String id;

    @Indexed(unique = true)
    @NotBlank
    private String title;
}


@Document
public class Comment {

    @NotBlank
    private String text;

    @Indexed
    @NotBlank
    private String topic;  // id of topic

    @CreatedDate
    @Indexed
    private LocalDateTime createdDate;
}

所以我实际上将Topic的ID参考保存在注释中。

这是我的汇总方案:列出今天收到最多评论的主题。所以三件事:

  • 获取今天的所有评论(MatchOperation)
  • 按主题和汇总注释分组(GroupOperation)
  • 按此总和排序(SortOperation)

这是到目前为止的代码:

MatchOperation filterCreatedDate = match(Criteria.where("createdDate").gte(today.atStartOfDay()).lt(today.plusDays(1).atStartOfDay()));
GroupOperation groupByTopic = group("topic").count().as("todaysCommentsCount");
SortOperation sortByTodaysCommentsCount = sort(Sort.Direction.DESC, "todaysCommentsCount");

Aggregation aggregation = newAggregation(filterCreatedDate, groupByTopic, sortByTodaysCommentsCount);
AggregationResults<TopTopic> result = mongoTemplate.aggregate(aggregation, Comment.class, TopTopic.class);

这是此输出的特殊类:

public class TopTopic {
    private int todaysCommentsCount;
}

这是我的聚合结果,其中只有一个主题:

{
    "mappedResults": [
        {
            "todaysCommentsCount": 3
        }
    ],
    "rawResults": {
        "results": [
            {
                "_id": "5dbdca8112a617031728c417",     // topic id
                "todaysCommentsCount": 3
            }
        ],
        "ok": 1.0
    },
    "serverUsed": null,
    "uniqueMappedResult": {
        "todaysCommentsCount": 1
    }
}

我以为我实际上很接近,但是以某种方式只有当我只有一个主题时,它才能起作用。当有多个主题的评论应创建多个组时,出现此错误:

Could not write JSON: Expected unique result or null, but got more than one!; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Expected unique result or null, but got more than one! (through reference chain: org.springframework.data.mongodb.core.aggregation.AggregationResults[\"uniqueMappedResult\"]

..虽然我不调用任何getUniqueMappedResult方法。

我该怎么办?

第二,如何摆脱我的输出类TopTopic,而返回使用todaysCommentsCount扩展的原始Topic值,而无需创建特殊的输出类?

感谢您的帮助。

mongodb spring-data spring-data-mongodb
1个回答
0
投票

第一部分

您正在将AggregationResults发送回给调用者,其中杰克逊正在串行化为json并在调用getUniqueMappedResult时失败。

将主题字段_id添加到TopTopic,并在AggregationResults中读取映射的结果。

List<TopTopic> topTopics = result.getMappedResults()

您的输出看起来像

 [
    {
       "_id": "5dbdca8112a617031728c417",
       "todaysCommentsCount": 3
    }
 ]

第二部分

您可以将$$ROOT变量与$first一起使用,以在$group阶段映射整个文档,然后通过$replaceRoot来提升合并文档($mergeObjects合并doctodaysCommentCount)。

todaysCommentsCount添加到Topic类。

类似

MatchOperation filterCreatedDate = match(Criteria.where("createdDate").gte(today.atStartOfDay()).lt(today.plusDays(1).atStartOfDay()));
GroupOperation groupByTopic = group("topic").first("$$ROOT").as("doc").count().as("todaysCommentsCount");
SortOperation sortByTodaysCommentsCount = sort(Sort.Direction.DESC, "todaysCommentsCount");
ReplaceRootOperation replaceRoot = Aggregation.replaceRoot().withValueOf(ObjectOperators.valueOf("doc").mergeWith(new Document("todaysCommentsCount", "$todaysCommentsCount")));

Aggregation aggregation = newAggregation(filterCreatedDate, groupByTopic, sortByTodaysCommentsCount, replaceRoot);
AggregationResults<TopTopic> result = mongoTemplate.aggregate(aggregation, Comment.class, Topic.class);

List<Topic> topTopics = result.getMappedResults();

您的输出看起来像

 [
    {
       "_id": "5dbdca8112a617031728c417",
       "title" : "topic",
       "todaysCommentsCount": 3
    }
 ]

更新(添加$ lookup阶段以拉入主题字段)

MatchOperation filterCreatedDate = match(Criteria.where("createdDate").gte(today.atStartOfDay()).lt(today.plusDays(1).atStartOfDay()));
GroupOperation groupByTopic = group("topic").count().as("todaysCommentsCount");
SortOperation sortByTodaysCommentsCount = sort(Sort.Direction.DESC, "todaysCommentsCount");
LookupOperation lookupOperation = LookupOperation.newLookup().
                                    from("topic").
                                    localField("_id").
                                    foreignField("_id").
                                    as("topic");
ReplaceRootOperation replaceRoot = Aggregation.replaceRoot().withValueOf(ObjectOperators.valueOf("todaysCommentsCount").mergeWithValuesOf(ArrayOperators.arrayOf("topic").elementAt(0)));

Aggregation aggregation = newAggregation(filterCreatedDate, groupByTopic, sortByTodaysCommentsCount, lookupOperation, replaceRoot);
AggregationResults<TopTopic> result = mongoTemplate.aggregate(aggregation, Comment.class, Topic.class);

List<Topic> topTopics = result.getMappedResults();
© www.soinside.com 2019 - 2024. All rights reserved.