Skip to content

Commit 7e471e2

Browse files
Thomas Darimontodrotbohm
Thomas Darimont
authored andcommitted
DATAMONGO-774 - Support SpEL expressions to define projection operations in the aggregation framework.
ProjectionOperations can now be built using SpEL expressions as this significantly shortens the code needed to express the project, especially for slightly more complex mathematical expressions. Projection now has an ….andExpression(…) method that takes a SpEL expression and optional arguments that can be referred to via their index, i.e. a SpEl expression "5 + [0]" can be expanded using ….andExpression("5 + [0]", 7).… so that the projection can be prepared and dynamically get values bound. Original pull request: spring-projects#81.
1 parent 0871a43 commit 7e471e2

21 files changed

+1385
-80
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java

+4
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,11 @@ public FieldReference(ExposedField field) {
340340
* @return
341341
*/
342342
public String getRaw() {
343+
343344
String target = field.getTarget();
345+
if (target.startsWith("$")) {
346+
target = target.substring(1);
347+
}
344348
return field.synthetic ? target : String.format("%s.%s", Fields.UNDERSCORE_ID, target);
345349
}
346350

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java

+75-6
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ public ProjectionOperationBuilder and(String name) {
116116
return new ProjectionOperationBuilder(name, this, null);
117117
}
118118

119+
public ExpressionProjectionOperationBuilder andExpression(String expression, Object... params) {
120+
return new ExpressionProjectionOperationBuilder(expression, this, params);
121+
}
122+
119123
/**
120124
* Excludes the given fields from the projection.
121125
*
@@ -188,13 +192,80 @@ public DBObject toDBObject(AggregationOperationContext context) {
188192
return new BasicDBObject("$project", fieldObject);
189193
}
190194

195+
/**
196+
* @author Thomas Darimont
197+
*/
198+
public static abstract class AbstractProjectionOperationBuilder implements AggregationOperation {
199+
200+
protected final Object value;
201+
protected final ProjectionOperation operation;
202+
203+
public AbstractProjectionOperationBuilder(Object value, ProjectionOperation operation) {
204+
205+
Assert.notNull(value, "value must not be null or empty!");
206+
Assert.notNull(operation, "ProjectionOperation must not be null!");
207+
208+
this.value = value;
209+
this.operation = operation;
210+
}
211+
212+
public abstract ProjectionOperation as(String alias);
213+
214+
/* (non-Javadoc)
215+
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
216+
*/
217+
@Override
218+
public DBObject toDBObject(AggregationOperationContext context) {
219+
return this.operation.toDBObject(context);
220+
}
221+
}
222+
223+
/**
224+
* @author Thomas Darimont
225+
*/
226+
public static class ExpressionProjectionOperationBuilder extends AbstractProjectionOperationBuilder {
227+
228+
private Object[] params;
229+
230+
public ExpressionProjectionOperationBuilder(Object value, ProjectionOperation operation, Object[] params) {
231+
super(value, operation);
232+
this.params = params;
233+
}
234+
235+
public ProjectionOperation as(String alias) {
236+
237+
return this.operation.and(new ExpressionProjection(Fields.field(alias, "expr"), this.value.toString(), params));
238+
}
239+
240+
static class ExpressionProjection extends Projection {
241+
242+
private String expression;
243+
private Object[] params;
244+
245+
public ExpressionProjection(Field field, String expression, Object[] params) {
246+
super(field);
247+
this.expression = expression;
248+
this.params = params;
249+
}
250+
251+
/* (non-Javadoc)
252+
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
253+
*/
254+
@Override
255+
public DBObject toDBObject(AggregationOperationContext context) {
256+
return new BasicDBObject(getExposedField().getName(),
257+
SpelExpressionToMongoExpressionTransformer.INSTANCE.transform(expression, context, params));
258+
}
259+
}
260+
}
261+
191262
/**
192263
* Builder for {@link ProjectionOperation}s on a field.
193264
*
194265
* @author Oliver Gierke
195266
* @author Thomas Darimont
196267
*/
197-
public static class ProjectionOperationBuilder implements AggregationOperation {
268+
public static class ProjectionOperationBuilder extends AbstractProjectionOperationBuilder {
198269

199270
private final String name;
200271
private final ProjectionOperation operation;
@@ -209,9 +280,7 @@ public static class ProjectionOperationBuilder implements AggregationOperation {
209280
* @param previousProjection the previous operation projection, may be {@literal null}.
210281
*/
211282
public ProjectionOperationBuilder(String name, ProjectionOperation operation, OperationProjection previousProjection) {
212-
213-
Assert.hasText(name, "Field name must not be null or empty!");
214-
Assert.notNull(operation, "ProjectionOperation must not be null!");
283+
super(name, operation);
215284

216285
this.name = name;
217286
this.operation = operation;
@@ -248,8 +317,8 @@ public ProjectionOperation nested(Fields fields) {
248317
*/
249318
public ProjectionOperation as(String alias) {
250319

251-
if (previousProjection != null) {
252-
return this.operation.andReplaceLastOneWith(previousProjection.withAlias(alias));
320+
if (this.previousProjection != null) {
321+
return this.operation.andReplaceLastOneWith(this.previousProjection.withAlias(alias));
253322
} else {
254323
return this.operation.and(new FieldProjection(Fields.field(alias, name), null));
255324
}

0 commit comments

Comments
 (0)