DATAMONGO-586 - Yet another round of polish.
Added Apache license headers where missing. Removed separate package. Reduced visibility of ReferenceUtil as it's internal use only. Fixed formatting.
This commit is contained in:
committed by
Oliver Gierke
parent
b7b61405f9
commit
6b634d08ce
@@ -19,6 +19,9 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResult;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResults;
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.aggregation.operation.AggregationOperation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
@@ -118,6 +118,7 @@ import com.mongodb.util.JSONParseException;
|
||||
* @author Amol Nayak
|
||||
* @author Patryk Wasik
|
||||
* @author Tobias Trelle
|
||||
* @author Sebastian Herold
|
||||
*/
|
||||
public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
@@ -1239,10 +1240,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
return new AggregationResults<T>(mappedResults, commandResult);
|
||||
}
|
||||
|
||||
public <T> AggregationResults<T> aggregate(String inputCollectionName, Class<T> entityClass, AggregationOperation... operations) {
|
||||
return aggregate(inputCollectionName, new AggregationPipeline(operations), entityClass);
|
||||
}
|
||||
|
||||
public <T> AggregationResults<T> aggregate(String inputCollectionName, Class<T> entityClass,
|
||||
AggregationOperation... operations) {
|
||||
return aggregate(inputCollectionName, new AggregationPipeline(operations), entityClass);
|
||||
}
|
||||
|
||||
protected String replaceWithResourceIfNecessary(String function) {
|
||||
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
package org.springframework.data.mongodb.core.aggregation.operation;
|
||||
/*
|
||||
* Copyright 2013 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.aggregation;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Represents one single operation in an aggregation pipeline
|
||||
*
|
||||
* Represents one single operation in an aggregation pipeline.
|
||||
*
|
||||
* @author Sebastian Herold
|
||||
* @since 1.3
|
||||
*/
|
||||
public interface AggregationOperation {
|
||||
|
||||
/**
|
||||
* Gets the {@link DBObject} behind this operation
|
||||
*
|
||||
* @return the DBObject
|
||||
*/
|
||||
DBObject getDBObject();
|
||||
/**
|
||||
* Gets the {@link DBObject} backing this operation
|
||||
*
|
||||
* @return the DBObject
|
||||
*/
|
||||
DBObject getDBObject();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.core.aggregation.operation.AggregationOperation;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -40,17 +39,24 @@ public class AggregationPipeline {
|
||||
|
||||
private final List<DBObject> operations = new ArrayList<DBObject>();
|
||||
|
||||
public AggregationPipeline() {
|
||||
}
|
||||
public AggregationPipeline() {
|
||||
}
|
||||
|
||||
public AggregationPipeline(AggregationOperation... operations) {
|
||||
Assert.notNull(operations, "Operations are missing");
|
||||
/**
|
||||
* Creates a new {@link AggregationPipeline} from the given {@link AggregationOperation}s.
|
||||
*
|
||||
* @param operations must not be {@literal null} or empty.
|
||||
*/
|
||||
public AggregationPipeline(AggregationOperation... operations) {
|
||||
|
||||
for (AggregationOperation operation : operations) {
|
||||
Assert.notNull(operation, "Operation is not allowed to be null");
|
||||
this.operations.add(operation.getDBObject());
|
||||
}
|
||||
}
|
||||
Assert.notNull(operations, "Operations must not be null!");
|
||||
Assert.isTrue(operations.length > 0, "operations must not be empty!");
|
||||
|
||||
for (AggregationOperation operation : operations) {
|
||||
Assert.notNull(operation, "Operation is not allowed to be null");
|
||||
this.operations.add(operation.getDBObject());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a projection operation to the pipeline.
|
||||
@@ -174,17 +180,18 @@ public class AggregationPipeline {
|
||||
return operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an empty pipeline
|
||||
* @return the new pipeline
|
||||
*/
|
||||
public static AggregationPipeline pipeline() {
|
||||
return new AggregationPipeline();
|
||||
}
|
||||
/**
|
||||
* creates an empty pipeline
|
||||
*
|
||||
* @return the new pipeline
|
||||
*/
|
||||
public static AggregationPipeline pipeline() {
|
||||
return new AggregationPipeline();
|
||||
}
|
||||
|
||||
private AggregationPipeline addDocumentOperation(String opName, String operation) {
|
||||
|
||||
Assert.hasText(operation, "Missing operation name!");
|
||||
Assert.hasText(operation, "Operation must not be null or empty!");
|
||||
return addOperation(opName, parseJson(operation));
|
||||
}
|
||||
|
||||
@@ -194,10 +201,11 @@ public class AggregationPipeline {
|
||||
}
|
||||
|
||||
private DBObject parseJson(String json) {
|
||||
|
||||
try {
|
||||
return (DBObject) JSON.parse(json);
|
||||
} catch (JSONParseException e) {
|
||||
throw new IllegalArgumentException("Not a valid JSON document: " + json, e);
|
||||
throw new IllegalArgumentException("Invalid JSON document: " + json, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,334 +1,381 @@
|
||||
package org.springframework.data.mongodb.core.aggregation.operation;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import org.springframework.data.mongodb.core.aggregation.ReferenceUtil;
|
||||
import org.springframework.util.Assert;
|
||||
/*
|
||||
* Copyright 2013 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.aggregation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/group/#stage._S_group">
|
||||
* <code>$group</code>-operation
|
||||
* </a>
|
||||
*
|
||||
* Encapsulates the aggregation framework {@code $group}-operation.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/group/#stage._S_group
|
||||
* @author Sebastian Herold
|
||||
* @since 1.3
|
||||
*/
|
||||
public class GroupOperation implements AggregationOperation {
|
||||
|
||||
private static final String ID_KEY = "_id";
|
||||
private final Object id;
|
||||
private final Map<String, DBObject> fields = new HashMap<String, DBObject>();
|
||||
private static final String ID_KEY = "_id";
|
||||
|
||||
public GroupOperation(Object id) {
|
||||
this.id = id;
|
||||
}
|
||||
private final Object id;
|
||||
private final Map<String, DBObject> fields = new HashMap<String, DBObject>();
|
||||
|
||||
public DBObject getDBObject() {
|
||||
DBObject projection = new BasicDBObject(ID_KEY, id);
|
||||
for (Entry<String, DBObject> entry : fields.entrySet()) {
|
||||
projection.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return new BasicDBObject("$group", projection);
|
||||
}
|
||||
public GroupOperation(Object id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public GroupOperation addField(String key, DBObject value) {
|
||||
Assert.hasText(key, "Key is empty");
|
||||
Assert.notNull(value, "Value is null");
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getDBObject()
|
||||
*/
|
||||
public DBObject getDBObject() {
|
||||
|
||||
String trimmedKey = key.trim();
|
||||
if (ID_KEY.equals(trimmedKey)) {
|
||||
throw new IllegalArgumentException("_id field can only be set in constructor");
|
||||
}
|
||||
DBObject projection = new BasicDBObject(ID_KEY, id);
|
||||
|
||||
fields.put(key, value);
|
||||
for (Entry<String, DBObject> entry : fields.entrySet()) {
|
||||
projection.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
return new BasicDBObject("$group", projection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_addToSet">$addToSet operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$addToSet: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation addToSet(String name, String field) {
|
||||
return addOperation("$addToSet", name, field);
|
||||
}
|
||||
public GroupOperation addField(String key, DBObject value) {
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_first">$first operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$first: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation first(String name, String field) {
|
||||
return addOperation("$first", name, field);
|
||||
}
|
||||
Assert.hasText(key, "Key is empty");
|
||||
Assert.notNull(value, "Value is null");
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_last">$last operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$last: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation last(String name, String field) {
|
||||
return addOperation("$last", name, field);
|
||||
}
|
||||
String trimmedKey = key.trim();
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_max">$max operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$max: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation max(String name, String field) {
|
||||
return addOperation("$max", name, field);
|
||||
}
|
||||
if (ID_KEY.equals(trimmedKey)) {
|
||||
throw new IllegalArgumentException("_id field can only be set in constructor");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_min">$min operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$min: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation min(String name, String field) {
|
||||
return addOperation("$min", name, field);
|
||||
}
|
||||
fields.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the <a
|
||||
* href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_addToSet">$addToSet operation</a>.
|
||||
*
|
||||
* <pre>
|
||||
* { $group : {
|
||||
* _id : "$id_field",
|
||||
* name : { $addToSet : "$field" }
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_addToSet
|
||||
* @param name key of the field.
|
||||
* @param field reference to a field of the document.
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation addToSet(String name, String field) {
|
||||
return addOperation("$addToSet", name, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_avg">$avg operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$avg: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation avg(String name, String field) {
|
||||
return addOperation("$avg", name, field);
|
||||
}
|
||||
/**
|
||||
* Adds a field with the {@code $first} operation.
|
||||
*
|
||||
* <pre>
|
||||
* { $group : {
|
||||
* _id : "$id_field",
|
||||
* name : { $first : "$field" }
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_first
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation first(String name, String field) {
|
||||
return addOperation("$first", name, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_last">$last
|
||||
* operation</a>.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$last: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation last(String name, String field) {
|
||||
return addOperation("$last", name, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_push">$push operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$push: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation push(String name, String field) {
|
||||
return addOperation("$push", name, field);
|
||||
}
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_max">$max
|
||||
* operation</a>.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$max: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation max(String name, String field) {
|
||||
return addOperation("$max", name, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_sum">$sum operation</a>
|
||||
* with a constant value.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$sum: increment}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param increment increment for each item
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation count(String name, double increment) {
|
||||
return addField(name, new BasicDBObject("$sum", increment));
|
||||
}
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_min">$min
|
||||
* operation</a>.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$min: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation min(String name, String field) {
|
||||
return addOperation("$min", name, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_sum">$sum operation</a>
|
||||
* count every item.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$sum: 1}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation count(String name) {
|
||||
return count(name, 1);
|
||||
}
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_avg">$avg
|
||||
* operation</a>.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$avg: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation avg(String name, String field) {
|
||||
return addOperation("$avg", name, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with the
|
||||
* <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_sum">$sum operation</a>.
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$sum: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public GroupOperation sum(String name, String field) {
|
||||
return addOperation("$sum", name, field);
|
||||
}
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_push">$push
|
||||
* operation</a>.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$push: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation push(String name, String field) {
|
||||
return addOperation("$push", name, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>$group</code> operation with <code>_id</code> referencing to a field of the document. The
|
||||
* returned db object equals to <pre>{_id: "$field"}</pre>
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
public static GroupOperation group(String field) {
|
||||
return new GroupOperation(ReferenceUtil.safeReference(field));
|
||||
}
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_sum">$sum
|
||||
* operation</a> with a constant value.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$sum: increment}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @param increment increment for each item
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation count(String name, double increment) {
|
||||
return addField(name, new BasicDBObject("$sum", increment));
|
||||
}
|
||||
|
||||
protected GroupOperation addOperation(String operation, String name, String field) {
|
||||
return addField(name, new BasicDBObject(operation, ReferenceUtil.safeReference(field)));
|
||||
}
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_sum">$sum
|
||||
* operation</a> count every item.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$sum: 1}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation count(String name) {
|
||||
return count(name, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>$group</code> operation with a id that consists of multiple fields.
|
||||
*
|
||||
* Using {@link IdField#idField(String)} or {@link IdField#idField(String, String)} you can easily create
|
||||
* complex id fields like:
|
||||
* <pre>
|
||||
*
|
||||
* group(idField("path"), idField("pageView", "page.views"), idField("field3"))
|
||||
*
|
||||
* </pre>
|
||||
* which would result in:
|
||||
* <pre>
|
||||
*
|
||||
* {$group: {_id: {path: "$path", pageView: "$page.views", field3: "$field3"}}}
|
||||
*
|
||||
* </pre>
|
||||
* @param idFields
|
||||
* @return
|
||||
*/
|
||||
public static GroupOperation group(IdField... idFields) {
|
||||
Assert.notNull(idFields, "Combined id is null");
|
||||
/**
|
||||
* Adds a field with the <a href="http://docs.mongodb.org/manual/reference/aggregation/addToSet/#grp._S_sum">$sum
|
||||
* operation</a>.
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {
|
||||
* _id: "$id_field",
|
||||
* name: {$sum: "$field"}
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name key of the field
|
||||
* @param field reference to a field of the document
|
||||
* @return
|
||||
*/
|
||||
public GroupOperation sum(String name, String field) {
|
||||
return addOperation("$sum", name, field);
|
||||
}
|
||||
|
||||
BasicDBObject id = new BasicDBObject();
|
||||
for (IdField idField : idFields) {
|
||||
id.put(idField.getKey(), idField.getValue());
|
||||
}
|
||||
/**
|
||||
* Creates a <code>$group</code> operation with <code>_id</code> referencing to a field of the document. The returned
|
||||
* db object equals to
|
||||
*
|
||||
* <pre>
|
||||
* {_id: "$field"}
|
||||
* </pre>
|
||||
*
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
public static GroupOperation group(String field) {
|
||||
return new GroupOperation(ReferenceUtil.safeReference(field));
|
||||
}
|
||||
|
||||
return new GroupOperation(id);
|
||||
}
|
||||
protected GroupOperation addOperation(String operation, String name, String field) {
|
||||
return addField(name, new BasicDBObject(operation, ReferenceUtil.safeReference(field)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single field in a complex id of a <code>$group</code> operation.
|
||||
*
|
||||
* For example:
|
||||
* <pre>
|
||||
* {$group: {_id: {key: "$value"}}}
|
||||
* </pre>
|
||||
*/
|
||||
public static class IdField {
|
||||
/**
|
||||
* Creates a <code>$group</code> operation with a id that consists of multiple fields. Using
|
||||
* {@link IdField#idField(String)} or {@link IdField#idField(String, String)} you can easily create complex id fields
|
||||
* like:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* group(idField("path"), idField("pageView", "page.views"), idField("field3"))
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* which would result in:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* {$group: {_id: {path: "$path", pageView: "$page.views", field3: "$field3"}}}
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @param idFields
|
||||
* @return
|
||||
*/
|
||||
public static GroupOperation group(IdField... idFields) {
|
||||
Assert.notNull(idFields, "Combined id is null");
|
||||
|
||||
private final String key;
|
||||
private final String value;
|
||||
BasicDBObject id = new BasicDBObject();
|
||||
for (IdField idField : idFields) {
|
||||
id.put(idField.getKey(), idField.getValue());
|
||||
}
|
||||
|
||||
public IdField(String key, String value) {
|
||||
Assert.hasText(key, "Key is empty");
|
||||
Assert.hasText(value, "Value is empty");
|
||||
return new GroupOperation(id);
|
||||
}
|
||||
|
||||
this.key = ReferenceUtil.safeNonReference(key);
|
||||
this.value = ReferenceUtil.safeReference(value);
|
||||
}
|
||||
/**
|
||||
* Represents a single field in a complex id of a <code>$group</code> operation. For example:
|
||||
*
|
||||
* <pre>
|
||||
* {$group: {_id: {key: "$value"}}}
|
||||
* </pre>
|
||||
*/
|
||||
public static class IdField {
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
private final String key;
|
||||
private final String value;
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link IdField} with the given key and value.
|
||||
*
|
||||
* @param key must not be {@literal null} or empty.
|
||||
* @param value must not be {@literal null} or empty.
|
||||
*/
|
||||
public IdField(String key, String value) {
|
||||
|
||||
/**
|
||||
* Creates an id field with the name of the referenced field:
|
||||
* <pre>
|
||||
* _id: {field: "$field"}
|
||||
* </pre>
|
||||
* @param field reference to a field of the document
|
||||
* @return the id field
|
||||
*/
|
||||
public static IdField idField(String field) {
|
||||
return new IdField(field, field);
|
||||
}
|
||||
Assert.hasText(key, "Key must not be null or empty");
|
||||
Assert.hasText(value, "Value must not be null or empty");
|
||||
|
||||
/**
|
||||
* Creates an id field with key and reference
|
||||
* <pre>
|
||||
* _id: {key: "$field"}
|
||||
* </pre>
|
||||
* @param key the key
|
||||
* @param field reference to a field of the document
|
||||
* @return the id field
|
||||
*/
|
||||
public static IdField idField(String key, String field) {
|
||||
return new IdField(key, field);
|
||||
}
|
||||
}
|
||||
this.key = ReferenceUtil.safeNonReference(key);
|
||||
this.value = ReferenceUtil.safeReference(value);
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an id field with the name of the referenced field:
|
||||
*
|
||||
* <pre>
|
||||
* _id : { field : "$field" }
|
||||
* </pre>
|
||||
*
|
||||
* @param field reference to a field of the document
|
||||
* @return the id field
|
||||
*/
|
||||
public static IdField idField(String field) {
|
||||
return new IdField(field, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an id field with key and reference.
|
||||
*
|
||||
* <pre>
|
||||
* _id: {key: "$field"}
|
||||
* </pre>
|
||||
*
|
||||
* @param key the key
|
||||
* @param field reference to a field of the document
|
||||
* @return the id field
|
||||
*/
|
||||
public static IdField idField(String key, String field) {
|
||||
return new IdField(key, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,63 @@
|
||||
package org.springframework.data.mongodb.core.aggregation.operation;
|
||||
/*
|
||||
* Copyright 2013 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.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
|
||||
/**
|
||||
* Encapsulates the <code>$match</code>-operation
|
||||
*
|
||||
* Encapsulates the {@code $match}-operation
|
||||
*
|
||||
* @author Sebastian Herold
|
||||
* @since 1.3
|
||||
*/
|
||||
public class MatchOperation implements AggregationOperation {
|
||||
|
||||
private final DBObject criteria;
|
||||
private final DBObject criteria;
|
||||
|
||||
public MatchOperation(Criteria criteria) {
|
||||
this.criteria = criteria.getCriteriaObject();
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link MatchOperation} for the given {@link Criteria}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
*/
|
||||
public MatchOperation(Criteria criteria) {
|
||||
|
||||
public DBObject getDBObject() {
|
||||
return new BasicDBObject("$match", criteria);
|
||||
}
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
this.criteria = criteria.getCriteriaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link MatchOperation} for the given {@link Criteria}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static MatchOperation match(Criteria criteria) {
|
||||
return new MatchOperation(criteria);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getDBObject()
|
||||
*/
|
||||
public DBObject getDBObject() {
|
||||
return new BasicDBObject("$match", criteria);
|
||||
}
|
||||
|
||||
public static MatchOperation match(Criteria criteria) {
|
||||
return new MatchOperation(criteria);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013 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.aggregation;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility class for mongo db reference operator <code>$</code>
|
||||
*
|
||||
* @author Sebastian Herold
|
||||
* @since 1.3
|
||||
*/
|
||||
public class ReferenceUtil {
|
||||
class ReferenceUtil {
|
||||
|
||||
public static final String REFERENCE_PREFIX = "$";
|
||||
private static final String REFERENCE_PREFIX = "$";
|
||||
|
||||
/**
|
||||
* Ensures that the returned string begins with {@link #REFERENCE_PREFIX $}
|
||||
*
|
||||
* @param key reference key with or without {@link #REFERENCE_PREFIX $} at the beginning
|
||||
* @return key that definitely begins with {@link #REFERENCE_PREFIX $}
|
||||
*/
|
||||
public static String safeReference(String key) {
|
||||
/**
|
||||
* Ensures that the returned string begins with {@link #REFERENCE_PREFIX}.
|
||||
*
|
||||
* @param key reference key with or without {@link #REFERENCE_PREFIX} at the beginning.
|
||||
* @return key that definitely begins with {@link #REFERENCE_PREFIX}.
|
||||
*/
|
||||
public static String safeReference(String key) {
|
||||
|
||||
Assert.hasText(key);
|
||||
Assert.hasText(key);
|
||||
|
||||
if (!key.startsWith(REFERENCE_PREFIX)) {
|
||||
return REFERENCE_PREFIX + key;
|
||||
} else {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
if (!key.startsWith(REFERENCE_PREFIX)) {
|
||||
return REFERENCE_PREFIX + key;
|
||||
} else {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the returned string does not start with {@link #REFERENCE_PREFIX $}
|
||||
*
|
||||
* @param field reference key with or without {@link #REFERENCE_PREFIX $} at the beginning
|
||||
* @return key that definitely does not begin with {@link #REFERENCE_PREFIX $}
|
||||
*/
|
||||
public static String safeNonReference(String field) {
|
||||
/**
|
||||
* Ensures that the returned string does not start with {@link #REFERENCE_PREFIX}.
|
||||
*
|
||||
* @param field reference key with or without {@link #REFERENCE_PREFIX} at the beginning.
|
||||
* @return key that definitely does not begin with {@link #REFERENCE_PREFIX}.
|
||||
*/
|
||||
public static String safeNonReference(String field) {
|
||||
|
||||
Assert.hasText(field);
|
||||
Assert.hasText(field);
|
||||
|
||||
if (field.startsWith(REFERENCE_PREFIX)) {
|
||||
return field.substring(REFERENCE_PREFIX.length());
|
||||
}
|
||||
if (field.startsWith(REFERENCE_PREFIX)) {
|
||||
return field.substring(REFERENCE_PREFIX.length());
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,17 +89,23 @@ public class AggregationTests {
|
||||
|
||||
createDocuments();
|
||||
|
||||
AggregationPipeline pipeline = new AggregationPipeline().project("{_id:0,tags:1}}").unwind("tags")
|
||||
.group("{_id:\"$tags\", n:{$sum:1}}").project("{tag: \"$_id\", n:1, _id:0}")
|
||||
.sort(new Sort(new Sort.Order(Direction.DESC, "n")));
|
||||
AggregationPipeline pipeline = new AggregationPipeline(). //
|
||||
project("{_id:0,tags:1}}"). //
|
||||
unwind("tags"). //
|
||||
group("{_id:\"$tags\", n:{$sum:1}}"). //
|
||||
project("{tag: \"$_id\", n:1, _id:0}"). //
|
||||
sort(new Sort(new Sort.Order(Direction.DESC, "n")));
|
||||
|
||||
AggregationResults<TagCount> results = mongoTemplate.aggregate(INPUT_COLLECTION, pipeline, TagCount.class);
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results.getServerUsed(), is("/127.0.0.1:27017"));
|
||||
|
||||
List<TagCount> tagCount = results.getAggregationResult();
|
||||
|
||||
assertThat(tagCount, is(notNullValue()));
|
||||
assertThat(tagCount.size(), is(3));
|
||||
|
||||
assertTagCount("spring", 3, tagCount.get(0));
|
||||
assertTagCount("mongodb", 2, tagCount.get(1));
|
||||
assertTagCount("nosql", 1, tagCount.get(2));
|
||||
@@ -116,14 +122,20 @@ public class AggregationTests {
|
||||
@Test
|
||||
public void shouldAggregateEmptyCollection() {
|
||||
|
||||
AggregationPipeline pipeline = new AggregationPipeline().project("{_id:0,tags:1}}").unwind("$tags")
|
||||
.group("{_id:\"$tags\", n:{$sum:1}}").project("{tag: \"$_id\", n:1, _id:0}").sort("{n:-1}");
|
||||
AggregationPipeline pipeline = new AggregationPipeline(). //
|
||||
project("{_id:0,tags:1}}"). //
|
||||
unwind("$tags").//
|
||||
group("{_id:\"$tags\", n:{$sum:1}}").//
|
||||
project("{tag: \"$_id\", n:1, _id:0}").//
|
||||
sort("{n:-1}");
|
||||
|
||||
AggregationResults<TagCount> results = mongoTemplate.aggregate(INPUT_COLLECTION, pipeline, TagCount.class);
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results.getServerUsed(), is("/127.0.0.1:27017"));
|
||||
|
||||
List<TagCount> tagCount = results.getAggregationResult();
|
||||
|
||||
assertThat(tagCount, is(notNullValue()));
|
||||
assertThat(tagCount.size(), is(0));
|
||||
}
|
||||
@@ -132,14 +144,19 @@ public class AggregationTests {
|
||||
public void shouldDetectResultMismatch() {
|
||||
|
||||
createDocuments();
|
||||
AggregationPipeline pipeline = new AggregationPipeline().project("{_id:0,tags:1}}").unwind("$tags")
|
||||
.group("{_id:\"$tags\", count:{$sum:1}}").limit(2);
|
||||
AggregationPipeline pipeline = new AggregationPipeline(). //
|
||||
project("{_id:0,tags:1}}"). //
|
||||
unwind("$tags"). //
|
||||
group("{_id:\"$tags\", count:{$sum:1}}"). //
|
||||
limit(2);
|
||||
|
||||
AggregationResults<TagCount> results = mongoTemplate.aggregate(INPUT_COLLECTION, pipeline, TagCount.class);
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results.getServerUsed(), is("/127.0.0.1:27017"));
|
||||
|
||||
List<TagCount> tagCount = results.getAggregationResult();
|
||||
|
||||
assertThat(tagCount, is(notNullValue()));
|
||||
assertThat(tagCount.size(), is(2));
|
||||
assertTagCount(null, 0, tagCount.get(0));
|
||||
@@ -153,6 +170,7 @@ public class AggregationTests {
|
||||
private void createDocuments() {
|
||||
|
||||
DBCollection coll = mongoTemplate.getCollection(INPUT_COLLECTION);
|
||||
|
||||
coll.insert(createDocument("Doc1", "spring", "mongodb", "nosql"));
|
||||
coll.insert(createDocument("Doc2", "spring", "mongodb"));
|
||||
coll.insert(createDocument("Doc3", "spring"));
|
||||
|
||||
Reference in New Issue
Block a user