使用 Spring Data Mongo 进行 MongoDB 聚合分页时字段返回为 null

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

我正在使用 MongoDB 聚合在 Spring Data Mongo 项目中实现分页。检索数据时,结果中的字段全部返回为空,我很难理解为什么。

环境:

  • Spring Boot 3.1.4
  • Spring Data MongoDB 4.1.4
  • MongoDB

问题描述: 数据已成功存储在 MongoDB 中,并且我已经使用聚合框架设置了分页。然而,检索到的对象中的字段在结果中全部为空。

代码详情:

标签类

@Document(collection = "labels")
public class Label extends BaseDocument {
    private LabelType type;
    private String path;
    private ObjectId datasetId;
    private String annotations;
    // Constructors, getters, setters...
}

存储库代码

@Repository
public class LabelRepositoryCustomImpl implements LabelRepositoryCustom {
    private final MongoTemplate mongoTemplate;
    
    @Override
    public Page<Label> findAllWithPagination(Pageable pageable, ObjectId datasetId) {
        Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.match(Criteria.where("datasetId").is(datasetId)),
            Aggregation.sort(pageable.getSort()),
            Aggregation.facet(
                Aggregation.count().as("totalCount"),
                Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize()),
                Aggregation.limit(pageable.getPageSize())
            ).as("metadata")
        );
        AggregationResults<Label> results = mongoTemplate.aggregate(aggregation, "labels", Label.class);
        List<Label> labels = results.getMappedResults();
        return PageableExecutionUtils.getPage(labels, pageable, labels::size);
    }
}

Mongo聚合查询

{ 
    "aggregate" : "labels", 
    "pipeline" : [
        { "$match" : { "datasetId" : { "$oid" : "672ac21827cf3f529bbb5a57"}}}, 
        { "$sort" : { "createdAt" : -1}}, 
        { "$facet" : { 
            "metadata" : [
                { "$count" : "totalCount"}, 
                { "$skip" : 0}, 
                { "$limit" : 10}
            ]
        }}
    ]
}

问题:

  • 虽然聚合管道找到了匹配的数据,但 Label 对象中的所有字段都返回为 null。
  • Label 类具有私有默认构造函数、@Document 注释以及与 MongoDB 数据类型匹配的字段类型。

任何指导将不胜感激。谢谢!

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

问题似乎是您的类

Label
与输出查询不匹配。您的查询返回这样的结构:

{
  metadata: [
  { totalCount: 100 }
  ]
}

你的班级有这样的结构:

{
  type: ""
  path: ""
  datasetId: ""
  annotations: ""
}

因此您将丢失文档的所有先前字段,因为

$facet

在同一组输入文档的单个阶段内处理多个聚合管道。每个子管道在输出文档中都有自己的字段,其结果存储为文档数组。

来源:https://www.mongodb.com/docs/v6.1/reference/operator/aggregation/facet/

我可以建议使用 MongoRepository 而不使用 MongoTemplate 吗?

这样的例子:

import org.bson.types.ObjectId;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

public interface LabelRepository extends MongoRepository<Label, ObjectId> {
    
    @Query("{ 'datasetId': ?0 }")
    Page<Label> findByDatasetId(ObjectId datasetId, Pageable pageable);
}

这将自动返回

Page

如果您想将聚合与 mongoTemplate 一起使用,您必须运行 2 个查询(一个计数,一个用于页面),如下所示:

@Repository
public class LabelRepositoryCustomImpl implements LabelRepositoryCustom {
    private final MongoTemplate mongoTemplate;
    
    @Autowired
    public LabelRepositoryCustomImpl(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }
    
    @Override
    public Page<Label> findAllWithPagination(Pageable pageable, ObjectId datasetId) {
        // Conteggio totale dei documenti
        long total = countByDatasetId(datasetId);
        
        // Aggregazione per la paginazione
        Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.match(Criteria.where("datasetId").is(datasetId)),
            Aggregation.sort(pageable.getSort()),
            Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize()),
            Aggregation.limit(pageable.getPageSize())
        );
        
        AggregationResults<Label> results = mongoTemplate.aggregate(aggregation, "labels", Label.class);
        List<Label> labels = results.getMappedResults();

        return new PageImpl<>(labels, pageable, total);
    }
    
    private long countByDatasetId(ObjectId datasetId) {
        // Aggregazione per il conteggio totale dei documenti
        Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.match(Criteria.where("datasetId").is(datasetId)),
            Aggregation.count().as("total")
        );
        
        AggregationResults<CountResult> results = mongoTemplate.aggregate(aggregation, "labels", CountResult.class);
        CountResult countResult = results.getUniqueMappedResult();
        return countResult != null ? countResult.getTotal() : 0;
    }
    
    static class CountResult {
        private long total;

        public long getTotal() {
            return total;
        }

        public void setTotal(long total) {
            this.total = total;
        }
    }
}

在我看来,很多代码行都是不必要的。

希望这对你有帮助

© www.soinside.com 2019 - 2024. All rights reserved.