DATAMONGO-1334 - Map-reduce operations now honor MapReduceOptions.limit.
We now also consider the limit set via MapReduceOptions when executing mapReduce operations via MongoTemplate.mapReduce(…). MapReduceOptions.limit(…) supersedes a potential limit set via the Query itself. This change also allows to define a limit even when no explicit Query is used. Original pull request: #338.
This commit is contained in:
committed by
Oliver Gierke
parent
0b492b6c55
commit
b0336c27a9
@@ -1579,8 +1579,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"Can not use skip or field specification with map reduce operations");
|
||||
}
|
||||
|
||||
if (query.getLimit() > 0) {
|
||||
if (query.getLimit() > 0 && mapReduceOptions.getLimit() == null) {
|
||||
mapReduceCommand.setLimit(query.getLimit());
|
||||
}
|
||||
if (query.getSortObject() != null) {
|
||||
@@ -1588,6 +1587,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
}
|
||||
|
||||
if (mapReduceOptions.getLimit() != null && mapReduceOptions.getLimit().intValue() > 0) {
|
||||
mapReduceCommand.setLimit(mapReduceOptions.getLimit());
|
||||
}
|
||||
|
||||
if (mapReduceOptions.getJavaScriptMode() != null) {
|
||||
mapReduceCommand.setJsMode(true);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ public class MapReduceOptions {
|
||||
|
||||
private Boolean verbose = true;
|
||||
|
||||
private Integer limit;
|
||||
|
||||
private Map<String, Object> extraOptions = new HashMap<String, Object>();
|
||||
|
||||
/**
|
||||
@@ -64,6 +66,8 @@ public class MapReduceOptions {
|
||||
* @return MapReduceOptions so that methods can be chained in a fluent API style
|
||||
*/
|
||||
public MapReduceOptions limit(int limit) {
|
||||
|
||||
this.limit = limit;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -247,6 +251,15 @@ public class MapReduceOptions {
|
||||
return this.scopeVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of documents for the input into the map function.
|
||||
*
|
||||
* @return {@literal null} if not set.
|
||||
*/
|
||||
public Integer getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public DBObject getOptionsObject() {
|
||||
BasicDBObject cmd = new BasicDBObject();
|
||||
|
||||
@@ -264,6 +277,10 @@ public class MapReduceOptions {
|
||||
cmd.put("scope", scopeVariables);
|
||||
}
|
||||
|
||||
if (limit != null) {
|
||||
cmd.put("limit", limit);
|
||||
}
|
||||
|
||||
if (!extraOptions.keySet().isEmpty()) {
|
||||
cmd.putAll(extraOptions);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 the original author or authors.
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,6 +52,7 @@ import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
@@ -66,6 +67,8 @@ import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MapReduceCommand;
|
||||
import com.mongodb.MapReduceOutput;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.ReadPreference;
|
||||
@@ -422,6 +425,112 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
verify(this.db, times(1)).command(Mockito.any(DBObject.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1334
|
||||
*/
|
||||
@Test
|
||||
public void mapReduceShouldUseZeroAsDefaultLimit() {
|
||||
|
||||
ArgumentCaptor<MapReduceCommand> captor = ArgumentCaptor.forClass(MapReduceCommand.class);
|
||||
|
||||
MapReduceOutput output = mock(MapReduceOutput.class);
|
||||
when(output.results()).thenReturn(Collections.<DBObject> emptySet());
|
||||
when(collection.mapReduce(Mockito.any(MapReduceCommand.class))).thenReturn(output);
|
||||
|
||||
Query query = new BasicQuery("{'foo':'bar'}");
|
||||
|
||||
template.mapReduce(query, "collection", "function(){}", "function(key,values){}", Wrapper.class);
|
||||
|
||||
verify(collection).mapReduce(captor.capture());
|
||||
|
||||
assertThat(captor.getValue().getLimit(), is(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1334
|
||||
*/
|
||||
@Test
|
||||
public void mapReduceShouldPickUpLimitFromQuery() {
|
||||
|
||||
ArgumentCaptor<MapReduceCommand> captor = ArgumentCaptor.forClass(MapReduceCommand.class);
|
||||
|
||||
MapReduceOutput output = mock(MapReduceOutput.class);
|
||||
when(output.results()).thenReturn(Collections.<DBObject> emptySet());
|
||||
when(collection.mapReduce(Mockito.any(MapReduceCommand.class))).thenReturn(output);
|
||||
|
||||
Query query = new BasicQuery("{'foo':'bar'}");
|
||||
query.limit(100);
|
||||
|
||||
template.mapReduce(query, "collection", "function(){}", "function(key,values){}", Wrapper.class);
|
||||
|
||||
verify(collection).mapReduce(captor.capture());
|
||||
|
||||
assertThat(captor.getValue().getLimit(), is(100));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1334
|
||||
*/
|
||||
@Test
|
||||
public void mapReduceShouldPickUpLimitFromOptions() {
|
||||
|
||||
ArgumentCaptor<MapReduceCommand> captor = ArgumentCaptor.forClass(MapReduceCommand.class);
|
||||
|
||||
MapReduceOutput output = mock(MapReduceOutput.class);
|
||||
when(output.results()).thenReturn(Collections.<DBObject> emptySet());
|
||||
when(collection.mapReduce(Mockito.any(MapReduceCommand.class))).thenReturn(output);
|
||||
|
||||
Query query = new BasicQuery("{'foo':'bar'}");
|
||||
|
||||
template.mapReduce(query, "collection", "function(){}", "function(key,values){}",
|
||||
new MapReduceOptions().limit(1000), Wrapper.class);
|
||||
|
||||
verify(collection).mapReduce(captor.capture());
|
||||
assertThat(captor.getValue().getLimit(), is(1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1334
|
||||
*/
|
||||
@Test
|
||||
public void mapReduceShouldPickUpLimitFromOptionsWhenQueryIsNotPresent() {
|
||||
|
||||
ArgumentCaptor<MapReduceCommand> captor = ArgumentCaptor.forClass(MapReduceCommand.class);
|
||||
|
||||
MapReduceOutput output = mock(MapReduceOutput.class);
|
||||
when(output.results()).thenReturn(Collections.<DBObject> emptySet());
|
||||
when(collection.mapReduce(Mockito.any(MapReduceCommand.class))).thenReturn(output);
|
||||
|
||||
template.mapReduce("collection", "function(){}", "function(key,values){}", new MapReduceOptions().limit(1000),
|
||||
Wrapper.class);
|
||||
|
||||
verify(collection).mapReduce(captor.capture());
|
||||
assertThat(captor.getValue().getLimit(), is(1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1334
|
||||
*/
|
||||
@Test
|
||||
public void mapReduceShouldPickUpLimitFromOptionsEvenWhenQueryDefinesItDifferently() {
|
||||
|
||||
ArgumentCaptor<MapReduceCommand> captor = ArgumentCaptor.forClass(MapReduceCommand.class);
|
||||
|
||||
MapReduceOutput output = mock(MapReduceOutput.class);
|
||||
when(output.results()).thenReturn(Collections.<DBObject> emptySet());
|
||||
when(collection.mapReduce(Mockito.any(MapReduceCommand.class))).thenReturn(output);
|
||||
|
||||
Query query = new BasicQuery("{'foo':'bar'}");
|
||||
query.limit(100);
|
||||
|
||||
template.mapReduce(query, "collection", "function(){}", "function(key,values){}",
|
||||
new MapReduceOptions().limit(1000), Wrapper.class);
|
||||
|
||||
verify(collection).mapReduce(captor.capture());
|
||||
|
||||
assertThat(captor.getValue().getLimit(), is(1000));
|
||||
}
|
||||
|
||||
class AutogenerateableId {
|
||||
|
||||
@Id BigInteger id;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,12 +15,40 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.mapreduce;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MapReduceOptionsTests {
|
||||
|
||||
@Test
|
||||
public void testFinalize() {
|
||||
new MapReduceOptions().finalizeFunction("code");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1334
|
||||
*/
|
||||
@Test
|
||||
public void limitShouldBeIncludedCorrectly() {
|
||||
|
||||
MapReduceOptions options = new MapReduceOptions();
|
||||
options.limit(10);
|
||||
|
||||
assertThat(options.getOptionsObject(), isBsonObject().containing("limit", 10));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1334
|
||||
*/
|
||||
@Test
|
||||
public void limitShouldNotBePresentInDboWhenNotSet() {
|
||||
assertThat(new MapReduceOptions().getOptionsObject(), isBsonObject().notContaining("limit"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user