31

朋友们!

我通过spring-data在java项目中使用MongoDB。我使用 Repository 接口来访问集合中的数据。对于某些处理,我需要遍历集合的所有元素。我可以使用存储库的 fetchAll 方法,但它总是返回 ArrayList。

但是,假设其中一个集合会很大 - 多达 100 万条记录,每个记录至少有几千字节。我想我不应该在这种情况下使用 fetchAll ,但是我找不到返回一些迭代器的方便方法(这可能允许部分获取集合),也找不到带有回调的方便方法。

我只看到支持在页面中检索此类集合。我想知道这是否是处理此类收藏的唯一方法?

4

9 回答 9

28

迟到的回应,但也许会在未来帮助某人。Spring data 不提供任何 API 来包装Mongo DB Cursor功能。它在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 处理这种情况。

于 2012-08-30T16:58:47.000 回答
15

由于这个问题最近遇到了问题,所以这个答案需要更多的爱!

如果您使用 Spring Data Repository 接口,您可以声明一个返回 Stream 的自定义方法,它将由 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.repositories.queries

于 2017-08-03T20:29:12.203 回答
12

您仍然可以使用 mongoTemplate 访问 Collection 并简单地使用 DBCursor:

     DBCollection collection = mongoTemplate.getCollection("boundary");
     DBCursor cursor = collection.find();        
     while(cursor.hasNext()){
         DBObject obj = cursor.next();
         Object object =  obj.get("polygons");
         ..
      ...
     }
于 2016-05-01T13:55:27.747 回答
11

使用 MongoTemplate::stream() 作为 DBCursor 最合适的 Java 包装器

于 2015-09-03T16:13:23.610 回答
4

另一种方式:

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

}while (!page.isLastPage());
于 2014-03-28T11:34:07.407 回答
2

您可能想尝试这样的 DBCursor 方式:

    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.
    }
于 2017-02-15T07:54:20.153 回答
0

这个答案基于: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;

于 2021-12-16T14:25:21.953 回答
0

对大型集合进行迭代的最佳方法是直接使用 Mongo API。我使用了下面的代码,它对我的​​用例来说就像一个魅力。
我必须迭代超过 15M 的记录,其中一些记录的文档大小很大。
以下代码在 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.next() 直到 MongoCursor.hasNext() 返回 true。

一个重要的观察:请不要错过在“FindIterable”(MongoCollection.find() 的返回类型)上添加 batchSize。如果您不提供批量大小,则游标将获取初始 101 条记录并在此之后挂起(它会尝试一次获取所有剩余的记录)。
对于我的场景,我使用了 2000 的批量大小,因为它在测试期间给出了最好的结果。这种优化的批量大小将受到记录的平均大小的影响。

这是 Java 中的等效代码(从查询中删除 createTime,因为它特定于我的数据模型)。

    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();
    }
于 2021-06-07T19:48:44.973 回答