Skip to content

Commit 6d88d1f

Browse files
ninja-incmminella
authored andcommitted
BATCH-2568: MongoItemReader should support Query object by default
JIRA: https://jira.spring.io/browse/BATCH-2568
1 parent 62a580f commit 6d88d1f

File tree

2 files changed

+207
-37
lines changed

2 files changed

+207
-37
lines changed

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

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@
2525

2626
import com.mongodb.util.JSON;
2727

28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
2830
import org.springframework.batch.item.ExecutionContext;
2931
import org.springframework.batch.item.ItemReader;
3032
import org.springframework.beans.factory.InitializingBean;
@@ -45,14 +47,23 @@
4547
* </p>
4648
*
4749
* <p>
48-
* It executes the JSON {@link #setQuery(String)} to retrieve the requested
49-
* documents. The query is executed using paged requests specified in the
50+
* If you set JSON String query {@link #setQuery(String)} then
51+
* it executes the JSON to retrieve the requested documents.
52+
* </p>
53+
*
54+
* <p>
55+
* If you set Query object {@link #setQuery(Query)} then
56+
* it executes the Query to retrieve the requested documents.
57+
* </p>
58+
*
59+
* <p>
60+
* The query is executed using paged requests specified in the
5061
* {@link #setPageSize(int)}. Additional pages are requested as needed to
5162
* provide data when the {@link #read()} method is called.
5263
* </p>
5364
*
5465
* <p>
55-
* The JSON query provided supports parameter substitution via ?&lt;index&gt;
66+
* The JSON String query provided supports parameter substitution via ?&lt;index&gt;
5667
* placeholders where the &lt;index&gt; indicates the index of the
5768
* parameterValue to substitute.
5869
* </p>
@@ -65,12 +76,16 @@
6576
*
6677
*
6778
* @author Michael Minella
79+
* @author Takaaki Iida
6880
*/
6981
public class MongoItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {
70-
82+
83+
private static final Logger log = LoggerFactory.getLogger(MongoItemReader.class);
84+
7185
private static final Pattern PLACEHOLDER = Pattern.compile("\\?(\\d+)");
7286
private MongoOperations template;
73-
private String query;
87+
private Query query;
88+
private String queryString;
7489
private Class<? extends T> type;
7590
private Sort sort;
7691
private String hint;
@@ -82,6 +97,15 @@ public MongoItemReader() {
8297
super();
8398
setName(ClassUtils.getShortName(MongoItemReader.class));
8499
}
100+
101+
/**
102+
* A Mongo Query to be used.
103+
*
104+
* @param query Mongo Query to be used.
105+
*/
106+
public void setQuery(Query query) {
107+
this.query = query;
108+
}
85109

86110
/**
87111
* Used to perform operations against the MongoDB instance. Also
@@ -99,10 +123,10 @@ public void setTemplate(MongoOperations template) {
99123
* via ?&lt;index&gt; placeholders where the &lt;index&gt; indicates the index of the
100124
* parameterValue to substitute.
101125
*
102-
* @param query JSON formatted Mongo query
126+
* @param queryString JSON formatted Mongo query
103127
*/
104-
public void setQuery(String query) {
105-
this.query = query;
128+
public void setQuery(String queryString) {
129+
this.queryString = queryString;
106130
}
107131

108132
/**
@@ -163,30 +187,41 @@ public void setHint(String hint) {
163187
@Override
164188
@SuppressWarnings("unchecked")
165189
protected Iterator<T> doPageRead() {
166-
167-
Pageable pageRequest = PageRequest.of(page, pageSize, sort);
168-
169-
String populatedQuery = replacePlaceholders(query, parameterValues);
170-
171-
Query mongoQuery;
172-
173-
if(StringUtils.hasText(fields)) {
174-
mongoQuery = new BasicQuery(populatedQuery, fields);
175-
}
176-
else {
177-
mongoQuery = new BasicQuery(populatedQuery);
178-
}
179-
180-
mongoQuery.with(pageRequest);
181-
182-
if(StringUtils.hasText(hint)) {
183-
mongoQuery.withHint(hint);
184-
}
185-
186-
if(StringUtils.hasText(collection)) {
187-
return (Iterator<T>) template.find(mongoQuery, type, collection).iterator();
190+
if (queryString != null) {
191+
Pageable pageRequest = new PageRequest(page, pageSize, sort);
192+
193+
String populatedQuery = replacePlaceholders(queryString, parameterValues);
194+
195+
Query mongoQuery = null;
196+
197+
if(StringUtils.hasText(fields)) {
198+
mongoQuery = new BasicQuery(populatedQuery, fields);
199+
}
200+
else {
201+
mongoQuery = new BasicQuery(populatedQuery);
202+
}
203+
204+
mongoQuery.with(pageRequest);
205+
206+
if(StringUtils.hasText(hint)) {
207+
mongoQuery.withHint(hint);
208+
}
209+
210+
if(StringUtils.hasText(collection)) {
211+
return (Iterator<T>) template.find(mongoQuery, type, collection).iterator();
212+
} else {
213+
return (Iterator<T>) template.find(mongoQuery, type).iterator();
214+
}
215+
188216
} else {
189-
return (Iterator<T>) template.find(mongoQuery, type).iterator();
217+
Pageable pageRequest = new PageRequest(page, pageSize);
218+
query.with(pageRequest);
219+
220+
if(StringUtils.hasText(collection)) {
221+
return (Iterator<T>) template.find(query, type, collection).iterator();
222+
} else {
223+
return (Iterator<T>) template.find(query, type).iterator();
224+
}
190225
}
191226
}
192227

@@ -199,8 +234,18 @@ protected Iterator<T> doPageRead() {
199234
public void afterPropertiesSet() throws Exception {
200235
Assert.state(template != null, "An implementation of MongoOperations is required.");
201236
Assert.state(type != null, "A type to convert the input into is required.");
202-
Assert.state(query != null, "A query is required.");
203-
Assert.state(sort != null, "A sort is required.");
237+
Assert.state(queryString != null || query != null, "A query is required.");
238+
239+
if (queryString != null) {
240+
Assert.state(sort != null, "A sort is required.");
241+
}
242+
if (query != null) {
243+
Assert.state(query.getSortObject() != null, "A Sort in Query object is required.");
244+
}
245+
246+
if (query != null && query.getLimit() != 0) {
247+
log.warn("PageSize in Query object was ignored. Please set it by MongoItemReader.setPageSize().");
248+
}
204249
}
205250

206251
// Copied from StringBasedMongoQuery...is there a place where this type of logic is already exposed?

spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/MongoItemReaderTests.java

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,17 @@
2525
import org.mockito.Mock;
2626
import org.mockito.MockitoAnnotations;
2727

28+
import org.springframework.data.domain.PageRequest;
2829
import org.springframework.data.domain.Sort;
30+
import org.springframework.data.domain.Sort.Order;
2931
import org.springframework.data.mongodb.core.MongoOperations;
3032
import org.springframework.data.mongodb.core.query.Query;
3133

3234
import static org.junit.Assert.assertEquals;
3335
import static org.junit.Assert.assertFalse;
3436
import static org.junit.Assert.assertTrue;
3537
import static org.junit.Assert.fail;
36-
import static org.mockito.ArgumentMatchers.eq;
38+
import static org.mockito.Matchers.eq;
3739
import static org.mockito.Mockito.when;
3840

3941
public class MongoItemReaderTests {
@@ -60,8 +62,8 @@ public void setUp() throws Exception {
6062
}
6163

6264
@Test
63-
public void testAfterPropertiesSet() throws Exception{
64-
reader = new MongoItemReader<>();
65+
public void testAfterPropertiesSetForQueryString() throws Exception{
66+
reader = new MongoItemReader<String>();
6567

6668
try {
6769
reader.afterPropertiesSet();
@@ -109,6 +111,31 @@ public void testAfterPropertiesSet() throws Exception{
109111

110112
reader.afterPropertiesSet();
111113
}
114+
115+
@Test
116+
public void testAfterPropertiesSetForQueryObject() throws Exception{
117+
reader = new MongoItemReader<String>();
118+
119+
reader.setTemplate(template);
120+
reader.setTargetType(String.class);
121+
122+
Query query1 = new Query();
123+
reader.setQuery(query1);
124+
125+
try {
126+
reader.afterPropertiesSet();
127+
fail("Sort was not set but exception was not thrown.");
128+
} catch (IllegalStateException iae) {
129+
assertEquals("A Sort in Query object is required.", iae.getMessage());
130+
} catch (Throwable t) {
131+
fail("Wrong exception was thrown.");
132+
}
133+
134+
Query query2 = new Query().with(new Sort(new Order(Sort.Direction.ASC, "_id")));
135+
reader.setQuery(query2);
136+
137+
reader.afterPropertiesSet();
138+
}
112139

113140
@Test
114141
public void testBasicQueryFirstPage() {
@@ -223,4 +250,102 @@ public void testQueryWithCollection() {
223250
assertEquals("{ \"name\" : -1 }", query.getSortObject().toJson());
224251
assertEquals("collection", collectionContainer.getValue());
225252
}
253+
254+
@Test
255+
public void testQueryObject() throws Exception {
256+
reader = new MongoItemReader<String>();
257+
reader.setTemplate(template);
258+
259+
Query query = new Query()
260+
.with(new Sort(new Order(Sort.Direction.ASC, "_id")));
261+
reader.setQuery(query);
262+
reader.setTargetType(String.class);
263+
264+
reader.afterPropertiesSet();
265+
266+
ArgumentCaptor<Query> queryContainer = ArgumentCaptor.forClass(Query.class);
267+
when(template.find(queryContainer.capture(), eq(String.class))).thenReturn(new ArrayList<String>());
268+
269+
assertFalse(reader.doPageRead().hasNext());
270+
271+
Query actualQuery = queryContainer.getValue();
272+
assertFalse(reader.doPageRead().hasNext());
273+
assertEquals(10, actualQuery.getLimit());
274+
assertEquals(0, actualQuery.getSkip());
275+
}
276+
277+
@Test
278+
public void testQueryObjectWithIgnoredPageSize() throws Exception {
279+
reader = new MongoItemReader<String>();
280+
reader.setTemplate(template);
281+
282+
Query query = new Query()
283+
.with(new Sort(new Order(Sort.Direction.ASC, "_id")))
284+
.with(new PageRequest(0, 50));
285+
reader.setQuery(query);
286+
reader.setTargetType(String.class);
287+
288+
reader.afterPropertiesSet();
289+
290+
ArgumentCaptor<Query> queryContainer = ArgumentCaptor.forClass(Query.class);
291+
when(template.find(queryContainer.capture(), eq(String.class))).thenReturn(new ArrayList<String>());
292+
293+
assertFalse(reader.doPageRead().hasNext());
294+
295+
Query actualQuery = queryContainer.getValue();
296+
assertFalse(reader.doPageRead().hasNext());
297+
assertEquals(10, actualQuery.getLimit());
298+
assertEquals(0, actualQuery.getSkip());
299+
}
300+
301+
@Test
302+
public void testQueryObjectWithPageSize() throws Exception {
303+
reader = new MongoItemReader<String>();
304+
reader.setTemplate(template);
305+
306+
Query query = new Query()
307+
.with(new Sort(new Order(Sort.Direction.ASC, "_id")))
308+
.with(new PageRequest(30, 50));
309+
reader.setQuery(query);
310+
reader.setTargetType(String.class);
311+
reader.setPageSize(100);
312+
313+
reader.afterPropertiesSet();
314+
315+
ArgumentCaptor<Query> queryContainer = ArgumentCaptor.forClass(Query.class);
316+
when(template.find(queryContainer.capture(), eq(String.class))).thenReturn(new ArrayList<String>());
317+
318+
assertFalse(reader.doPageRead().hasNext());
319+
320+
Query actualQuery = queryContainer.getValue();
321+
assertFalse(reader.doPageRead().hasNext());
322+
assertEquals(100, actualQuery.getLimit());
323+
assertEquals(0, actualQuery.getSkip());
324+
}
325+
326+
@Test
327+
public void testQueryObjectWithCollection() throws Exception {
328+
reader = new MongoItemReader<String>();
329+
reader.setTemplate(template);
330+
331+
Query query = new Query()
332+
.with(new Sort(new Order(Sort.Direction.ASC, "_id")));
333+
reader.setQuery(query);
334+
reader.setTargetType(String.class);
335+
reader.setCollection("collection");
336+
337+
reader.afterPropertiesSet();
338+
339+
ArgumentCaptor<Query> queryContainer = ArgumentCaptor.forClass(Query.class);
340+
ArgumentCaptor<String> stringContainer = ArgumentCaptor.forClass(String.class);
341+
when(template.find(queryContainer.capture(), eq(String.class), stringContainer.capture())).thenReturn(new ArrayList<String>());
342+
343+
assertFalse(reader.doPageRead().hasNext());
344+
345+
Query actualQuery = queryContainer.getValue();
346+
assertFalse(reader.doPageRead().hasNext());
347+
assertEquals(10, actualQuery.getLimit());
348+
assertEquals(0, actualQuery.getSkip());
349+
assertEquals("collection", stringContainer.getValue());
350+
}
226351
}

0 commit comments

Comments
 (0)