Skip to content

DATAMONGO-1391 - Support Mongo 3.2 syntax for $unwind in aggregation. #355

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.9.0.BUILD-SNAPSHOT</version>
<version>1.9.0.DATAMONGO-1391-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.9.0.BUILD-SNAPSHOT</version>
<version>1.9.0.DATAMONGO-1391-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.9.0.BUILD-SNAPSHOT</version>
<version>1.9.0.DATAMONGO-1391-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.9.0.BUILD-SNAPSHOT</version>
<version>1.9.0.DATAMONGO-1391-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.9.0.BUILD-SNAPSHOT</version>
<version>1.9.0.DATAMONGO-1391-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.9.0.BUILD-SNAPSHOT</version>
<version>1.9.0.DATAMONGO-1391-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,56 @@ public static ProjectionOperation project(Fields fields) {
/**
* Factory method to create a new {@link UnwindOperation} for the field with the given name.
*
* @param fieldName must not be {@literal null} or empty.
* @param field must not be {@literal null} or empty.
* @return
*/
public static UnwindOperation unwind(String field) {
return new UnwindOperation(field(field));
}

/**
* Factory method to create a new {@link UnwindOperation} for the field with the given name and
* {@code preserveNullAndEmptyArrays}. Note that extended unwind is supported in MongoDB version 3.2+.
*
* @param field must not be {@literal null} or empty.
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
* array is empty.
* @return
* @since 1.10
*/
public static UnwindOperation unwind(String field, boolean preserveNullAndEmptyArrays) {
return new UnwindOperation(field(field), preserveNullAndEmptyArrays);
}

/**
* Factory method to create a new {@link UnwindOperation} for the field with the given name and to include the index
* field as {@code arrayIndex}. Note that extended unwind is supported in MongoDB version 3.2+.
*
* @param field must not be {@literal null} or empty.
* @param arrayIndex must not be {@literal null} or empty.
* @return
* @since 1.10
*/
public static UnwindOperation unwind(String field, String arrayIndex) {
return new UnwindOperation(field(field), field(arrayIndex), false);
}

/**
* Factory method to create a new {@link UnwindOperation} for the field with the given name, to include the index
* field as {@code arrayIndex} and {@code preserveNullAndEmptyArrays}. Note that extended unwind is supported in
* MongoDB version 3.2+.
*
* @param field must not be {@literal null} or empty.
* @param arrayIndex must not be {@literal null} or empty.
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
* array is empty.
* @return
* @since 1.10
*/
public static UnwindOperation unwind(String field, String arrayIndex, boolean preserveNullAndEmptyArrays) {
return new UnwindOperation(field(field), field(arrayIndex), preserveNullAndEmptyArrays);
}

/**
* Creates a new {@link GroupOperation} for the given fields.
*
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 Down Expand Up @@ -30,6 +30,7 @@
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
* @since 1.3
*/
public final class ExposedFields implements Iterable<ExposedField> {
Expand Down Expand Up @@ -203,8 +204,13 @@ private int exposedFieldsCount() {
public Iterator<ExposedField> iterator() {

CompositeIterator<ExposedField> iterator = new CompositeIterator<ExposedField>();
iterator.add(syntheticFields.iterator());
iterator.add(originalFields.iterator());
if (!syntheticFields.isEmpty()) {
iterator.add(syntheticFields.iterator());
}

if (!originalFields.isEmpty()) {
iterator.add(originalFields.iterator());
}

return iterator;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 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 @@ -19,6 +19,7 @@
import org.springframework.util.Assert;

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

/**
Expand All @@ -30,29 +31,207 @@
* @see http://docs.mongodb.org/manual/reference/aggregation/unwind/#pipe._S_unwind
* @author Thomas Darimont
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.3
*/
public class UnwindOperation implements AggregationOperation {
public class UnwindOperation
implements AggregationOperation, FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation {

private final ExposedField field;
private final ExposedField arrayIndex;
private final boolean preserveNullAndEmptyArrays;

/**
* Creates a new {@link UnwindOperation} for the given {@link Field}.
*
* @param field must not be {@literal null}.
*/
public UnwindOperation(Field field) {
this(new ExposedField(field, true), false);
}

/**
* Creates a new {@link UnwindOperation} using Mongo 3.2 syntax.
*
* @param field must not be {@literal null}.
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
* array is empty.
* @since 1.10
*/
public UnwindOperation(Field field, boolean preserveNullAndEmptyArrays) {
Assert.notNull(field, "Field must not be null!");

this.field = new ExposedField(field, true);
this.arrayIndex = null;
this.preserveNullAndEmptyArrays = preserveNullAndEmptyArrays;
}

/**
* Creates a new {@link UnwindOperation} using Mongo 3.2 syntax.
*
* @param field must not be {@literal null}.
* @param arrayIndex optional field name to expose the field array index, must not be {@literal null}.
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
* array is empty.
* @since 1.10
*/
public UnwindOperation(Field field, Field arrayIndex, boolean preserveNullAndEmptyArrays) {

Assert.notNull(field, "Field must not be null!");
Assert.notNull(arrayIndex, "ArrayIndex must not be null!");

Assert.notNull(field);
this.field = new ExposedField(field, true);
this.arrayIndex = new ExposedField(arrayIndex, true);
this.preserveNullAndEmptyArrays = preserveNullAndEmptyArrays;
}

/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$unwind", context.getReference(field).toString());

String unwindField = context.getReference(field).toString();
Object unwindArg;

if (preserveNullAndEmptyArrays || arrayIndex != null) {

BasicDBObjectBuilder builder = BasicDBObjectBuilder.start().add("path", unwindField);
builder.add("preserveNullAndEmptyArrays", preserveNullAndEmptyArrays);

if (arrayIndex != null) {
builder.add("includeArrayIndex", arrayIndex.getName());
}

unwindArg = builder.get();
} else {
unwindArg = unwindField;
}

return new BasicDBObject("$unwind", unwindArg);
}

@Override
public ExposedFields getFields() {
return arrayIndex != null ? ExposedFields.from(arrayIndex) : ExposedFields.from();
}

/**
* Get a builder that allows creation of {@link LookupOperation}.
*
* @return
*/
public static PathBuilder newUnwind() {
return UnwindOperationBuilder.newBuilder();
}

public static interface PathBuilder {

/**
* @param path the path to unwind, must not be {@literal null} or empty.
* @return
*/
IndexBuilder path(String path);
}

public static interface IndexBuilder {

/**
* Exposes the array index as {@code field}.
*
* @param field field name to expose the field array index, must not be {@literal null} or empty.
* @return
*/
EmptyArraysBuilder arrayIndex(String field);

/**
* Do not expose the array index.
*
* @return
*/
EmptyArraysBuilder noArrayIndex();
}

public static interface EmptyArraysBuilder {

/**
* Output documents if the array is null or empty.
*
* @return
*/
UnwindOperation preserveNullAndEmptyArrays();

/**
* Do not output documents if the array is null or empty.
*
* @return
*/
UnwindOperation skipNullAndEmptyArrays();
}

/**
* Builder for fluent {@link UnwindOperation} creation.
*
* @author Mark Paluch
* @since 1.10
*/
public static final class UnwindOperationBuilder implements PathBuilder, IndexBuilder, EmptyArraysBuilder {

private Field field;
private Field arrayIndex;

private UnwindOperationBuilder() {}

/**
* Creates new builder for {@link UnwindOperation}.
*
* @return never {@literal null}.
*/
public static PathBuilder newBuilder() {
return new UnwindOperationBuilder();
}

@Override
public UnwindOperation preserveNullAndEmptyArrays() {

if (arrayIndex != null) {
return new UnwindOperation(field, arrayIndex, true);
}

return new UnwindOperation(field, true);
}

@Override
public UnwindOperation skipNullAndEmptyArrays() {

if (arrayIndex != null) {
return new UnwindOperation(field, arrayIndex, false);
}

return new UnwindOperation(field, false);
}

@Override
public EmptyArraysBuilder arrayIndex(String field) {
Assert.hasText(field, "'ArrayIndex' must not be null or empty!");
arrayIndex = Fields.field(field);
return this;
}

@Override
public EmptyArraysBuilder noArrayIndex() {
arrayIndex = null;
return this;
}

@Override
public UnwindOperationBuilder path(String path) {
Assert.hasText(path, "'Path' must not be null or empty!");
field = Fields.field(path);
return this;
}

}

}
Loading