Spring MongoDB 数据聚合中的错误引用

Posted

技术标签:

【中文标题】Spring MongoDB 数据聚合中的错误引用【英文标题】:Wrong reference in Spring MongoDB data aggregation 【发布时间】:2016-06-29 21:15:45 【问题描述】:

我有以下类型的 mongodb 文档:


"_id" : 
    "refId" : ObjectId("55e44dd70a975a6fec7ae66e"),
    "someString" : "foo"
,
"someValue" : 1,
"date" : NumberLong("1441025536869"),
"subdoc" : 
    "someRef" : ObjectId("xf2h55e44dd70a975a6fec7a"),
    "count" : 99


想要获取具有唯一"subdoc.someRef" 和最早"date" 的文档列表(如果有任何具有相同"subdoc.someRef" 的文档)并按任何字段排序。问题在于按字段"subdoc.count" 排序。 我使用以下弹簧数据聚合:

Aggregation agg = Aggregation.newAggregation(
            Aggregation.match(match),
            Aggregation.project("date", "someValue")
                    .and("subdoc.someRef").as("someRef")
                    .and("subdoc.count").as("count")
                    .and("_id.someString").as("someString")
                    .and("_id.refId").as("refId"),
            Aggregation.sort(Sort.Direction.ASC, "date"),
            Aggregation.group("someRef")
                    .min("date").as("date")
                    .first("refId").as("refId")
                    .first("someValue").as("someValue")
                    .first("count").as("count"),
            Aggregation.project("someValue", "date", "refId", "count")
                    .and("_id").as("someRef"),
            Aggregation.sort(pageable.getSort()),
            Aggregation.skip(pageable.getOffset()),
            Aggregation.limit(pageable.getPageSize())
    );

一切都很好,除了弹簧数据如何转换:.first("count").as("count") 我总是得到 "count" : null 聚合结果。 spring 创建的 DBObject 不是我所期望的。日志中的那一行与我有关:

"$group" :  "_id" : "$someRef" , "date" :  "$min" : "$date" , "refId" :  "$first" : "$_id.refId" , "someValue" :  "$first" : "$someValue" , "count" :  "$first" : "$subdoc.count"

我无法理解为什么它总是将 "$subdoc.count" 放入而不是放入 "$count" 作为聚合管道的上一步的结果。因此,结果中的“计数”始终为空,因为 "$subdoc.count" 在最后一个 grop 步骤中始终为空。 如何让 Spring 使用我想要的值而不是引用?

【问题讨论】:

在这里杀死你的是$project 阶段,你实际上都不需要。每个聚合管道阶段实际上必须传递该阶段可用的所有数据,这意味着它会占用时间和资源。因此,您的管道应该是[match,sort,group,sort,skip,limit],而不需要重命名密钥。只需在group 中需要的地方引用"subdoc.field",当然使用.as() 作为“非点”名称(因为这在$group 中是不允许的)。更好更快。此外,在排序后以及其他字段均为$first 时,$min 的意义不大。 感谢@BlakesSeven,我错过了可以在first() 运算符中使用虚线名称 重点是您确实不应该。没有必要这样做,因此“正确”的做法是完全删除 $project 阶段。这里的问题是spring-mongo“记住”了原始文档结构,因此会引用原来的文档结构。但是没有额外的阶段更有效,因此它不应该在这里应用。 【参考方案1】:

感谢评论,解决方案是消除预测并更新小组赛阶段。分组与子文档引用配合得很好,这是我没想到的:

    Aggregation agg = Aggregation.newAggregation(
            Aggregation.match(match),
            Aggregation.sort(Sort.Direction.ASC, "date"),
            Aggregation.group("subdoc.someRef")
                    .first("subdoc.count").as("count")
                    .first("_id.refId").as("refId")
                    .first("_id.someString").as("someString")
                    .first("date").as("date")
                    .first("someValue").as("someValue"),
            Aggregation.sort(pageable.getSort()),
            Aggregation.skip(pageable.getOffset()),
            Aggregation.limit(pageable.getPageSize())
    );

【讨论】:

以上是关于Spring MongoDB 数据聚合中的错误引用的主要内容,如果未能解决你的问题,请参考以下文章

java spring 中使用 AggregationOperation 的 Mongodb 内部连接:错误 [需要'cursor' 选项,但带有解释参数的聚合除外]

从String到ObjectId的Spring数据MongoDb聚合查找

使用 Spring 数据和 mongoDB 进行日期排序的聚合

使用 MongoDB 进行 Spring Boot 数据聚合

Spring Data mongodb聚合框架:project()中的ConditionalOperators进行中位数计算

如何使用 java spring 注释在 MongoDB 中创建一个完全填充的引用对象