programing

스프링 데이터를 통해 MongoDB에서 대규모 수집을 반복합니다.

starjava 2023. 6. 26. 20:45
반응형

스프링 데이터를 통해 MongoDB에서 대규모 수집을 반복합니다.

친구들!

java project에서 스프링 데이터를 통해 MongoDB를 사용하고 있습니다.저장소 인터페이스를 사용하여 컬렉션의 데이터에 액세스합니다.일부 처리를 위해 수집의 모든 요소를 반복해야 합니다.저장소의 fetchAll 메서드를 사용할 수 있지만 항상 ArrayList를 반환합니다.

그러나 컬렉션 중 하나는 최대 100만 개의 레코드가 최소 몇 킬로바이트로 클 것으로 예상됩니다.이러한 경우 fetchAll을 사용하면 안 된다고 생각하지만 일부 반복기를 반환하는 편리한 방법(수집을 부분적으로 가져올 수 있음)이나 콜백이 있는 편리한 방법을 찾을 수 없었습니다.

페이지에서 해당 컬렉션을 검색하는 지원만 보았습니다.저는 그것이 그러한 컬렉션으로 작업하는 유일한 방법인지 궁금합니다.

답장이 늦었지만, 아마도 미래에 누군가에게 도움이 될 것입니다.Spring 데이터는 Mongo DB Cursor 기능을 랩하기 위한 API를 제공하지 않습니다.내에서 사용합니다.find메서드이지만 항상 완료된 개체 목록을 반환합니다.Mongo API를 직접 사용하거나 Spring Data Paging API를 사용하는 옵션이 있습니다.

        final int pageLimit = 300;
        int pageNumber = 0;
        Page<T> page = repository.findAll(new PageRequest(pageNumber, pageLimit));
        while (page.hasNextPage()) {
            processPageContent(page.getContent());
            page = repository.findAll(new PageRequest(++pageNumber, pageLimit));
        }
        // process last page
        processPageContent(page.getContent());

UPD(!) 이 방법은 대용량 데이터 집합에 충분하지 않습니다(@Shawn Bush 주석 참조). 이러한 경우 Mongo API를 직접 사용하십시오.

이 질문은 최근에 부딪혔기 때문에, 이 대답은 좀 더 사랑이 필요합니다!

Spring Data Repository 인터페이스를 사용하는 경우 스트림을 반환하는 사용자 지정 메서드를 선언할 수 있으며, 이 메서드는 커서를 사용하여 Spring Data에 의해 구현됩니다.

import java.util.Stream;

public interface AlarmRepository extends CrudRepository<Alarm, String> {

    Stream<Alarm> findAllBy();

}

따라서 대용량 데이터의 경우 메모리 제한 없이 데이터를 스트리밍하고 라인별로 처리할 수 있습니다.

자세한 내용은 https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/ #mongodb.deparitory.dll을 참조하십시오.

mongoTemplate를 사용하여 Collection에 액세스하고 DCursor를 사용할 수 있습니다.

     DBCollection collection = mongoTemplate.getCollection("boundary");
     DBCursor cursor = collection.find();        
     while(cursor.hasNext()){
         DBObject obj = cursor.next();
         Object object =  obj.get("polygons");
         ..
      ...
     }

DBCursor에 가장 적합한 Java 래퍼로 MongoTemplate::stream() 사용

다른 방법:

do{
  page = repository.findAll(new PageRequest(pageNumber, pageLimit));
  pageNumber++;

}while (!page.isLastPage());

문서 기준으로 결과를 처리할 새 방법을 선택합니다.

http://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/MongoTemplate.html#executeQuery-org.springframework.data.mongodb.core.query.Query-java.lang.String-org.springframework.data.mongodb.core.DocumentCallbackHandler-

다음과 같은 방법으로 DB 커서를 사용해 볼 수 있습니다.

    DBObject query = new BasicDBObject(); //setup the query criteria
    query.put("method", method);
    query.put("ctime", (new BasicDBObject("$gte", bTime)).append("$lt", eTime));

    logger.debug("query: {}", query);

    DBObject fields = new BasicDBObject(); //only get the needed fields.
    fields.put("_id", 0);
    fields.put("uId", 1);
    fields.put("ctime", 1);

    DBCursor dbCursor = mongoTemplate.getCollection("collectionName").find(query, fields);

    while (dbCursor.hasNext()){
        DBObject object = dbCursor.next();
        logger.debug("object: {}", object);
        //do something.
    }

대규모 컬렉션에서 반복하는 가장 좋은 방법은 Mongo API를 직접 사용하는 것입니다.나는 아래 코드를 사용했고 그것은 나의 사용 사례에 매력적으로 작용했습니다.
저는 1,500만 개 이상의 레코드를 반복해야 했고, 그 중 일부는 문서 크기가 컸습니다.
다음 코드는 Kotlin Spring Boot App(Spring Boot 버전: 2.4.5)에 있습니다.

fun getAbcCursor(batchSize: Int, from: Long?, to: Long?): MongoCursor<Document> {

    val collection = xyzMongoTemplate.getCollection("abc")
    val query = Document("field1", "value1")
    if (from != null) {
        val fromDate = Date(from)
        val toDate = if (to != null) { Date(to) } else { Date() }
        query.append(
            "createTime",
            Document(
                "\$gte", fromDate
            ).append(
                "\$lte", toDate
            )
        )
    }
    return collection.find(query).batchSize(batchSize).iterator()
}

그런 다음 서비스 계층 메서드에서 MongoCursor.hasNext()가 true를 반환할 때까지 반환된 커서에서 MongoCursor.next()를 계속 호출할 수 있습니다.

중요한 관찰:'FindItable'(MongoCollection.find()의 반환 유형)에 batchSize를 추가하는 것을 잊지 마십시오.배치 크기를 제공하지 않으면 커서가 초기 101개의 레코드를 가져오고 그 후에 보류됩니다(나머지 레코드를 한 번에 모두 가져오려고 함).
제 시나리오에서는 테스트 중에 가장 좋은 결과를 얻었기 때문에 배치 크기를 2000으로 사용했습니다.이 최적화된 배치 크기는 레코드의 평균 크기에 영향을 받습니다.

다음은 Java에서 동일한 코드입니다(작성 제거).내 데이터 모델과 관련된 쿼리로부터의 시간).

    MongoCursor<Document> getAbcCursor(Int batchSize) {
        MongoCollection<Document> collection = xyzMongoTemplate.getCollection("your_collection_name");
        Document query = new Document("field1", "value1");// query --> {"field1": "value1"}
        return collection.find(query).batchSize(batchSize).iterator();
    }

이 답변은 다음을 기반으로 합니다. https://stackoverflow.com/a/22711715/5622596

그 대답은 약간의 업데이트가 필요합니다.PageRequest구성 방법이 변경되었습니다.

그 말과 함께 제 수정된 답변은 다음과 같습니다.

int pageNumber = 1;

//Change value to whatever size you want the page to have
int pageLimit = 100;

Page<SomeClass> page;
List<SomeClass> compondList= new LinkedList<>();

do{
    PageRequest pageRequest = PageRequest.of(pageNumber, pageLimit);
    
    page = repository.findAll(pageRequest);
    
    List<SomeClass> listFromPage = page.getContent();

    //Do something with this list example below
    compondList.addAll(listFromPage);

    pageNumber++;

  }while (!page.isLast());

//Do something with the compondList: example below
return compondList;

언급URL : https://stackoverflow.com/questions/11046105/iterate-over-large-collection-in-mongodb-via-spring-data

반응형