Skip to content

DATAMONGO-1530 - Add support for missing aggregation operators in SpEL based support. #413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.10.0.BUILD-SNAPSHOT</version>
<version>1.10.0.DATAMONGO-1530-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data MongoDB</name>
Expand Down
4 changes: 2 additions & 2 deletions spring-data-mongodb-cross-store/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.10.0.BUILD-SNAPSHOT</version>
<version>1.10.0.DATAMONGO-1530-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -48,7 +48,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.10.0.BUILD-SNAPSHOT</version>
<version>1.10.0.DATAMONGO-1530-SNAPSHOT</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.10.0.BUILD-SNAPSHOT</version>
<version>1.10.0.DATAMONGO-1530-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-log4j/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.10.0.BUILD-SNAPSHOT</version>
<version>1.10.0.DATAMONGO-1530-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.10.0.BUILD-SNAPSHOT</version>
<version>1.10.0.DATAMONGO-1530-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-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.
Expand All @@ -26,19 +26,26 @@
import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport;
import org.springframework.data.mongodb.core.spel.LiteralNode;
import org.springframework.data.mongodb.core.spel.MethodReferenceNode;
import org.springframework.data.mongodb.core.spel.MethodReferenceNode.AggregationMethodReference;
import org.springframework.data.mongodb.core.spel.MethodReferenceNode.AggregationMethodReference.ArgumentType;
import org.springframework.data.mongodb.core.spel.NotOperatorNode;
import org.springframework.data.mongodb.core.spel.OperatorNode;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.ast.CompoundExpression;
import org.springframework.expression.spel.ast.ConstructorReference;
import org.springframework.expression.spel.ast.Indexer;
import org.springframework.expression.spel.ast.InlineList;
import org.springframework.expression.spel.ast.InlineMap;
import org.springframework.expression.spel.ast.OperatorNot;
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
Expand All @@ -48,6 +55,7 @@
* Renders the AST of a SpEL expression as a MongoDB Aggregation Framework projection expression.
*
* @author Thomas Darimont
* @author Christoph Strobl
*/
class SpelExpressionTransformer implements AggregationExpressionTransformer {

Expand All @@ -69,6 +77,8 @@ public SpelExpressionTransformer() {
conversions.add(new PropertyOrFieldReferenceNodeConversion(this));
conversions.add(new CompoundExpressionNodeConversion(this));
conversions.add(new MethodReferenceNodeConversion(this));
conversions.add(new NotOperatorNodeConversion(this));
conversions.add(new ValueRetrievingNodeConversion(this));

this.conversions = Collections.unmodifiableList(conversions);
}
Expand Down Expand Up @@ -131,8 +141,8 @@ private ExpressionNodeConversion<ExpressionNode> lookupConversionFor(ExpressionN
* @author Thomas Darimont
* @author Oliver Gierke
*/
private static abstract class ExpressionNodeConversion<T extends ExpressionNode> implements
AggregationExpressionTransformer {
private static abstract class ExpressionNodeConversion<T extends ExpressionNode>
implements AggregationExpressionTransformer {

private final AggregationExpressionTransformer transformer;
private final Class<? extends ExpressionNode> nodeType;
Expand Down Expand Up @@ -235,8 +245,17 @@ public OperatorNodeConversion(AggregationExpressionTransformer transformer) {
protected Object convert(AggregationExpressionTransformationContext<OperatorNode> context) {

OperatorNode currentNode = context.getCurrentNode();

DBObject operationObject = createOperationObjectAndAddToPreviousArgumentsIfNecessary(context, currentNode);

if (currentNode.isLogicalOperator()) {

for (ExpressionNode expressionNode : currentNode) {
transform(expressionNode, currentNode, operationObject, context);
}

return operationObject;
}

Object leftResult = transform(currentNode.getLeft(), currentNode, operationObject, context);

if (currentNode.isUnaryMinus()) {
Expand Down Expand Up @@ -271,7 +290,8 @@ private DBObject createOperationObjectAndAddToPreviousArgumentsIfNecessary(
return nextDbObject;
}

private Object convertUnaryMinusOp(ExpressionTransformationContextSupport<OperatorNode> context, Object leftResult) {
private Object convertUnaryMinusOp(ExpressionTransformationContextSupport<OperatorNode> context,
Object leftResult) {

Object result = leftResult instanceof Number ? leftResult
: new BasicDBObject("$multiply", dbList(-1, leftResult));
Expand All @@ -289,7 +309,7 @@ private Object convertUnaryMinusOp(ExpressionTransformationContextSupport<Operat
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isMathematicalOperation();
return node.isMathematicalOperation() || node.isLogicalOperator();
}
}

Expand Down Expand Up @@ -462,13 +482,31 @@ public MethodReferenceNodeConversion(AggregationExpressionTransformer transforme
protected Object convert(AggregationExpressionTransformationContext<MethodReferenceNode> context) {

MethodReferenceNode node = context.getCurrentNode();
List<Object> args = new ArrayList<Object>();
AggregationMethodReference methodReference = node.getMethodReference();

for (ExpressionNode childNode : node) {
args.add(transform(childNode, context));
Object args = null;

if (ObjectUtils.nullSafeEquals(methodReference.getArgumentType(), ArgumentType.SINGLE)) {
args = transform(node.getChild(0), context);
} else if (ObjectUtils.nullSafeEquals(methodReference.getArgumentType(), ArgumentType.MAP)) {

DBObject dbo = new BasicDBObject();
for (int i = 0; i < methodReference.getArgumentMap().length; i++) {
dbo.put(methodReference.getArgumentMap()[i], transform(node.getChild(i), context));
}
args = dbo;
} else {

List<Object> argList = new ArrayList<Object>();

for (ExpressionNode childNode : node) {
argList.add(transform(childNode, context));
}

args = dbList(argList.toArray());
}

return context.addToPreviousOrReturn(new BasicDBObject(node.getMethodName(), dbList(args.toArray())));
return context.addToPreviousOrReturn(new BasicDBObject(methodReference.getMongoOperator(), args));
}
}

Expand Down Expand Up @@ -510,4 +548,81 @@ protected boolean supports(ExpressionNode node) {
return node.isOfType(CompoundExpression.class);
}
}

/**
* @author Christoph Strobl
* @since 1.10
*/
static class NotOperatorNodeConversion extends ExpressionNodeConversion<NotOperatorNode> {

/**
* Creates a new {@link ExpressionNodeConversion}.
*
* @param transformer must not be {@literal null}.
*/
public NotOperatorNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<NotOperatorNode> context) {

NotOperatorNode node = context.getCurrentNode();
List<Object> args = new ArrayList<Object>();

for (ExpressionNode childNode : node) {
args.add(transform(childNode, context));
}

return context.addToPreviousOrReturn(new BasicDBObject(node.getMongoOperator(), dbList(args.toArray())));
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isOfType(OperatorNot.class);
}
}

/**
* @author Christoph Strobl
* @since 1.10
*/
static class ValueRetrievingNodeConversion extends ExpressionNodeConversion<ExpressionNode> {

/**
* Creates a new {@link ExpressionNodeConversion}.
*
* @param transformer must not be {@literal null}.
*/
public ValueRetrievingNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
return context.getCurrentNode().getValue();
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isOfType(InlineMap.class) || node.isOfType(InlineList.class)
|| node.isOfType(ConstructorReference.class);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-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.
Expand All @@ -20,15 +20,20 @@

import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.ast.ConstructorReference;
import org.springframework.expression.spel.ast.InlineList;
import org.springframework.expression.spel.ast.InlineMap;
import org.springframework.expression.spel.ast.Literal;
import org.springframework.expression.spel.ast.MethodReference;
import org.springframework.expression.spel.ast.Operator;
import org.springframework.expression.spel.ast.OperatorNot;
import org.springframework.util.Assert;

/**
* A value object for nodes in an expression. Allows iterating ove potentially available child {@link ExpressionNode}s.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class ExpressionNode implements Iterable<ExpressionNode> {

Expand Down Expand Up @@ -79,6 +84,10 @@ public static ExpressionNode from(SpelNode node, ExpressionState state) {
return new LiteralNode((Literal) node, state);
}

if (node instanceof OperatorNot) {
return new NotOperatorNode((OperatorNot) node, state);
}

return new ExpressionNode(node, state);
}

Expand Down Expand Up @@ -122,6 +131,16 @@ public boolean isMathematicalOperation() {
return false;
}

/**
* Returns whether the {@link ExpressionNode} is a logical conjunction operation like {@code &&, ||}.
*
* @return
* @since 1.10
*/
public boolean isLogicalOperator() {
return false;
}

/**
* Returns whether the {@link ExpressionNode} is a literal.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-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.
Expand All @@ -15,7 +15,12 @@
*/
package org.springframework.data.mongodb.core.spel;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.ast.BooleanLiteral;
import org.springframework.expression.spel.ast.FloatLiteral;
import org.springframework.expression.spel.ast.IntLiteral;
import org.springframework.expression.spel.ast.Literal;
Expand All @@ -26,13 +31,29 @@

/**
* A node representing a literal in an expression.
*
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class LiteralNode extends ExpressionNode {

private static final Set<Class<?>> SUPPORTED_LITERAL_TYPES;
private final Literal literal;

static {

Set<Class<?>> supportedTypes = new HashSet<Class<?>>(7, 1);
supportedTypes.add(BooleanLiteral.class);
supportedTypes.add(FloatLiteral.class);
supportedTypes.add(IntLiteral.class);
supportedTypes.add(LongLiteral.class);
supportedTypes.add(NullLiteral.class);
supportedTypes.add(RealLiteral.class);
supportedTypes.add(StringLiteral.class);

SUPPORTED_LITERAL_TYPES = Collections.unmodifiableSet(supportedTypes);
}

/**
* Creates a new {@link LiteralNode} from the given {@link Literal} and {@link ExpressionState}.
*
Expand Down Expand Up @@ -66,7 +87,6 @@ public boolean isUnaryMinus(ExpressionNode parent) {
*/
@Override
public boolean isLiteral() {
return literal instanceof FloatLiteral || literal instanceof RealLiteral || literal instanceof IntLiteral
|| literal instanceof LongLiteral || literal instanceof StringLiteral || literal instanceof NullLiteral;
return SUPPORTED_LITERAL_TYPES.contains(literal.getClass());
}
}
Loading