DATAMONGO-2312 - Add support for array projections.
Original pull request: #770.
This commit is contained in:
committed by
Mark Paluch
parent
7c25675cbb
commit
c2436bcdfc
@@ -20,6 +20,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
@@ -177,6 +178,48 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return new ProjectionOperation(this.projections, FieldProjection.from(fields, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the current {@link ProjectionOperation} as an array with given name. <br />
|
||||
* If you want to specify array values directly use {@link #andArrayOf(Object...)}.
|
||||
*
|
||||
* @param name the target property name.
|
||||
* @return new instance of {@link ProjectionOperation}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public ProjectionOperation asArray(String name) {
|
||||
|
||||
return new ProjectionOperation(Collections.emptyList(),
|
||||
Collections.singletonList(new ArrayProjection(Fields.field(name), (List) this.projections)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the given values ({@link Field field references}, {@link AggregationExpression expression}, plain values)
|
||||
* as an array. <br />
|
||||
* The target property name needs to be set via {@link ArrayProjectionOperationBuilder#as(String)}.
|
||||
*
|
||||
* @param values must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayProjectionOperationBuilder}.
|
||||
* @throws IllegalArgumentException if the required argument it {@literal null}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public ArrayProjectionOperationBuilder andArrayOf(Object... values) {
|
||||
|
||||
ArrayProjectionOperationBuilder builder = new ArrayProjectionOperationBuilder(this);
|
||||
|
||||
for (Object value : values) {
|
||||
|
||||
if (value instanceof Field) {
|
||||
builder.and((Field) value);
|
||||
} else if (value instanceof AggregationExpression) {
|
||||
builder.and((AggregationExpression) value);
|
||||
} else {
|
||||
builder.and(value);
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
@@ -1743,4 +1786,95 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return context.getMappedObject(projections, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.2
|
||||
*/
|
||||
public static class ArrayProjectionOperationBuilder {
|
||||
|
||||
private ProjectionOperation target;
|
||||
private final List<Object> projections;
|
||||
|
||||
public ArrayProjectionOperationBuilder(ProjectionOperation target) {
|
||||
|
||||
this.target = target;
|
||||
this.projections = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ArrayProjectionOperationBuilder and(AggregationExpression expression) {
|
||||
|
||||
this.projections.add(expression);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ArrayProjectionOperationBuilder and(Field field) {
|
||||
|
||||
this.projections.add(field);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ArrayProjectionOperationBuilder and(Object value) {
|
||||
|
||||
this.projections.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link ProjectionOperation} for the array property with given {@literal name}.
|
||||
*
|
||||
* @param name The target property name. Must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayProjectionOperationBuilder}.
|
||||
*/
|
||||
public ProjectionOperation as(String name) {
|
||||
|
||||
return new ProjectionOperation(target.projections,
|
||||
Collections.singletonList(new ArrayProjection(Fields.field(name), this.projections)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.2
|
||||
*/
|
||||
static class ArrayProjection extends Projection {
|
||||
|
||||
private final Field targetField;
|
||||
private final List<Object> projections;
|
||||
|
||||
public ArrayProjection(Field targetField, List<Object> projections) {
|
||||
|
||||
super(targetField);
|
||||
this.targetField = targetField;
|
||||
this.projections = projections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Document toDocument(AggregationOperationContext context) {
|
||||
|
||||
return new Document(targetField.getName(),
|
||||
projections.stream().map(it -> toArrayEntry(it, context)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private Object toArrayEntry(Object projection, AggregationOperationContext ctx) {
|
||||
|
||||
if (projection instanceof Field) {
|
||||
return ctx.getReference((Field) projection).toString();
|
||||
}
|
||||
|
||||
if (projection instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) projection).toDocument(ctx);
|
||||
}
|
||||
|
||||
if (projection instanceof FieldProjection) {
|
||||
return ctx.getReference(((FieldProjection) projection).getExposedField().getTarget()).toString();
|
||||
}
|
||||
|
||||
if (projection instanceof Projection) {
|
||||
((Projection) projection).toDocument(ctx);
|
||||
}
|
||||
|
||||
return projection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.domain.Range.Bound;
|
||||
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||
@@ -2157,6 +2158,67 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(projectClause).isEmpty();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2312
|
||||
public void simpleFieldReferenceAsArray() {
|
||||
|
||||
org.bson.Document doc = Aggregation.newAggregation(project("x", "y", "someField").asArray("myArray"))
|
||||
.toDocument("coll", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(doc).isEqualTo(Document.parse(
|
||||
"{\"aggregate\":\"coll\", \"pipeline\":[ { $project: { myArray: [ \"$x\", \"$y\", \"$someField\" ] } } ] }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2312
|
||||
public void mappedFieldReferenceAsArray() {
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
|
||||
org.bson.Document doc = Aggregation
|
||||
.newAggregation(BookWithFieldAnnotation.class, project("title", "author").asArray("myArray"))
|
||||
.toDocument("coll", new TypeBasedAggregationOperationContext(BookWithFieldAnnotation.class, mappingContext,
|
||||
new QueryMapper(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext))));
|
||||
|
||||
assertThat(doc).isEqualTo(Document
|
||||
.parse("{\"aggregate\":\"coll\", \"pipeline\":[ { $project: { myArray: [ \"$ti_t_le\", \"$author\" ] } } ] }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2312
|
||||
public void arrayWithNullValue() {
|
||||
|
||||
Document doc = project() //
|
||||
.andArrayOf(Fields.field("field-1"), null, "value").as("myArray") //
|
||||
.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(doc).isEqualTo(Document.parse("{ $project: { \"myArray\" : [ \"$field-1\", null, \"value\" ] } }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2312
|
||||
public void nestedArrayField() {
|
||||
|
||||
Document doc = project("_id", "value") //
|
||||
.andArrayOf(Fields.field("field-1"), "plain - string", ArithmeticOperators.valueOf("field-1").sum().and(10))
|
||||
.as("myArray") //
|
||||
.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(doc).isEqualTo(Document.parse(
|
||||
"{ $project: { \"_id\" : 1, \"value\" : 1, \"myArray\" : [ \"$field-1\", \"plain - string\", { \"$sum\" : [\"$field-1\", 10] } ] } } ] }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2312
|
||||
public void nestedMappedFieldReferenceInArrayField() {
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
|
||||
Document doc = project("author") //
|
||||
.andArrayOf(Fields.field("title"), "plain - string", ArithmeticOperators.valueOf("title").sum().and(10))
|
||||
.as("myArray") //
|
||||
.toDocument(new TypeBasedAggregationOperationContext(BookWithFieldAnnotation.class, mappingContext,
|
||||
new QueryMapper(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext))));
|
||||
|
||||
assertThat(doc).isEqualTo(Document.parse(
|
||||
"{ $project: { \"author\" : 1, \"myArray\" : [ \"$ti_t_le\", \"plain - string\", { \"$sum\" : [\"$ti_t_le\", 10] } ] } } ] }"));
|
||||
}
|
||||
|
||||
private static Document exctractOperation(String field, Document fromProjectClause) {
|
||||
return (Document) fromProjectClause.get(field);
|
||||
}
|
||||
@@ -2167,6 +2229,13 @@ public class ProjectionOperationUnitTests {
|
||||
Author author;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class BookWithFieldAnnotation {
|
||||
|
||||
@Field("ti_t_le") String title;
|
||||
Author author;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class BookRenamed {
|
||||
@Field("ti_tl_e") String title;
|
||||
|
||||
Reference in New Issue
Block a user