DATAMONGO-2363 - Polishing.
Introduce support for SpEL aggregation expressions for AddFields and Set operations. Rearrange methods. Make fields final where possible. Original pull request: #801.
This commit is contained in:
@@ -151,6 +151,13 @@ public class AddFieldsOperation extends DocumentEnhancingOperation {
|
||||
valueMap.put(field, value instanceof String ? Fields.fields((String) value) : value);
|
||||
return AddFieldsOperationBuilder.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values) {
|
||||
|
||||
valueMap.put(field, new ExpressionProjection(operation, values));
|
||||
return AddFieldsOperationBuilder.this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -179,7 +186,15 @@ public class AddFieldsOperation extends DocumentEnhancingOperation {
|
||||
* @return new instance of {@link AddFieldsOperation}.
|
||||
*/
|
||||
AddFieldsOperationBuilder withValueOf(Object value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a generic projection for the current field.
|
||||
*
|
||||
* @param operation the operation key, e.g. {@code $add}.
|
||||
* @param values the values to be set for the projection operation.
|
||||
* @return new instance of {@link AddFieldsOperation}.
|
||||
*/
|
||||
AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link AggregationUpdate} from the given {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param operations can be {@literal empty} but must not be {@literal null}.
|
||||
* @return new instance of {@link AggregationUpdate}.
|
||||
* @since 3.0
|
||||
@@ -202,11 +202,16 @@ public class Aggregation {
|
||||
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
|
||||
Assert.notNull(options, "AggregationOptions must not be null!");
|
||||
|
||||
// check $out is the last operation if it exists
|
||||
// check $out/$merge is the last operation if it exists
|
||||
for (AggregationOperation aggregationOperation : aggregationOperations) {
|
||||
|
||||
if (aggregationOperation instanceof OutOperation && !isLast(aggregationOperation, aggregationOperations)) {
|
||||
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
|
||||
}
|
||||
|
||||
if (aggregationOperation instanceof MergeOperation && !isLast(aggregationOperation, aggregationOperations)) {
|
||||
throw new IllegalArgumentException("The $merge operator must be the last stage in the pipeline.");
|
||||
}
|
||||
}
|
||||
|
||||
this.operations = aggregationOperations;
|
||||
@@ -236,6 +241,20 @@ public class Aggregation {
|
||||
return "_id";
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an {@link AddFieldsOperationBuilder builder} instance to create a new {@link AddFieldsOperation}.
|
||||
* <p/>
|
||||
* Starting in version 4.2, MongoDB adds a new aggregation pipeline stage {@link AggregationUpdate#set $set} that is
|
||||
* an alias for {@code $addFields}.
|
||||
*
|
||||
* @return new instance of {@link AddFieldsOperationBuilder}.
|
||||
* @see AddFieldsOperation
|
||||
* @since 3.0
|
||||
*/
|
||||
public static AddFieldsOperationBuilder addFields() {
|
||||
return AddFieldsOperation.builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionOperation} including the given fields.
|
||||
*
|
||||
@@ -495,6 +514,30 @@ public class Aggregation {
|
||||
return new MatchOperation(criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
|
||||
* {@code distanceField} defines output field that contains the calculated distance.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param distanceField must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
|
||||
return new GeoNearOperation(query, distanceField);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link MergeOperationBuilder builder} instance to create a new {@link MergeOperation}.
|
||||
*
|
||||
* @return new instance of {@link MergeOperationBuilder}.
|
||||
* @see MergeOperation
|
||||
* @since 3.0
|
||||
*/
|
||||
public static MergeOperationBuilder merge() {
|
||||
return MergeOperation.builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation in
|
||||
* the pipeline.
|
||||
@@ -636,41 +679,6 @@ public class Aggregation {
|
||||
return Fields.from(field(name, target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
|
||||
* {@code distanceField} defines output field that contains the calculated distance.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param distanceField must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
|
||||
return new GeoNearOperation(query, distanceField);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link MergeOperationBuilder builder} instance to create a new {@link MergeOperation}.
|
||||
*
|
||||
* @return new instance of {@link MergeOperationBuilder}.
|
||||
* @see MergeOperation
|
||||
* @since 3.0
|
||||
*/
|
||||
public static MergeOperationBuilder merge() {
|
||||
return MergeOperation.builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an {@link AddFieldsOperationBuilder builder} instance to create a new {@link AddFieldsOperation}.
|
||||
*
|
||||
* @return new instance of {@link AddFieldsOperationBuilder}.
|
||||
* @see AddFieldsOperation
|
||||
* @since 3.0
|
||||
*/
|
||||
public static AddFieldsOperationBuilder addFields() {
|
||||
return AddFieldsOperation.builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link AggregationOptions.Builder}.
|
||||
*
|
||||
|
||||
@@ -79,7 +79,7 @@ import org.springframework.util.Assert;
|
||||
public class AggregationUpdate extends Aggregation implements UpdateDefinition {
|
||||
|
||||
private boolean isolated = false;
|
||||
private Set<String> keysTouched = new HashSet<>();
|
||||
private final Set<String> keysTouched = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Create new {@link AggregationUpdate}.
|
||||
|
||||
@@ -24,16 +24,18 @@ import java.util.stream.Collectors;
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for common taks required by {@link SetOperation} and {@link AddFieldsOperation}.
|
||||
* Base class for common tasks required by {@link SetOperation} and {@link AddFieldsOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
abstract class DocumentEnhancingOperation implements InheritsFieldsAggregationOperation {
|
||||
|
||||
private Map<Object, Object> valueMap;
|
||||
private final Map<Object, Object> valueMap;
|
||||
|
||||
private ExposedFields exposedFields = ExposedFields.empty();
|
||||
|
||||
protected DocumentEnhancingOperation(Map<Object, Object> source) {
|
||||
@@ -112,14 +114,53 @@ abstract class DocumentEnhancingOperation implements InheritsFieldsAggregationOp
|
||||
if (value instanceof Field) {
|
||||
return context.getReference((Field) value).toString();
|
||||
}
|
||||
|
||||
if (value instanceof ExpressionProjection) {
|
||||
return ((ExpressionProjection) value).toExpression(context);
|
||||
}
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDocument(context);
|
||||
}
|
||||
|
||||
if (value instanceof Collection) {
|
||||
return ((Collection) value).stream().map(it -> computeValue(it, context)).collect(Collectors.toList());
|
||||
return ((Collection<?>) value).stream().map(it -> computeValue(it, context)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link AggregationExpression} based on a SpEL expression.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ExpressionProjection {
|
||||
|
||||
private static final SpelExpressionTransformer TRANSFORMER = new SpelExpressionTransformer();
|
||||
|
||||
private final String expression;
|
||||
private final Object[] params;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionOperation.ExpressionProjectionOperationBuilder.ExpressionProjection} for the given
|
||||
* field, SpEL expression and parameters.
|
||||
*
|
||||
* @param expression must not be {@literal null} or empty.
|
||||
* @param parameters must not be {@literal null}.
|
||||
*/
|
||||
ExpressionProjection(String expression, Object[] parameters) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
Assert.notNull(parameters, "Parameters must not be null!");
|
||||
|
||||
this.expression = expression;
|
||||
this.params = parameters.clone();
|
||||
}
|
||||
|
||||
Object toExpression(AggregationOperationContext context) {
|
||||
return TRANSFORMER.transform(expression, context, params);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* Encapsulates the {@code $merge}-operation.
|
||||
* <p>
|
||||
* We recommend to use the {@link MergeOperationBuilder builder} via {@link MergeOperation#builder()} instead of creating
|
||||
* instances of this class directly.
|
||||
* We recommend to use the {@link MergeOperationBuilder builder} via {@link MergeOperation#builder()} instead of
|
||||
* creating instances of this class directly.
|
||||
*
|
||||
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/merge/">MongoDB Documentation</a>
|
||||
* @author Christoph Strobl
|
||||
@@ -42,15 +42,15 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class MergeOperation implements FieldsExposingAggregationOperation, InheritsFieldsAggregationOperation {
|
||||
|
||||
private MergeOperationTarget into;
|
||||
private UniqueMergeId on;
|
||||
private @Nullable Let let;
|
||||
private @Nullable WhenDocumentsMatch whenMatched;
|
||||
private @Nullable WhenDocumentsDontMatch whenNotMatched;
|
||||
private final MergeOperationTarget into;
|
||||
private final UniqueMergeId on;
|
||||
private final @Nullable Let let;
|
||||
private final @Nullable WhenDocumentsMatch whenMatched;
|
||||
private final @Nullable WhenDocumentsDontMatch whenNotMatched;
|
||||
|
||||
/**
|
||||
* Create new instance of {@link MergeOperation}.
|
||||
*
|
||||
*
|
||||
* @param into the target (collection and database)
|
||||
* @param on the unique identifier. Can be {@literal null}.
|
||||
* @param let exposed variables for {@link WhenDocumentsMatch#updateWith(Aggregation)}. Can be {@literal null}.
|
||||
@@ -63,7 +63,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
@Nullable WhenDocumentsMatch whenMatched, @Nullable WhenDocumentsDontMatch whenNotMatched) {
|
||||
|
||||
Assert.notNull(into, "Into must not be null! Please provide a target collection.");
|
||||
Assert.notNull(on, "On must not be null! Use Collections.emptySet() instead.");
|
||||
Assert.notNull(on, "On must not be null! Use UniqueMergeId.id() instead.");
|
||||
|
||||
this.into = into;
|
||||
this.on = on;
|
||||
@@ -93,7 +93,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.Aggregation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
@@ -109,15 +109,17 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
if (!on.isJustIdField()) {
|
||||
$merge.putAll(on.toDocument(context));
|
||||
}
|
||||
|
||||
if (let != null) {
|
||||
$merge.append("let", let.toDocument(context).get("$let", Document.class).get("vars"));
|
||||
}
|
||||
|
||||
if (whenMatched != null) {
|
||||
$merge.putAll(whenMatched.toDocument(context));
|
||||
}
|
||||
|
||||
if (whenNotMatched != null) {
|
||||
$merge.putAll(whenNotMatched.toDocument(context));
|
||||
|
||||
}
|
||||
|
||||
return new Document("$merge", $merge);
|
||||
@@ -159,13 +161,12 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
* collection.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.3
|
||||
*/
|
||||
public static class UniqueMergeId {
|
||||
|
||||
private static final UniqueMergeId ID = new UniqueMergeId(Collections.emptyList());
|
||||
|
||||
private Collection<String> uniqueIdentifier;
|
||||
private final Collection<String> uniqueIdentifier;
|
||||
|
||||
private UniqueMergeId(Collection<String> uniqueIdentifier) {
|
||||
this.uniqueIdentifier = uniqueIdentifier;
|
||||
@@ -173,6 +174,8 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
|
||||
public static UniqueMergeId ofIdFields(String... fields) {
|
||||
|
||||
Assert.noNullElements(fields, "Fields must not contain null values!");
|
||||
|
||||
if (ObjectUtils.isEmpty(fields)) {
|
||||
return id();
|
||||
}
|
||||
@@ -182,7 +185,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
|
||||
/**
|
||||
* Merge Documents by using the MongoDB {@literal _id} field.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static UniqueMergeId id() {
|
||||
@@ -206,19 +209,19 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
* If not stated explicitly via {@link MergeOperationTarget#inDatabase(String)} the {@literal collection} is created
|
||||
* in the very same {@literal database}. In this case {@code into} is just a single String holding the collection
|
||||
* name. <br />
|
||||
*
|
||||
*
|
||||
* <pre class="code">
|
||||
* into: "target-collection-name"
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* If the collection needs to be in a different database {@code into} will be a {@link Document} like the following
|
||||
*
|
||||
*
|
||||
* <pre class="code">
|
||||
* {
|
||||
* into: {}
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.3
|
||||
*/
|
||||
@@ -267,7 +270,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
/**
|
||||
* Value Object specifying how to deal with a result document that matches an existing document in the collection
|
||||
* based on the fields of the {@code on} property describing the unique identifier.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.3
|
||||
*/
|
||||
@@ -354,7 +357,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
/**
|
||||
* Value Object specifying how to deal with a result document that do not match an existing document in the collection
|
||||
* based on the fields of the {@code on} property describing the unique identifier.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.3
|
||||
*/
|
||||
@@ -363,9 +366,18 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
private final String value;
|
||||
|
||||
private WhenDocumentsDontMatch(String value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method creating {@link WhenDocumentsDontMatch} from a {@code value} literal.
|
||||
*
|
||||
* @param value
|
||||
* @return new instance of {@link WhenDocumentsDontMatch}.
|
||||
*/
|
||||
public static WhenDocumentsDontMatch whenNotMatchedOf(String value) {
|
||||
return new WhenDocumentsDontMatch(value);
|
||||
}
|
||||
@@ -412,7 +424,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
|
||||
private String collection;
|
||||
private @Nullable String database;
|
||||
private @Nullable UniqueMergeId id;
|
||||
private UniqueMergeId id = UniqueMergeId.id();
|
||||
private @Nullable Let let;
|
||||
private @Nullable WhenDocumentsMatch whenMatched;
|
||||
private @Nullable WhenDocumentsDontMatch whenNotMatched;
|
||||
@@ -447,7 +459,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
|
||||
/**
|
||||
* Define the target to store results in.
|
||||
*
|
||||
*
|
||||
* @param into must not be {@literal null}.
|
||||
* @return this.
|
||||
*/
|
||||
@@ -484,7 +496,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
/**
|
||||
* Set the identifier that determines if a results document matches an already existing document in the output
|
||||
* collection.
|
||||
*
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @return this.
|
||||
*/
|
||||
@@ -497,7 +509,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
/**
|
||||
* Expose the variables defined by {@link Let} to the {@link WhenDocumentsMatch#updateWith(Aggregation) update
|
||||
* aggregation}.
|
||||
*
|
||||
*
|
||||
* @param let the variable expressions
|
||||
* @return this.
|
||||
*/
|
||||
@@ -520,7 +532,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
|
||||
/**
|
||||
* The action to take place when documents already exist in the target collection.
|
||||
*
|
||||
*
|
||||
* @param whenMatched must not be {@literal null}.
|
||||
* @return this.
|
||||
*/
|
||||
@@ -576,8 +588,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher
|
||||
* @return new instance of {@link MergeOperation}.
|
||||
*/
|
||||
public MergeOperation build() {
|
||||
return new MergeOperation(new MergeOperationTarget(database, collection), id != null ? id : UniqueMergeId.id(),
|
||||
let, whenMatched, whenNotMatched);
|
||||
return new MergeOperation(new MergeOperationTarget(database, collection), id, let, whenMatched, whenNotMatched);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* Adds new fields to documents. {@code $set} outputs documents that contain all existing fields from the input
|
||||
* documents and newly added fields.
|
||||
*
|
||||
*
|
||||
* <pre class="code">
|
||||
* SetOperation.set("totalHomework").toValue("A+").and().set("totalQuiz").toValue("B-")
|
||||
* </pre>
|
||||
@@ -143,6 +143,13 @@ public class SetOperation extends DocumentEnhancingOperation {
|
||||
valueMap.put(field, value instanceof String ? Fields.fields((String) value) : value);
|
||||
return FieldAppender.this.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetOperation withValueOfExpression(String operation, Object... values) {
|
||||
|
||||
valueMap.put(field, new ExpressionProjection(operation, values));
|
||||
return FieldAppender.this.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -152,6 +159,7 @@ public class SetOperation extends DocumentEnhancingOperation {
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ValueAppender {
|
||||
@@ -171,6 +179,15 @@ public class SetOperation extends DocumentEnhancingOperation {
|
||||
* @return new instance of {@link SetOperation}.
|
||||
*/
|
||||
SetOperation toValueOf(Object value);
|
||||
|
||||
/**
|
||||
* Adds a generic projection for the current field.
|
||||
*
|
||||
* @param operation the operation key, e.g. {@code $add}.
|
||||
* @param values the values to be set for the projection operation.
|
||||
* @return new instance of {@link SetOperation}.
|
||||
*/
|
||||
SetOperation withValueOfExpression(String operation, Object... values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,10 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AddFieldsOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class AddFieldsOperationUnitTests {
|
||||
|
||||
@@ -92,6 +95,17 @@ class AddFieldsOperationUnitTests {
|
||||
"{\"$addFields\" : {\"scoresWithMappedField.student_name\":\"$scoresWithMappedField.home_work\"}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2363
|
||||
void appliesSpelExpressionCorrectly() {
|
||||
|
||||
AddFieldsOperation operation = AddFieldsOperation.builder().addField("totalHomework")
|
||||
.withValueOfExpression("sum(homework) * [0]", 2) //
|
||||
.build();
|
||||
|
||||
assertThat(operation.toPipelineStages(contextFor(ScoresWrapper.class))).contains(
|
||||
Document.parse("{\"$addFields\" : {\"totalHomework\": { $multiply : [{ \"$sum\" : [\"$homework\"] }, 2] }}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2363
|
||||
void rendersTargetValueExpressionCorrectly() {
|
||||
|
||||
@@ -105,10 +119,11 @@ class AddFieldsOperationUnitTests {
|
||||
|
||||
ExposedFields fields = AddFieldsOperation.builder().addField("totalHomework").withValue("A+") //
|
||||
.addField("totalQuiz").withValue("B-") //
|
||||
.build().getFields();
|
||||
.addField("computed").withValueOfExpression("totalHomework").build().getFields();
|
||||
|
||||
assertThat(fields.getField("totalHomework")).isNotNull();
|
||||
assertThat(fields.getField("totalQuiz")).isNotNull();
|
||||
assertThat(fields.getField("computed")).isNotNull();
|
||||
assertThat(fields.getField("does-not-exist")).isNull();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MergeOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class MergeOperationUnitTests {
|
||||
@@ -98,7 +100,7 @@ class MergeOperationUnitTests {
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2363
|
||||
public void mapsFieldNames() {
|
||||
void mapsFieldNames() {
|
||||
|
||||
assertThat(merge().intoCollection("newrestaurants").on("date", "postCode").build()
|
||||
.toDocument(contextFor(Restaurant.class))).isEqualTo(
|
||||
|
||||
@@ -32,30 +32,31 @@ import org.springframework.lang.Nullable;
|
||||
* Unit tests for {@link SetOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class SetOperationUnitTests {
|
||||
class SetOperationUnitTests {
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void raisesErrorOnNullField() {
|
||||
void raisesErrorOnNullField() {
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new SetOperation(null, "value"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void rendersFieldReferenceCorrectly() {
|
||||
void rendersFieldReferenceCorrectly() {
|
||||
|
||||
assertThat(new SetOperation("name", "value").toPipelineStages(contextFor(Scores.class)))
|
||||
.containsExactly(Document.parse("{\"$set\" : {\"name\":\"value\"}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void rendersMappedFieldReferenceCorrectly() {
|
||||
void rendersMappedFieldReferenceCorrectly() {
|
||||
|
||||
assertThat(new SetOperation("student", "value").toPipelineStages(contextFor(ScoresWithMappedField.class)))
|
||||
.containsExactly(Document.parse("{\"$set\" : {\"student_name\":\"value\"}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void rendersNestedMappedFieldReferenceCorrectly() {
|
||||
void rendersNestedMappedFieldReferenceCorrectly() {
|
||||
|
||||
assertThat(
|
||||
new SetOperation("scoresWithMappedField.student", "value").toPipelineStages(contextFor(ScoresWrapper.class)))
|
||||
@@ -63,14 +64,14 @@ public class SetOperationUnitTests {
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void rendersTargetValueFieldReferenceCorrectly() {
|
||||
void rendersTargetValueFieldReferenceCorrectly() {
|
||||
|
||||
assertThat(new SetOperation("name", Fields.field("value")).toPipelineStages(contextFor(Scores.class)))
|
||||
.containsExactly(Document.parse("{\"$set\" : {\"name\":\"$value\"}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void rendersMappedTargetValueFieldReferenceCorrectly() {
|
||||
void rendersMappedTargetValueFieldReferenceCorrectly() {
|
||||
|
||||
assertThat(
|
||||
new SetOperation("student", Fields.field("homework")).toPipelineStages(contextFor(ScoresWithMappedField.class)))
|
||||
@@ -78,7 +79,7 @@ public class SetOperationUnitTests {
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void rendersNestedMappedTargetValueFieldReferenceCorrectly() {
|
||||
void rendersNestedMappedTargetValueFieldReferenceCorrectly() {
|
||||
|
||||
assertThat(new SetOperation("scoresWithMappedField.student", Fields.field("scoresWithMappedField.homework"))
|
||||
.toPipelineStages(contextFor(ScoresWrapper.class)))
|
||||
@@ -86,8 +87,18 @@ public class SetOperationUnitTests {
|
||||
.parse("{\"$set\" : {\"scoresWithMappedField.student_name\":\"$scoresWithMappedField.home_work\"}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2363
|
||||
void appliesSpelExpressionCorrectly() {
|
||||
|
||||
SetOperation operation = SetOperation.builder().set("totalHomework").withValueOfExpression("sum(homework) * [0]",
|
||||
2);
|
||||
|
||||
assertThat(operation.toPipelineStages(contextFor(AddFieldsOperationUnitTests.ScoresWrapper.class))).contains(
|
||||
Document.parse("{\"$set\" : {\"totalHomework\": { $multiply : [{ \"$sum\" : [\"$homework\"] }, 2] }}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void rendersTargetValueExpressionCorrectly() {
|
||||
void rendersTargetValueExpressionCorrectly() {
|
||||
|
||||
assertThat(SetOperation.builder().set("totalHomework").toValueOf(ArithmeticOperators.valueOf("homework").sum())
|
||||
.toPipelineStages(contextFor(Scores.class)))
|
||||
@@ -95,7 +106,7 @@ public class SetOperationUnitTests {
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2331
|
||||
public void exposesFieldsCorrectly() {
|
||||
void exposesFieldsCorrectly() {
|
||||
|
||||
ExposedFields fields = SetOperation.builder().set("totalHomework").toValue("A+") //
|
||||
.and() //
|
||||
@@ -138,5 +149,4 @@ public class SetOperationUnitTests {
|
||||
Scores scores;
|
||||
ScoresWithMappedField scoresWithMappedField;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user