programing

봄에 데이터 mongodb 집계를 위한 페이지화를 달성하는 방법

muds 2023. 7. 30. 18:09
반응형

봄에 데이터 mongodb 집계를 위한 페이지화를 달성하는 방법

mongotemplate 또는 mongore repository를 이용한 봄철 데이터 mongodb, 집합체 페이지화 달성 방법

오래된 게시물에 대한 답변입니다만, 혹시 다른 사람이 이런 글을 검색하다가 올 경우를 대비해 답변을 드리겠습니다.

Firrat KüüK의 이전 솔루션을 기반으로 하여 pageImple 생성자의 "총계" 필드 값으로 results.size()를 지정하면 페이징 작업이 제대로 수행되지 않을 것입니다.매번 전체 크기를 페이지 크기로 설정하므로 쿼리가 반환하는 실제 총 결과 수를 확인해야 합니다.

public Page<UserListItemView> list(final Pageable pageable) {
    long total = getCount(<your property name>, <your property value>);

    final Aggregation agg = newAggregation(
        skip(pageable.getPageNumber() * pageable.getPageSize()),
        limit(pageable.getPageSize())
    );

    final List<UserListItemView> results = mongoTemplate
        .aggregate(agg, User.class, UserListItemView.class)
        .getMappedResults();

    return new PageImpl<>(results, pageable, total);
}

자, 그렇다면, 총 결과의 수를 얻는 가장 좋은 방법은 또 다른 질문입니다. 그것은 제가 현재 알아내려고 노력하고 있는 질문입니다.제가 시도한 방법은 거의 동일한 집계를 두 번 실행하는 것이었습니다(한 번은 총 카운트를 얻고 다시 한 번은 페이징의 실제 결과를 얻는 것). 단, Match Operation을 사용한 다음 Group Operation을 사용하여 카운트를 가져오는 것이었습니다.

private long getCount(String propertyName, String propertyValue) {
    MatchOperation matchOperation = match(Criteria.where(propertyName).is(propertyValue));
    GroupOperation groupOperation = group(propertyName).count().as("count");
    Aggregation aggregation = newAggregation(matchOperation, groupOperation);
    return mongoTemplate.aggregate(aggregation, Foo.class, NumberOfResults.class).getMappedResults().get(0).getCount();
}

private class NumberOfResults {
    private int count;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

거의 동일한 쿼리를 두 번 실행하는 것은 다소 비효율적이지만, 결과를 페이지로 이동하려면 페이징 가능한 개체가 페이징처럼 동작하려면 총 결과 수를 알아야 합니다.제 방법을 개선하여 총 결과를 얻을 수 있다면 정말 멋질 것입니다!

편집: 이것은 또한 카운트를 제공할 것이며, 결과를 유지하기 위해 래퍼 객체가 필요하지 않기 때문에 더 단순합니다. 따라서 전체 이전 코드 블록을 다음 코드 블록으로 바꿀 수 있습니다.

private long getCount(String propertyName, String propertyValue) {
    Query countQuery = new Query(Criteria.where(propertyName).is(propertyValue));
    return mongoTemplate.count(countQuery, Foo.class);
}

souris 솔루션 외에도 결과에 페이지 가능 클래스를 사용할 수 있습니다.

public Page<UserListItemView> list(final Pageable pageable) {

    final Aggregation agg = newAggregation(
        skip(pageable.getPageNumber() * pageable.getPageSize()),
        limit(pageable.getPageSize())
    );

    final List<UserListItemView> results = mongoTemplate
        .aggregate(agg, User.class, UserListItemView.class)
        .getMappedResults();

    return new PageImpl<>(results, pageable, results.size())
}

MongoTemplate를 사용할 수 있습니다.

org.spring.framework.data.mongodb.core.aggregation.Aggregation#skip
        and 
org.springframework.data.mongodb.core.aggregation.Aggregation#limit

Aggregation agg = newAggregation(
        project("tags"),
        skip(10),
        limit(10)
);

AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class);
List<TagCount> tagCount = results.getMappedResults();

https://stackoverflow.com/a/39784851/4546949 이라는 답변에 따라 자바용 코드를 작성했습니다.

집계 그룹을 사용하여 다른 페이징 정보와 함께 데이터의 수와 배열을 가져옵니다.

    AggregationOperation group = Aggregation.group().count().as("total")
            .addToSet(pageable.getPageNumber()).as("pageNumber")
            .addToSet(pageable.getPageSize()).as("pageSize")
            .addToSet(pageable.getOffset()).as("offset")
            .push("$$ROOT").as("data");

집계 프로젝트를 사용하여 페이징 정보에 따라 슬라이스합니다.

    AggregationOperation project = Aggregation.project()
            .andInclude("pageSize", "pageNumber", "total", "offset")
            .and(ArrayOperators.Slice.sliceArrayOf("data").offset((int) pageable.getOffset()).itemCount(pageable.getPageSize()))
            .as("data");

mongo 템플릿을 사용하여 집계합니다.

    Aggregation aggr = newAggregation(group, project);
    CustomPage page = mongoTemplate.aggregate(aggregation, Foo.class, CustomPage.class).getUniqueMappedResult();

사용자 지정 페이지를 만듭니다.

    public class CustomPage {
        private long pageSize;
        private long pageNumber;
        private long offset;
        private long total;
        private List<Foo> data;
    }

일반 솔루션은 다음과 같습니다.

public Page<ResultObject> list(Pageable pageable) {
    // build your main stages
    List<AggregationOperation> mainStages = Arrays.asList(match(....), group(....));
    return pageAggregation(pageable, mainStages, "target-collection", ResultObject.class);
}

public <T> Page<T> pageAggregation(
        final Pageable pageable,
        final List<AggregationOperation> mainStages,
        final String collection,
        final Class<T> clazz) {
    final List<AggregationOperation> stagesWithCount = new ArrayList<>(mainStages);
    stagesWithCount.add(count().as("count"));
    final Aggregation countAgg = newAggregation(stagesWithCount);
    final Long count = Optional
            .ofNullable(mongoTemplate.aggregate(countAgg, collection, Document.class).getUniqueMappedResult())
            .map(doc -> ((Integer) doc.get("count")).longValue())
            .orElse(0L);

    final List<AggregationOperation> stagesWithPaging = new ArrayList<>(mainStages);
    stagesWithPaging.add(sort(pageable.getSort()));
    stagesWithPaging.add(skip(pageable.getOffset()));
    stagesWithPaging.add(limit(pageable.getPageSize()));
    final Aggregation resultAgg = newAggregation(stagesWithPaging);
    final List<T> result = mongoTemplate.aggregate(resultAgg, collection, clazz).getMappedResults();

    return new PageImpl<>(result, pageable, count);
}

페이징 가능한 개체의 올바른 값으로 페이징된 개체를 반환하는 것이 가장 좋고 간단한 방법이라고 생각합니다.

Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(Criteria.where("type").is("project")),
                        Aggregation.group("id").last("id").as("id"), Aggregation.project("id"),
                        Aggregation.skip(pageable.getPageNumber() * pageable.getPageSize()),
                        Aggregation.limit(pageable.getPageSize()));


    PageableExecutionUtils.getPage(mongoTemplate.aggregate(aggregation, Draft.class, Draft.class).getMappedResults(), pageable,() -> mongoTemplate.count(Query.of(query).limit(-1).skip(-1), Draft.class));

또 다른 접근 방식은 확장입니다.PagingAndSortingRepository<T, ID>인터페이스그런 다음, 다음을 생성할 수 있습니다.@Aggregation다음과 같은 쿼리 방법:

@Aggregation(pipeline = {
      "{ $match: { someField: ?0 } }",
      "{ $project: { _id: 0, someField: 1} }"
})
List<StuffAggregateModel> aggregateStuff(final String somePropertyName, final Pageable pageable);

비즈니스 로직 서비스 클래스에서 이를 호출하고 Pageable(원하는 경우 정렬 옵션도 포함)을 구성한 후 repo 메서드를 호출합니다.저는 이 접근 방식을 좋아합니다. 왜냐하면 당신이 작성해야 하는 코드의 양을 단순화하고 단순화하기 때문입니다.쿼리(집계 파이프라인)가 충분히 간단하다면 이것이 가장 좋은 해결책일 것입니다.이 접근 방식을 위한 유지보수 코딩은 거의 힘들이지 않습니다.

MongoDB $facet에 대한 나의 답변.

// User(_id, first name, etc), Car (user_id, brand, etc..)
LookupOperation lookupStageCar = Aggregation.lookup(‘cars ’, ‘user_id’, ‘_id’, ‘car’);
 MatchOperation matchStage = Aggregation.match(Criteria.where(‘car.user_id ‘).exists(true));

 CountOperation countOperation = Aggregation.count().as("total");
 AddFieldsOperation addFieldsOperation = Aggregation.addFields().addFieldWithValue("page", pageable.getPageNumber()).build();
 SkipOperation skipOperation = Aggregation.skip(Long.valueOf(pageable.getPageNumber() * pageable.getPageSize()));
 LimitOperation limitOperation = Aggregation.limit(pageable.getPageSize());

// here the magic
 FacetOperation facetOperation = Aggregation.facet( countOperation, addFieldsOperation).as("metadata")
         .and(skipOperation, limitOperation).as("data");

// users with car
 List<AggrigationResults> map = mongoTemplate.aggregate(Aggregation.newAggregation( lookupStageCar, matchStage, facetOperation), "User",  AggrigationResults.class).getMappedResults();

———————————————————————————
public class AggrigationResults  {

    private List<Metadata> metadata;
    private List<User> data;

}

public class Metadata {

    private long total;
    private long page;

}

———————————————————————————
output: 
{
    "metadata" : [ 
        {
            "total" : 300,
            "page" : 3
        }
    ],
    "data" : [ 
        {
            ... original document ...
        }, 
        {
            ... another document ...
        }, 
        {
            ... etc up to 10 docs ...
        }
    ]
}

참조: 페이지화를 위해 MongoDB 집계를 사용하는 방법은 무엇입니까?

언급URL : https://stackoverflow.com/questions/34427241/in-spring-data-mongodb-how-to-achieve-pagination-for-aggregation

반응형