Skip to content

DATAMONGO-1491 - Add support for $filter (aggregation). #412

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 2 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-1491-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-1491-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-1491-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-1491-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-1491-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-1491-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
import org.springframework.data.mongodb.core.query.Criteria;
Expand Down Expand Up @@ -557,7 +558,7 @@ public DBObject getMappedObject(DBObject dbObject) {
*/
@Override
public FieldReference getReference(Field field) {
return new FieldReference(new ExposedField(field, true));
return new DirectFieldReference(new ExposedField(field, true));
}

/*
Expand All @@ -566,7 +567,7 @@ public FieldReference getReference(Field field) {
*/
@Override
public FieldReference getReference(String name) {
return new FieldReference(new ExposedField(new AggregationField(name), true));
return new DirectFieldReference(new ExposedField(new AggregationField(name), true));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
/*
* Copyright 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.
* 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.ArrayList;
import java.util.List;

import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.util.Assert;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;

/**
* @author Christoph Strobl
* @since 1.10
*/
public interface AggregationExpressions {

/**
* {@code $filter} {@link AggregationExpression} allows to select a subset of the array to return based on the
* specified condition.
*
* @author Christoph Strobl
* @since 1.10
*/
class Filter implements AggregationExpression {

private Object input;
private ExposedField as;
private Object condition;

private Filter() {
// used by builder
}

/**
* Set the {@literal field} to apply the {@code $filter} to.
*
* @param field must not be {@literal null}.
* @return never {@literal null}.
*/
public static AsBuilder filter(String field) {

Assert.notNull(field, "Field must not be null!");
return filter(Fields.field(field));
}

/**
* Set the {@literal field} to apply the {@code $filter} to.
*
* @param field must not be {@literal null}.
* @return never {@literal null}.
*/
public static AsBuilder filter(Field field) {

Assert.notNull(field, "Field must not be null!");
return new FilterExpressionBuilder().filter(field);
}

/**
* Set the {@literal values} to apply the {@code $filter} to.
*
* @param values must not be {@literal null}.
* @return
*/
public static AsBuilder filter(List<?> values) {

Assert.notNull(values, "Values must not be null!");
return new FilterExpressionBuilder().filter(values);
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDbObject(final AggregationOperationContext context) {

return toFilter(new ExposedFieldsAggregationOperationContext(ExposedFields.from(as), context) {

@Override
public FieldReference getReference(Field field) {

FieldReference ref = null;
try {
ref = context.getReference(field);
} catch (Exception e) {
// just ignore that one.
}
return ref != null ? ref : super.getReference(field);
}
});
}

private DBObject toFilter(AggregationOperationContext context) {

DBObject filterExpression = new BasicDBObject();

filterExpression.putAll(context.getMappedObject(new BasicDBObject("input", getMappedInput(context))));
filterExpression.put("as", as.getTarget());

filterExpression.putAll(context.getMappedObject(new BasicDBObject("cond", getMappedCondition(context))));

return new BasicDBObject("$filter", filterExpression);
}

private Object getMappedInput(AggregationOperationContext context) {
return input instanceof Field ? context.getReference((Field) input).toString() : input;
}

private Object getMappedCondition(AggregationOperationContext context) {

if (!(condition instanceof AggregationExpression)) {
return condition;
}

NestedDelegatingExpressionAggregationOperationContext nea = new NestedDelegatingExpressionAggregationOperationContext(context);
DBObject mappedCondition = ((AggregationExpression) condition).toDbObject(nea);
return mappedCondition;
}

/**
* @author Christoph Strobl
*/
public interface InputBuilder {

/**
* Set the {@literal values} to apply the {@code $filter} to.
*
* @param array must not be {@literal null}.
* @return
*/
AsBuilder filter(List<?> array);

/**
* Set the {@literal field} holding an array to apply the {@code $filter} to.
*
* @param field must not be {@literal null}.
* @return
*/
AsBuilder filter(Field field);
}

/**
* @author Christoph Strobl
*/
public interface AsBuilder {

/**
* Set the {@literal variableName} for the elements in the input array.
*
* @param variableName must not be {@literal null}.
* @return
*/
ConditionBuilder as(String variableName);
}

/**
* @author Christoph Strobl
*/
public interface ConditionBuilder {

/**
* Set the {@link AggregationExpression} that determines whether to include the element in the resulting array.
*
* @param expression must not be {@literal null}.
* @return
*/
Filter by(AggregationExpression expression);

/**
* Set the {@literal expression} that determines whether to include the element in the resulting array.
*
* @param expression must not be {@literal null}.
* @return
*/
Filter by(String expression);

/**
* Set the {@literal expression} that determines whether to include the element in the resulting array.
*
* @param expression must not be {@literal null}.
* @return
*/
Filter by(DBObject expression);
}

/**
* @author Christoph Strobl
*/
static final class FilterExpressionBuilder implements InputBuilder, AsBuilder, ConditionBuilder {

private final Filter filter;

FilterExpressionBuilder() {
this.filter = new Filter();
}

public static InputBuilder newBuilder() {
return new FilterExpressionBuilder();
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(java.util.List)
*/
@Override
public AsBuilder filter(List<?> array) {

Assert.notNull(array, "Array must not be null!");
filter.input = new ArrayList(array);
return this;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(org.springframework.data.mongodb.core.aggregation.Field)
*/
@Override
public AsBuilder filter(Field field) {

Assert.notNull(field, "Field must not be null!");
filter.input = field;
return this;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.AsBuilder#as(java.lang.String)
*/
@Override
public ConditionBuilder as(String variableName) {

Assert.notNull(variableName, "Variable name must not be null!");
filter.as = new ExposedField(variableName, true);
return this;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
*/
@Override
public Filter by(AggregationExpression condition) {

Assert.notNull(condition, "Condition must not be null!");
filter.condition = condition;
return filter;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(java.lang.String)
*/
@Override
public Filter by(String expression) {

Assert.notNull(expression, "Expression must not be null!");
filter.condition = expression;
return filter;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(com.mongodb.DBObject)
*/
@Override
public Filter by(DBObject expression) {

Assert.notNull(expression, "Expression must not be null!");
filter.condition = expression;
return filter;
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
*/
public enum AggregationFunctionExpressions {

SIZE;
SIZE, GTE;

/**
* Returns an {@link AggregationExpression} build from the current {@link Enum} name and the given parameters.
Expand Down
Loading