/*
* Copyright 2010-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapreduce;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.bson.Document;
import org.springframework.data.mongodb.core.Collation;
import com.mongodb.MapReduceCommand;
/**
* @author Mark Pollack
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class MapReduceOptions {
private String outputCollection;
private Optional<String> outputDatabase = Optional.empty();
private MapReduceCommand.OutputType outputType = MapReduceCommand.OutputType.REPLACE;
private Map<String, Object> scopeVariables = new HashMap<String, Object>();
private Map<String, Object> extraOptions = new HashMap<String, Object>();
private Boolean jsMode;
private Boolean verbose = Boolean.TRUE;
private Integer limit;
private Optional<Boolean> outputSharded = Optional.empty();
private Optional<String> finalizeFunction = Optional.empty();
private Optional<Collation> collation = Optional.empty();
/**
* Static factory method to create a MapReduceOptions instance
*
* @return a new instance
*/
public static MapReduceOptions options() {
return new MapReduceOptions();
}
/**
* Limit the number of objects to return from the collection that is fed into the map reduce operation Often used in
* conjunction with a query and sort option so as to reduce the portion of the data that will be processed.
*
* @param limit Limit the number of objects to process
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions limit(int limit) {
this.limit = limit;
return this;
}
/**
* The collection where the results from the map-reduce operation will be stored. Note, you can set the database name
* as well with the outputDatabase option.
*
* @param collectionName The name of the collection where the results of the map-reduce operation will be stored.
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions outputCollection(String collectionName) {
this.outputCollection = collectionName;
return this;
}
/**
* The database where the results from the map-reduce operation will be stored. Note, you ca set the collection name
* as well with the outputCollection option.
*
* @param outputDatabase The name of the database where the results of the map-reduce operation will be stored.
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions outputDatabase(String outputDatabase) {
this.outputDatabase = Optional.ofNullable(outputDatabase);
return this;
}
/**
* With this option, no collection will be created, and the whole map-reduce operation will happen in RAM. Also, the
* results of the map-reduce will be returned within the result object. Note that this option is possible only when
* the result set fits within the 16MB limit of a single document.
*
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions outputTypeInline() {
this.outputType = MapReduceCommand.OutputType.INLINE;
return this;
}
/**
* This option will merge new data into the old output collection. In other words, if the same key exists in both the
* result set and the old collection, the new key will overwrite the old one.
*
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions outputTypeMerge() {
this.outputType = MapReduceCommand.OutputType.MERGE;
return this;
}
/**
* If documents exists for a given key in the result set and in the old collection, then a reduce operation (using the
* specified reduce function) will be performed on the two values and the result will be written to the output
* collection. If a finalize function was provided, this will be run after the reduce as well.
*
* @return
*/
public MapReduceOptions outputTypeReduce() {
this.outputType = MapReduceCommand.OutputType.REDUCE;
return this;
}
/**
* The output will be inserted into a collection which will atomically replace any existing collection with the same
* name. Note, the default is MapReduceCommand.OutputType.REPLACE
*
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions outputTypeReplace() {
this.outputType = MapReduceCommand.OutputType.REPLACE;
return this;
}
/**
* If true and combined with an output mode that writes to a collection, the output collection will be sharded using
* the _id field. For MongoDB 1.9+
*
* @param outputShared if true, output will be sharded based on _id key.
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions outputSharded(boolean outputShared) {
this.outputSharded = Optional.of(outputShared);
return this;
}
/**
* Sets the finalize function
*
* @param finalizeFunction The finalize function. Can be a JSON string or a Spring Resource URL
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions finalizeFunction(String finalizeFunction) {
this.finalizeFunction = Optional.ofNullable(finalizeFunction);
return this;
}
/**
* Key-value pairs that are placed into JavaScript global scope and can be accessed from map, reduce, and finalize
* scripts.
*
* @param scopeVariables variables that can be accessed from map, reduce, and finalize scripts
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions scopeVariables(Map<String, Object> scopeVariables) {
this.scopeVariables = scopeVariables;
return this;
}
/**
* Flag that toggles behavior in the map-reduce operation so as to avoid intermediate conversion to BSON between the
* map and reduce steps. For MongoDB 1.9+
*
* @param javaScriptMode if true, have the execution of map-reduce stay in JavaScript
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions javaScriptMode(boolean javaScriptMode) {
this.jsMode = javaScriptMode;
return this;
}
/**
* Flag to set that will provide statistics on job execution time.
*
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions verbose(boolean verbose) {
this.verbose = verbose;
return this;
}
/**
* Add additional extra options that may not have a method on this class. This method will help if you use a version
* of this client library with a server version that has added additional map-reduce options that do not yet have an
* method for use in setting them. options
*
* @param key The key option
* @param value The value of the option
* @return MapReduceOptions so that methods can be chained in a fluent API style
* @deprecated since 1.7.
*/
@Deprecated
public MapReduceOptions extraOption(String key, Object value) {
extraOptions.put(key, value);
return this;
}
/**
* Define the Collation specifying language-specific rules for string comparison.
*
* @param collation can be {@literal null}.
* @return
* @since 2.0
*/
public MapReduceOptions collation(Collation collation) {
this.collation = Optional.ofNullable(collation);
return this;
}
/**
* @return
* @deprecated since 1.7
*/
@Deprecated
public Map<String, Object> getExtraOptions() {
return extraOptions;
}
public Optional<String> getFinalizeFunction() {
return this.finalizeFunction;
}
public Boolean getJavaScriptMode() {
return this.jsMode;
}
public String getOutputCollection() {
return this.outputCollection;
}
public Optional<String> getOutputDatabase() {
return this.outputDatabase;
}
public Optional<Boolean> getOutputSharded() {
return this.outputSharded;
}
public MapReduceCommand.OutputType getOutputType() {
return this.outputType;
}
public Map<String, Object> getScopeVariables() {
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;
}
/**
* Get the Collation specifying language-specific rules for string comparison.
*
* @return
* @since 2.0
*/
public Optional<Collation> getCollation() {
return collation;
}
public Document getOptionsObject() {
Document cmd = new Document();
if (verbose != null) {
cmd.put("verbose", verbose);
}
cmd.put("out", createOutObject());
finalizeFunction.ifPresent(val -> cmd.append("finalize", val));
if (scopeVariables != null) {
cmd.put("scope", scopeVariables);
}
if (limit != null) {
cmd.put("limit", limit);
}
if (!extraOptions.keySet().isEmpty()) {
cmd.putAll(extraOptions);
}
getCollation().ifPresent(val -> cmd.append("collation", val.toDocument()));
return cmd;
}
protected Document createOutObject() {
Document out = new Document();
switch (outputType) {
case INLINE:
out.put("inline", 1);
break;
case REPLACE:
out.put("replace", outputCollection);
break;
case MERGE:
out.put("merge", outputCollection);
break;
case REDUCE:
out.put("reduce", outputCollection);
break;
}
outputDatabase.ifPresent(val -> out.append("db", val));
outputSharded.ifPresent(val -> out.append("sharded", val));
return out;
}
}