From c1c7daf0ed5b78b824b50a3fcceb36e2195814a2 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 12 Dec 2016 11:09:46 +0100 Subject: [PATCH] DATAMONGO-1550 - Polishing $replaceRoot (aggregation stage). Original Pull Request: #422. --- .../aggregation/ReplaceRootOperation.java | 112 ++++++++++-------- .../ReplaceRootOperationUnitTests.java | 2 +- 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java index 9d0958fbc..95648b67b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java @@ -28,13 +28,14 @@ import com.mongodb.BasicDBObject; import com.mongodb.DBObject; /** - * Encapsulates the aggregation framework {@code $replaceRoot}-operation. - *

+ * Encapsulates the aggregation framework {@code $replaceRoot}-operation.
* We recommend to use the static factory method {@link Aggregation#replaceRoot(String)} instead of creating instances * of this class directly. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/#pipe._S_replaceRoot + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/ * @author Mark Paluch + * @author Christoph Strobl * @since 1.10 */ public class ReplaceRootOperation implements FieldsExposingAggregationOperation { @@ -42,32 +43,37 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation private final Replacement replacement; /** - * Creates a new {@link ReplaceRootOperation} given the {@link as} field name. + * Creates a new {@link ReplaceRootOperation} given the {@link Field} field name. * * @param field must not be {@literal null} or empty. */ public ReplaceRootOperation(Field field) { - this.replacement = new FieldReplacement(field); + this(new FieldReplacement(field)); } /** - * Creates a new {@link ReplaceRootOperation} given the {@link as} field name. + * Creates a new {@link ReplaceRootOperation} given the {@link AggregationExpression} pointing to a document. * * @param aggregationExpression must not be {@literal null}. */ public ReplaceRootOperation(AggregationExpression aggregationExpression) { - - Assert.notNull(aggregationExpression, "AggregationExpression must not be null!"); - this.replacement = new AggregationExpressionReplacement(aggregationExpression); + this(new AggregationExpressionReplacement(aggregationExpression)); } - protected ReplaceRootOperation(Replacement replacement) { + /** + * Creates a new {@link ReplaceRootOperation} given the {@link Replacement}. + * + * @param replacement must not be {@literal null}. + */ + public ReplaceRootOperation(Replacement replacement) { + + Assert.notNull(replacement, "Replacement must not be null!"); this.replacement = replacement; } /** * Creates a new {@link ReplaceRootDocumentOperationBuilder}. - * + * * @return a new {@link ReplaceRootDocumentOperationBuilder}. */ public static ReplaceRootOperationBuilder builder() { @@ -79,7 +85,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation */ @Override public DBObject toDBObject(AggregationOperationContext context) { - return new BasicDBObject("$replaceRoot", new BasicDBObject("newRoot", replacement.toObject(context))); + return new BasicDBObject("$replaceRoot", new BasicDBObject("newRoot", replacement.toDocumentExpression(context))); } /* (non-Javadoc) @@ -99,7 +105,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation /** * Defines a root document replacement based on a {@literal fieldName} that resolves to a document. - * + * * @param fieldName must not be {@literal null} or empty. * @return the final {@link ReplaceRootOperation}. */ @@ -118,8 +124,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation } /** - * Defines a root document replacement based on a composable document that is empty initially. - *

+ * Defines a root document replacement based on a composable document that is empty initially.
* {@link ReplaceRootOperation} can be populated with individual entries and derive its values from other, existing * documents. * @@ -130,8 +135,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation } /** - * Defines a root document replacement based on a composable document given {@literal dbObject} - *

+ * Defines a root document replacement based on a composable document given {@literal dbObject}.
* {@link ReplaceRootOperation} can be populated with individual entries and derive its values from other, existing * documents. * @@ -148,8 +152,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation /** * Encapsulates the aggregation framework {@code $replaceRoot}-operation to result in a composable replacement - * document. - *

+ * document.
* Instances of {@link ReplaceRootDocumentOperation} yield empty upon construction and can be populated with single * values and documents. * @@ -175,7 +178,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation /** * Creates an extended {@link ReplaceRootDocumentOperation} that combines {@link ReplacementDocument}s from the * {@literal currentOperation} and {@literal extension} operation. - * + * * @param currentOperation must not be {@literal null}. * @param extension must not be {@literal null}. */ @@ -185,9 +188,9 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation } /** - * Creates a new {@link ReplaceRootDocumentOperationBuilder} to define a field for the {@link AggregationExpression} - * . - * + * Creates a new {@link ReplaceRootDocumentOperationBuilder} to define a field for the + * {@link AggregationExpression}. + * * @param aggregationExpression must not be {@literal null}. * @return the {@link ReplaceRootDocumentOperationBuilder}. */ @@ -251,17 +254,18 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * Replacement object that results in a replacement document or an expression that results in a document. * * @author Mark Paluch + * @author Christoph Strobl */ - private abstract static class Replacement { + public interface Replacement { /** - * Renders the current {@link Replacement} into a {@link DBObject} based on the given + * Renders the current {@link Replacement} into a its MongoDB representation based on the given * {@link AggregationOperationContext}. * * @param context will never be {@literal null}. * @return a replacement document or an expression that results in a document. */ - public abstract Object toObject(AggregationOperationContext context); + Object toDocumentExpression(AggregationOperationContext context); } /** @@ -269,11 +273,13 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * * @author Mark Paluch */ - private static class AggregationExpressionReplacement extends Replacement { + private static class AggregationExpressionReplacement implements Replacement { private final AggregationExpression aggregationExpression; protected AggregationExpressionReplacement(AggregationExpression aggregationExpression) { + + Assert.notNull(aggregationExpression, "AggregationExpression must not be null!"); this.aggregationExpression = aggregationExpression; } @@ -281,17 +287,17 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.Replacement#toObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) */ @Override - public DBObject toObject(AggregationOperationContext context) { + public DBObject toDocumentExpression(AggregationOperationContext context) { return aggregationExpression.toDbObject(context); } } /** * {@link Replacement that references a {@link Field} inside the current aggregation pipeline. - * + * * @author Mark Paluch */ - private static class FieldReplacement extends Replacement { + private static class FieldReplacement implements Replacement { private final Field field; @@ -308,7 +314,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.Replacement#toObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) */ @Override - public Object toObject(AggregationOperationContext context) { + public Object toDocumentExpression(AggregationOperationContext context) { return context.getReference(field).toString(); } } @@ -318,7 +324,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * * @author Mark Paluch */ - private static class ReplacementDocument extends Replacement { + private static class ReplacementDocument implements Replacement { private final Collection replacements; @@ -378,12 +384,12 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.Replacement#toObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) */ @Override - public DBObject toObject(AggregationOperationContext context) { + public DBObject toDocumentExpression(AggregationOperationContext context) { DBObject dbObject = new BasicDBObject(); for (ReplacementContributor replacement : replacements) { - dbObject.putAll(replacement.toDBObject(context)); + dbObject.putAll(replacement.toDbObject(context)); } return dbObject; @@ -403,7 +409,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation ReplacementDocument replacementDocument = new ReplacementDocument(); List replacements = new ArrayList( - this.replacements.size() + replacementDocument.replacements.size()); + this.replacements.size() + extension.replacements.size()); replacements.addAll(this.replacements); replacements.addAll(extension.replacements); @@ -414,10 +420,10 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation /** * Partial {@link DBObject} contributor for document replacement. - * + * * @author Mark Paluch */ - private abstract static class ReplacementContributor { + private interface ReplacementContributor extends AggregationExpression { /** * Renders the current {@link ReplacementContributor} into a {@link DBObject} based on the given @@ -426,17 +432,17 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * @param context will never be {@literal null}. * @return */ - public abstract DBObject toDBObject(AggregationOperationContext context); + DBObject toDbObject(AggregationOperationContext context); } /** - * {@link ReplacementContributor} to contribute multiple fields based on the input {@literal value}. - *

+ * {@link ReplacementContributor} to contribute multiple fields based on the input {@literal value}.
* The value object is mapped into a MongoDB {@link DBObject}. - * + * * @author Mark Paluch + * @author Christoph Strobl */ - private static class DocumentContributor extends ReplacementContributor { + private static class DocumentContributor implements ReplacementContributor { private final Object value; @@ -455,11 +461,13 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplacementContributor#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) */ @Override - public DBObject toDBObject(AggregationOperationContext context) { + public DBObject toDbObject(AggregationOperationContext context) { - DBObject dbObject = new BasicDBObject("$set", value); + if (value instanceof DBObject) { + return (DBObject) value; + } - return (DBObject) context.getMappedObject(dbObject).get("$set"); + return (DBObject) context.getMappedObject(new BasicDBObject("$set", value)).get("$set"); } } @@ -469,7 +477,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * * @author Mark Paluch */ - private abstract static class FieldContributorSupport extends ReplacementContributor { + private abstract static class FieldContributorSupport implements ReplacementContributor { private final ExposedField field; @@ -521,10 +529,12 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplacementContributor#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) */ @Override - public DBObject toDBObject(AggregationOperationContext context) { + public DBObject toDbObject(AggregationOperationContext context) { - DBObject dbObject = new BasicDBObject("$set", value); - return new BasicDBObject(getField().getTarget(), context.getMappedObject(dbObject).get("$set")); + Object mappedValue = value instanceof BasicDBObject ? value + : context.getMappedObject(new BasicDBObject("$set", value)).get("$set"); + + return new BasicDBObject(getField().getTarget(), mappedValue); } } @@ -557,7 +567,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation * @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplacementContributor#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) */ @Override - public DBObject toDBObject(AggregationOperationContext context) { + public DBObject toDbObject(AggregationOperationContext context) { return new BasicDBObject(getField().getTarget(), aggregationExpression.toDbObject(context)); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java index 375372040..5a6830336 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java @@ -28,7 +28,7 @@ import com.mongodb.util.JSON; /** * Unit tests for {@link ReplaceRootOperation}. - * + * * @author Mark Paluch */ public class ReplaceRootOperationUnitTests {