Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/340.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 通过spring数据迭代MongoDB中的大型集合_Java_Spring_Mongodb - Fatal编程技术网

Java 通过spring数据迭代MongoDB中的大型集合

Java 通过spring数据迭代MongoDB中的大型集合,java,spring,mongodb,Java,Spring,Mongodb,朋友们 我通过spring数据在java项目中使用MongoDB。我使用存储库接口访问集合中的数据。对于某些处理,我需要迭代集合的所有元素。我可以使用存储库的fetchAll方法,但它总是返回ArrayList 然而,据推测,其中一个集合将是大型的—多达100万条记录,每个记录至少有几千字节。我想我不应该在这种情况下使用fetchAll,但我既找不到返回某些迭代器的方便方法(这可能允许部分提取集合),也找不到带有回调的方便方法 我只看到了对在页面中检索此类集合的支持。我想知道这是否是处理此类收藏

朋友们

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

然而,据推测,其中一个集合将是大型的—多达100万条记录,每个记录至少有几千字节。我想我不应该在这种情况下使用fetchAll,但我既找不到返回某些迭代器的方便方法(这可能允许部分提取集合),也找不到带有回调的方便方法


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

反应迟钝,但可能会对将来的人有所帮助。Spring数据不提供任何API来包装Mongo DB游标功能。它在
find
方法中使用它,但总是返回完整的对象列表。选项包括直接使用Mongo API或使用Spring数据分页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());
final int pageLimit=300;
int pageNumber=0;
Page=repository.findAll(新页面请求(pageNumber,pageLimit));
while(page.hasNextPage()){
processPageContent(page.getContent());
page=repository.findAll(新页面请求(++pageNumber,pageLimit));
}
//处理最后一页
processPageContent(page.getContent());
UPD(!)这种方法对于大型数据集来说是不够的(请参见@Shawn Bush评论),请直接使用Mongo API处理此类情况。

另一种方法:

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

}while (!page.isLastPage());

检查新方法以根据文档处理结果


使用MongoTemplate::stream()可能是DBCursor最合适的Java包装器

您仍然可以使用MongoTemplate访问集合,只需使用DBCursor:

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

您可能希望尝试以下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.
    }
作为光标的流:

@Query("{}")
Stream<Alarm>  findAllByCustomQueryAndStream();
@Query(“{}”)
Stream findAllByCustomQueryAndStream();

因此,对于大量数据,您可以对其进行流式处理,并逐行处理,而不受内存限制

对大型集合进行迭代的最佳方法是直接使用Mongo API。我使用了下面的代码,它对我的用例来说就像一个符咒。
我必须迭代超过1500万条记录,其中一些记录的文档大小非常大。
以下代码位于Kotlin Spring Boot应用程序(Spring Boot版本:2.4.5)

fun getAbcCursor(batchSize:Int、from:Long?、to:Long?):MongoCursor{
val collection=xyzmongtemplate.getCollection(“abc”)
val查询=文档(“字段1”、“值1”)
if(from!=null){
val fromDate=日期(从)
val toDate=if(to!=null){Date(to)}else{Date()}
query.append(
“createTime”,
文件(
“\$gte”,起始日期
).附加(
“\$lte”,toDate
)
)
}
返回collection.find(query).batchSize(batchSize).iterator()
}
然后,从服务层方法,您可以继续对返回的游标调用MongoCursor.next(),直到MongoCursor.hasNext()返回true。

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

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

MongoCursor getAbcCursor(Int batchSize){
MongoCollection collection=xyzMongoTemplate.getCollection(“您的集合名称”);
文档查询=新文档(“field1”、“value1”);//查询-->{“field1”:“value1”}
返回collection.find(query).batchSize(batchSize.iterator();
}

您可以使用
limit()
函数来限制函数在chunk中检索数据。我不熟悉您正在使用的框架,但是如果它没有MongoDB游标的包装器,我会觉得很奇怪。你确定你得到的是一个
ArrayList
,而不是一个围绕光标的自定义
List
实现吗?是的,当然-我刚刚记录了它的findAll().getClass(),收集了大约300万的数据,我想我看到了java.util.ArrayList…从spring boot 2开始,你就可以使用streamAllBy()一次只加载一个条目。我只是想稍后再插话说,对于大型数据集,您也应该远离分页API,因为它必须在构建每个页面之前遍历整个集合。这很快就会变得昂贵。坚持直接使用Mongo API。@ShawnBush你确定吗?do{}while();我想重复肖恩·布什所说的话,如果不是纯粹因为失去了很多时间而感到沮丧,希望能节省别人的时间,我会看起来更好。不要将此用于更大的收藏!您将得到使用limit&skip的查询。每个被跳过的文档都将被检查,这使得具有大页码的请求速度非常慢。如果不知道如何创建查询对象来查找集合中的所有内容,这在上下文中没有多大帮助。我比预期的更难弄明白这一点。这个答案在我的案例中帮助了我很多。谢谢从spring boot 2开始,您就不能只使用streamAllBy()而不使用任何附加注释吗?
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<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();
    }