【问题标题】:Aggregation by $group got a strange behavirous with $first$group 的聚合对 $first 产生了奇怪的行为
【发布时间】:2020-10-15 01:15:49
【问题描述】:

我的聚合有一个奇怪的行为。我有很多这样的文件:

{
    "_id" : "xxxxxxx",
    "someId" : "xxxxxxx",
    "creationDatetime" : ISODate("2019-07-11T15:10:35.747Z"),
    "versions" : [ 
        {
            "versionDatetime" : ISODate("2019-07-11T15:10:35.785Z"),
            "startDatetime" : ISODate("2019-03-29T09:07:04.000Z"),
            "endDatetime" : ISODate("2019-08-05T11:07:04.000Z"),
            "name" : "ThisIsAName"
        },
        {
            "versionDatetime" : ISODate("2019-07-11T15:20:35.785Z"),
            "startDatetime" : ISODate("2019-03-29T09:07:04.000Z"),
            "endDatetime" : ISODate("2019-08-05T11:07:04.000Z"),
            "name" : "ThisIsAName"
        }
    ],
    "_class" : "com.xx.xx.xx.MyDocument"
}

当有新版本时,我尝试通过“someId”和“versions.name”聚合每个文档。

private List<> function(List<String> someIds) {
Instant now = Instant.now();

        MatchOperation matchOperation = Aggregation.match(new Criteria(MyDocument.version.dateTime)
                .gte(now.minus(1, ChronoUnit.MINUTES).truncatedTo(ChronoUnit.MINUTES))
                .lt(now.truncatedTo(ChronoUnit.MINUTES))
                .and(MyDocument.id).in(someIds));

        SortOperation sortByUpdateDatetime = sort(Sort.Direction.DESC, MyDocument.version.dateTime);

        GroupOperation groupByService = group(MyDocument.someId, MyDocument.version.name)
                .count().as(count)
                .first(MyDocument.someId).as(someId)
                .first(MyDocument.version.name).as(name)
                .first(MyDocument.version.dateTime).as(lastUpdate);

        Aggregation aggregation = newAggregation(matchOperation, sortByUpdateDatetime, groupByService);
        AggregationResults<ClientAggregationResult> result = mongoTemplate.aggregate(
                aggregation, MyDocument.class, ClientAggregationResult.class);

        return result.getMappedResults().stream().map(this::toDataDTO).collect(Collectors.toList());
}

在 mongoShell 中是这样的:

db.getCollection('parkingRight').aggregate([ { 
    "$match" : { 
        "versions.datetime" : { 
            "$gte" : { 
                "$date" : "2020-06-23T15:00:00.000Z"} , 
                "$lt" : { "$date" : "2020-06-24T17:20:00.000Z"}} , 
                "someId" : { "$in" : [ "xxx", "yyy", "kkk"]}}} , 
                { "$sort" : { "versions.datetime" : -1}} , 
                { "$group" : { 
                    "_id" : { 
                        "someId" : "$someId" , 
                        "name" : "$versions.name"} , 
                    "count" : {"$sum" : 1} , 
                    "someId" : { "$first" : "$someId"} , 
                    "name" : { "$first" : "$versions.name"} , 
                    "lastDate" : { "$first" : "$versions.datetime"}
                }
            }
        ])

但是当有多个版本时,聚合的 Doc 会像这样连接字段名称:name: ThisIsAName,ThisIsAName。每个版本的文档都有一个 ThisIsAName。

其他字段正常。

你知道这是我的言辞错误还是奇怪的行为?

【问题讨论】:

  • 你可以添加一个文档和mongo查询而不是java代码。
  • 为了更清晰,我添加了 mongoShell 查询

标签: java mongodb spring-data-mongodb aggregation


【解决方案1】:

这是预期的行为。

在聚合中,匹配阶段选择整个文档,而不是其中的一部分,因此在此阶段之后,管道中的文档看起来与原始文档完全相同。

排序阶段也对整个文档进行操作,并且不会对嵌入数组中的元素进行重新排序。在此阶段之后,文档的顺序可能与之前不同,但每个文档的格式仍与开始时完全相同。

当小组赛开始时,管道中的每个文档都已完全未经修改地通过。由于versions 是一个文档数组,投影versions.name 将为您提供一个数组,其中包含每个文档中name 字段的值。

如果您想处理单个版本文档,您可以在初始匹配阶段之后展开版本数组以考虑所有版本,或者使用带有数组运算符之一的 project/addFields 来消除您不感兴趣的条目。

【讨论】:

  • 您当然是对的,但为什么 version.datetime 没有相同的行为?字段 lastDate 仅填充第一个匹配项。
  • 因为您使用了$first 和日期时间。但没有名字。这就是这种行为的原因。
  • @afourcad 当我测试这个时,我得到了 name 和 dateTime 的数组,即使只有值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-28
  • 1970-01-01
  • 2015-05-08
  • 1970-01-01
  • 2020-01-22
  • 2012-12-11
  • 2011-07-29
相关资源
最近更新 更多