Skip to content

Commit b36633c

Browse files
committed
Switch mongoItemReader to use a cursor instead of paging
Paging in mongo runs in On^2 time since each skip needs to count all the records it is skipping
1 parent e6ea4bc commit b36633c

File tree

5 files changed

+166
-308
lines changed

5 files changed

+166
-308
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
buildscript {
66
repositories {
77
mavenCentral()
8-
maven { url 'https://repo.spring.io/plugins-release' }
8+
maven { url 'https://repo.spring.io/plugins-release-local' }
99
maven { url 'https://plugins.gradle.org/m2/' }
1010
}
1111
dependencies {
@@ -41,7 +41,7 @@ allprojects {
4141
mavenCentral()
4242
maven { url 'https://repo.spring.io/libs-snapshot' }
4343
maven { url 'https://repo.spring.io/libs-milestone' }
44-
maven { url 'https://repo.spring.io/plugins-release' }
44+
maven { url 'https://repo.spring.io/plugins-release-local' }
4545
}
4646

4747
ext {

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoItemReader.java

Lines changed: 60 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,29 @@
1717
package org.springframework.batch.item.data;
1818

1919
import java.util.ArrayList;
20-
import java.util.Iterator;
2120
import java.util.List;
2221
import java.util.Map;
2322

24-
import org.bson.Document;
25-
import org.bson.codecs.DecoderContext;
26-
import org.slf4j.Logger;
27-
import org.slf4j.LoggerFactory;
28-
2923
import org.springframework.batch.item.ExecutionContext;
3024
import org.springframework.batch.item.ItemReader;
25+
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
3126
import org.springframework.beans.factory.InitializingBean;
32-
import org.springframework.data.domain.PageRequest;
33-
import org.springframework.data.domain.Pageable;
3427
import org.springframework.data.domain.Sort;
3528
import org.springframework.data.mongodb.core.MongoOperations;
3629
import org.springframework.data.mongodb.core.query.BasicQuery;
3730
import org.springframework.data.mongodb.core.query.Query;
3831
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
3932
import org.springframework.data.mongodb.util.json.ParameterBindingJsonReader;
33+
import org.springframework.data.util.CloseableIterator;
4034
import org.springframework.util.Assert;
4135
import org.springframework.util.ClassUtils;
4236
import org.springframework.util.StringUtils;
4337

38+
import org.bson.Document;
39+
import org.bson.codecs.DecoderContext;
40+
import org.slf4j.Logger;
41+
import org.slf4j.LoggerFactory;
42+
4443
/**
4544
* <p>
4645
* Restartable {@link ItemReader} that reads documents from MongoDB
@@ -51,17 +50,11 @@
5150
* If you set JSON String query {@link #setQuery(String)} then
5251
* it executes the JSON to retrieve the requested documents.
5352
* </p>
54-
*
53+
*
5554
* <p>
5655
* If you set Query object {@link #setQuery(Query)} then
5756
* it executes the Query to retrieve the requested documents.
5857
* </p>
59-
*
60-
* <p>
61-
* The query is executed using paged requests specified in the
62-
* {@link #setPageSize(int)}. Additional pages are requested as needed to
63-
* provide data when the {@link #read()} method is called.
64-
* </p>
6558
*
6659
* <p>
6760
* The JSON String query provided supports parameter substitution via ?&lt;index&gt;
@@ -81,10 +74,10 @@
8174
* @author Mahmoud Ben Hassine
8275
* @author Parikshit Dutta
8376
*/
84-
public class MongoItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {
85-
77+
public class MongoItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements InitializingBean {
78+
8679
private static final Logger log = LoggerFactory.getLogger(MongoItemReader.class);
87-
80+
8881
private MongoOperations template;
8982
private Query query;
9083
private String queryString;
@@ -94,12 +87,13 @@ public class MongoItemReader<T> extends AbstractPaginatedDataItemReader<T> imple
9487
private String fields;
9588
private String collection;
9689
private List<Object> parameterValues = new ArrayList<>();
90+
private CloseableIterator<? extends T> cursor = null;
9791

9892
public MongoItemReader() {
9993
super();
10094
setName(ClassUtils.getShortName(MongoItemReader.class));
10195
}
102-
96+
10397
/**
10498
* A Mongo Query to be used.
10599
*
@@ -187,47 +181,6 @@ public void setHint(String hint) {
187181
this.hint = hint;
188182
}
189183

190-
@Override
191-
@SuppressWarnings("unchecked")
192-
protected Iterator<T> doPageRead() {
193-
if (queryString != null) {
194-
Pageable pageRequest = PageRequest.of(page, pageSize, sort);
195-
196-
String populatedQuery = replacePlaceholders(queryString, parameterValues);
197-
198-
Query mongoQuery;
199-
200-
if(StringUtils.hasText(fields)) {
201-
mongoQuery = new BasicQuery(populatedQuery, fields);
202-
}
203-
else {
204-
mongoQuery = new BasicQuery(populatedQuery);
205-
}
206-
207-
mongoQuery.with(pageRequest);
208-
209-
if(StringUtils.hasText(hint)) {
210-
mongoQuery.withHint(hint);
211-
}
212-
213-
if(StringUtils.hasText(collection)) {
214-
return (Iterator<T>) template.find(mongoQuery, type, collection).iterator();
215-
} else {
216-
return (Iterator<T>) template.find(mongoQuery, type).iterator();
217-
}
218-
219-
} else {
220-
Pageable pageRequest = PageRequest.of(page, pageSize);
221-
query.with(pageRequest);
222-
223-
if(StringUtils.hasText(collection)) {
224-
return (Iterator<T>) template.find(query, type, collection).iterator();
225-
} else {
226-
return (Iterator<T>) template.find(query, type).iterator();
227-
}
228-
}
229-
}
230-
231184
/**
232185
* Checks mandatory properties
233186
*
@@ -238,7 +191,7 @@ public void afterPropertiesSet() throws Exception {
238191
Assert.state(template != null, "An implementation of MongoOperations is required.");
239192
Assert.state(type != null, "A type to convert the input into is required.");
240193
Assert.state(queryString != null || query != null, "A query is required.");
241-
194+
242195
if (queryString != null) {
243196
Assert.state(sort != null, "A sort is required.");
244197
}
@@ -260,4 +213,50 @@ private Sort convertToSort(Map<String, Sort.Direction> sorts) {
260213

261214
return Sort.by(sortValues);
262215
}
216+
217+
@Override
218+
protected T doRead() throws Exception {
219+
return cursor.hasNext() ? cursor.next() : null;
220+
}
221+
222+
@Override
223+
protected void doOpen() throws Exception {
224+
if (queryString != null) {
225+
String populatedQuery = replacePlaceholders(queryString, parameterValues);
226+
227+
Query mongoQuery;
228+
229+
if (StringUtils.hasText(fields)) {
230+
mongoQuery = new BasicQuery(populatedQuery, fields);
231+
}
232+
else {
233+
mongoQuery = new BasicQuery(populatedQuery);
234+
}
235+
236+
if (StringUtils.hasText(hint)) {
237+
mongoQuery.withHint(hint);
238+
}
239+
mongoQuery.with(sort);
240+
if (StringUtils.hasText(collection)) {
241+
cursor = template.stream(mongoQuery, type, collection);
242+
}
243+
else {
244+
cursor = template.stream(mongoQuery, type);
245+
}
246+
247+
}
248+
else {
249+
if (StringUtils.hasText(collection)) {
250+
cursor = template.stream(query, type, collection);
251+
}
252+
else {
253+
cursor = template.stream(query, type);
254+
}
255+
}
256+
}
257+
258+
@Override
259+
protected void doClose() throws Exception {
260+
cursor.close();
261+
}
263262
}

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/builder/MongoItemReaderBuilder.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ public class MongoItemReaderBuilder<T> {
5555

5656
private List<Object> parameterValues = new ArrayList<>();
5757

58-
protected int pageSize = 10;
59-
6058
private boolean saveState = true;
6159

6260
private String name;
@@ -245,20 +243,7 @@ public MongoItemReaderBuilder<T> hint(String hint) {
245243
}
246244

247245
/**
248-
* The number of items to be read with each page.
249-
*
250-
* @param pageSize the number of items
251-
* @return this instance for method chaining
252-
* @see MongoItemReader#setPageSize(int)
253-
*/
254-
public MongoItemReaderBuilder<T> pageSize(int pageSize) {
255-
this.pageSize = pageSize;
256-
257-
return this;
258-
}
259-
260-
/**
261-
* Provide a Spring Data Mongo {@link Query}. This will take precedence over a JSON
246+
* Provide a Spring Data Mongo {@link Query}. This will take precedence over a JSON
262247
* configured query.
263248
*
264249
* @param query Query to execute
@@ -299,7 +284,6 @@ public MongoItemReader<T> build() {
299284
reader.setParameterValues(this.parameterValues);
300285
reader.setQuery(this.query);
301286

302-
reader.setPageSize(this.pageSize);
303287
reader.setName(this.name);
304288
reader.setSaveState(this.saveState);
305289
reader.setCurrentItemCount(this.currentItemCount);

0 commit comments

Comments
 (0)