/* * Copyright 2013-2017 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 static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; import static org.springframework.data.mongodb.core.aggregation.AggregationFunctionExpressions.*; import static org.springframework.data.mongodb.core.aggregation.Fields.*; import static org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable.*; import static org.springframework.data.mongodb.test.util.IsBsonObject.*; import java.util.Arrays; import java.util.List; import org.bson.Document; import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.data.domain.Range; import org.springframework.data.mongodb.core.DocumentTestUtils; import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.PropertyExpression; import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.Variable; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder; import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; /** * Unit tests for {@link ProjectionOperation}. * * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl * @author Mark Paluch */ public class ProjectionOperationUnitTests { static final String MOD = "$mod"; static final String ADD = "$add"; static final String SUBTRACT = "$subtract"; static final String MULTIPLY = "$multiply"; static final String DIVIDE = "$divide"; static final String PROJECT = "$project"; @Test(expected = IllegalArgumentException.class) // DATAMONGO-586 public void rejectsNullFields() { new ProjectionOperation(null); } @Test // DATAMONGO-586 public void declaresBackReferenceCorrectly() { ProjectionOperation operation = new ProjectionOperation(); operation = operation.and("prop").previousOperation(); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); assertThat(projectClause.get("prop"), is((Object) Fields.UNDERSCORE_ID_REF)); } @Test // DATAMONGO-586 public void alwaysUsesExplicitReference() { ProjectionOperation operation = new ProjectionOperation(Fields.fields("foo").and("bar", "foobar")); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); assertThat(projectClause.get("foo"), is((Object) 1)); assertThat(projectClause.get("bar"), is((Object) "$foobar")); } @Test // DATAMONGO-586 public void aliasesSimpleFieldProjection() { ProjectionOperation operation = new ProjectionOperation(); Document document = operation.and("foo").as("bar").toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); assertThat(projectClause.get("bar"), is((Object) "$foo")); } @Test // DATAMONGO-586 public void aliasesArithmeticProjection() { ProjectionOperation operation = new ProjectionOperation(); Document document = operation.and("foo").plus(41).as("bar").toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); Document barClause = DocumentTestUtils.getAsDocument(projectClause, "bar"); List<Object> addClause = (List<Object>) barClause.get("$add"); assertThat(addClause, hasSize(2)); assertThat(addClause.get(0), is((Object) "$foo")); assertThat(addClause.get(1), is((Object) 41)); } @Test // DATAMONGO-586 public void arithmenticProjectionOperationWithoutAlias() { String fieldName = "a"; ProjectionOperationBuilder operation = new ProjectionOperation().and(fieldName).plus(1); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); Document oper = exctractOperation(fieldName, projectClause); assertThat(oper.containsKey(ADD), is(true)); assertThat(oper.get(ADD), is((Object) Arrays.<Object> asList("$a", 1))); } @Test // DATAMONGO-586 public void arithmenticProjectionOperationPlus() { String fieldName = "a"; String fieldAlias = "b"; ProjectionOperation operation = new ProjectionOperation().and(fieldName).plus(1).as(fieldAlias); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); Document oper = exctractOperation(fieldAlias, projectClause); assertThat(oper.containsKey(ADD), is(true)); assertThat(oper.get(ADD), is((Object) Arrays.<Object> asList("$a", 1))); } @Test // DATAMONGO-586 public void arithmenticProjectionOperationMinus() { String fieldName = "a"; String fieldAlias = "b"; ProjectionOperation operation = new ProjectionOperation().and(fieldName).minus(1).as(fieldAlias); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); Document oper = exctractOperation(fieldAlias, projectClause); assertThat(oper.containsKey(SUBTRACT), is(true)); assertThat(oper.get(SUBTRACT), is((Object) Arrays.<Object> asList("$a", 1))); } @Test // DATAMONGO-586 public void arithmenticProjectionOperationMultiply() { String fieldName = "a"; String fieldAlias = "b"; ProjectionOperation operation = new ProjectionOperation().and(fieldName).multiply(1).as(fieldAlias); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); Document oper = exctractOperation(fieldAlias, projectClause); assertThat(oper.containsKey(MULTIPLY), is(true)); assertThat(oper.get(MULTIPLY), is((Object) Arrays.<Object> asList("$a", 1))); } @Test // DATAMONGO-586 public void arithmenticProjectionOperationDivide() { String fieldName = "a"; String fieldAlias = "b"; ProjectionOperation operation = new ProjectionOperation().and(fieldName).divide(1).as(fieldAlias); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); Document oper = exctractOperation(fieldAlias, projectClause); assertThat(oper.containsKey(DIVIDE), is(true)); assertThat(oper.get(DIVIDE), is((Object) Arrays.<Object> asList("$a", 1))); } @Test(expected = IllegalArgumentException.class) // DATAMONGO-586 public void arithmenticProjectionOperationDivideByZeroException() { new ProjectionOperation().and("a").divide(0); } @Test // DATAMONGO-586 public void arithmenticProjectionOperationMod() { String fieldName = "a"; String fieldAlias = "b"; ProjectionOperation operation = new ProjectionOperation().and(fieldName).mod(3).as(fieldAlias); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); Document oper = exctractOperation(fieldAlias, projectClause); assertThat(oper.containsKey(MOD), is(true)); assertThat(oper.get(MOD), is((Object) Arrays.<Object> asList("$a", 3))); } @Test(expected = IllegalArgumentException.class) // DATAMONGO-758 public void excludeShouldThrowExceptionForFieldsOtherThanUnderscoreId() { new ProjectionOperation().andExclude("foo"); } @Test // DATAMONGO-758 public void excludeShouldAllowExclusionOfUnderscoreId() { ProjectionOperation projectionOp = new ProjectionOperation().andExclude(Fields.UNDERSCORE_ID); Document document = projectionOp.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); assertThat((Integer) projectClause.get(Fields.UNDERSCORE_ID), is(0)); } @Test // DATAMONGO-757 public void usesImplictAndExplicitFieldAliasAndIncludeExclude() { ProjectionOperation operation = Aggregation.project("foo").and("foobar").as("bar").andInclude("inc1", "inc2") .andExclude("_id"); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); assertThat(projectClause.get("foo"), is((Object) 1)); // implicit assertThat(projectClause.get("bar"), is((Object) "$foobar")); // explicit assertThat(projectClause.get("inc1"), is((Object) 1)); // include shortcut assertThat(projectClause.get("inc2"), is((Object) 1)); assertThat(projectClause.get("_id"), is((Object) 0)); } @Test(expected = IllegalArgumentException.class) public void arithmenticProjectionOperationModByZeroException() { new ProjectionOperation().and("a").mod(0); } @Test // DATAMONGO-769 public void allowArithmeticOperationsWithFieldReferences() { ProjectionOperation operation = Aggregation.project() // .and("foo").plus("bar").as("fooPlusBar") // .and("foo").minus("bar").as("fooMinusBar") // .and("foo").multiply("bar").as("fooMultiplyBar") // .and("foo").divide("bar").as("fooDivideBar") // .and("foo").mod("bar").as("fooModBar"); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); assertThat((Document) projectClause.get("fooPlusBar"), // is(new Document("$add", Arrays.asList("$foo", "$bar")))); assertThat((Document) projectClause.get("fooMinusBar"), // is(new Document("$subtract", Arrays.asList("$foo", "$bar")))); assertThat((Document) projectClause.get("fooMultiplyBar"), // is(new Document("$multiply", Arrays.asList("$foo", "$bar")))); assertThat((Document) projectClause.get("fooDivideBar"), // is(new Document("$divide", Arrays.asList("$foo", "$bar")))); assertThat((Document) projectClause.get("fooModBar"), // is(new Document("$mod", Arrays.asList("$foo", "$bar")))); } @Test // DATAMONGO-774 public void projectionExpressions() { ProjectionOperation operation = Aggregation.project() // .andExpression("(netPrice + surCharge) * taxrate * [0]", 2).as("grossSalesPrice") // .and("foo").as("bar"); // Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(document, is(Document.parse( "{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}"))); } @Test // DATAMONGO-975 public void shouldRenderDateTimeFragmentExtractionsForSimpleFieldProjectionsCorrectly() { ProjectionOperation operation = Aggregation.project() // .and("date").extractHour().as("hour") // .and("date").extractMinute().as("min") // .and("date").extractSecond().as("second") // .and("date").extractMillisecond().as("millis") // .and("date").extractYear().as("year") // .and("date").extractMonth().as("month") // .and("date").extractWeek().as("week") // .and("date").extractDayOfYear().as("dayOfYear") // .and("date").extractDayOfMonth().as("dayOfMonth") // .and("date").extractDayOfWeek().as("dayOfWeek") // ; Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(document, is(notNullValue())); Document projected = exctractOperation("$project", document); assertThat(projected.get("hour"), is((Object) new Document("$hour", Arrays.asList("$date")))); assertThat(projected.get("min"), is((Object) new Document("$minute", Arrays.asList("$date")))); assertThat(projected.get("second"), is((Object) new Document("$second", Arrays.asList("$date")))); assertThat(projected.get("millis"), is((Object) new Document("$millisecond", Arrays.asList("$date")))); assertThat(projected.get("year"), is((Object) new Document("$year", Arrays.asList("$date")))); assertThat(projected.get("month"), is((Object) new Document("$month", Arrays.asList("$date")))); assertThat(projected.get("week"), is((Object) new Document("$week", Arrays.asList("$date")))); assertThat(projected.get("dayOfYear"), is((Object) new Document("$dayOfYear", Arrays.asList("$date")))); assertThat(projected.get("dayOfMonth"), is((Object) new Document("$dayOfMonth", Arrays.asList("$date")))); assertThat(projected.get("dayOfWeek"), is((Object) new Document("$dayOfWeek", Arrays.asList("$date")))); } @Test // DATAMONGO-975 public void shouldRenderDateTimeFragmentExtractionsForExpressionProjectionsCorrectly() throws Exception { ProjectionOperation operation = Aggregation.project() // .andExpression("date + 86400000") // .extractDayOfYear() // .as("dayOfYearPlus1Day") // ; Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(document, is(notNullValue())); Document projected = exctractOperation("$project", document); assertThat(projected.get("dayOfYearPlus1Day"), is((Object) new Document("$dayOfYear", Arrays.asList(new Document("$add", Arrays.<Object> asList("$date", 86400000)))))); } @Test // DATAMONGO-979 public void shouldRenderSizeExpressionInProjection() { ProjectionOperation operation = Aggregation // .project() // .and("tags") // .size()// .as("tags_count"); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projected = exctractOperation("$project", document); assertThat(projected.get("tags_count"), is((Object) new Document("$size", Arrays.asList("$tags")))); } @Test // DATAMONGO-979 public void shouldRenderGenericSizeExpressionInProjection() { ProjectionOperation operation = Aggregation // .project() // .and(SIZE.of(field("tags"))) // .as("tags_count"); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projected = exctractOperation("$project", document); assertThat(projected.get("tags_count"), is((Object) new Document("$size", Arrays.asList("$tags")))); } @Test // DATAMONGO-1457 public void shouldRenderSliceCorrectly() throws Exception { ProjectionOperation operation = Aggregation.project().and("field").slice(10).as("renamed"); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projected = exctractOperation("$project", document); assertThat(projected.get("renamed"), is((Object) new Document("$slice", Arrays.<Object> asList("$field", 10)))); } @Test // DATAMONGO-1457 public void shouldRenderSliceWithPositionCorrectly() throws Exception { ProjectionOperation operation = Aggregation.project().and("field").slice(10, 5).as("renamed"); Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); Document projected = exctractOperation("$project", document); assertThat(projected.get("renamed"), is((Object) new Document("$slice", Arrays.<Object> asList("$field", 5, 10)))); } @Test // DATAMONGO-784 public void shouldRenderCmpCorrectly() { ProjectionOperation operation = Aggregation.project().and("field").cmp(10).as("cmp10"); assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT), isBsonObject().containing("$project.cmp10.$cmp.[0]", "$field").containing("$project.cmp10.$cmp.[1]", 10)); } @Test // DATAMONGO-784 public void shouldRenderEqCorrectly() { ProjectionOperation operation = Aggregation.project().and("field").eq(10).as("eq10"); assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT), isBsonObject().containing("$project.eq10.$eq.[0]", "$field").containing("$project.eq10.$eq.[1]", 10)); } @Test // DATAMONGO-784 public void shouldRenderGtCorrectly() { ProjectionOperation operation = Aggregation.project().and("field").gt(10).as("gt10"); assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT), isBsonObject().containing("$project.gt10.$gt.[0]", "$field").containing("$project.gt10.$gt.[1]", 10)); } @Test // DATAMONGO-784 public void shouldRenderGteCorrectly() { ProjectionOperation operation = Aggregation.project().and("field").gte(10).as("gte10"); assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT), isBsonObject().containing("$project.gte10.$gte.[0]", "$field").containing("$project.gte10.$gte.[1]", 10)); } @Test // DATAMONGO-784 public void shouldRenderLtCorrectly() { ProjectionOperation operation = Aggregation.project().and("field").lt(10).as("lt10"); assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT), isBsonObject().containing("$project.lt10.$lt.[0]", "$field").containing("$project.lt10.$lt.[1]", 10)); } @Test // DATAMONGO-784 public void shouldRenderLteCorrectly() { ProjectionOperation operation = Aggregation.project().and("field").lte(10).as("lte10"); assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT), isBsonObject().containing("$project.lte10.$lte.[0]", "$field").containing("$project.lte10.$lte.[1]", 10)); } @Test // DATAMONGO-784 public void shouldRenderNeCorrectly() { ProjectionOperation operation = Aggregation.project().and("field").ne(10).as("ne10"); assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT), isBsonObject().containing("$project.ne10.$ne.[0]", "$field").containing("$project.ne10.$ne.[1]", 10)); } @Test // DATAMONGO-1536 public void shouldRenderSetEquals() { Document agg = project("A", "B").and("A").equalsArrays("B").as("sameElements") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, sameElements: { $setEquals: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetEqualsAggregationExpresssion() { Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").isEqualTo("B")).as("sameElements") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, sameElements: { $setEquals: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetIntersection() { Document agg = project("A", "B").and("A").intersectsArrays("B").as("commonToBoth") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, commonToBoth: { $setIntersection: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetIntersectionAggregationExpresssion() { Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").intersects("B")).as("commonToBoth") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, commonToBoth: { $setIntersection: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetUnion() { Document agg = project("A", "B").and("A").unionArrays("B").as("allValues").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, allValues: { $setUnion: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetUnionAggregationExpresssion() { Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").union("B")).as("allValues") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, allValues: { $setUnion: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetDifference() { Document agg = project("A", "B").and("B").differenceToArray("A").as("inBOnly") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, inBOnly: { $setDifference: [ \"$B\", \"$A\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetDifferenceAggregationExpresssion() { Document agg = project("A", "B").and(SetOperators.arrayAsSet("B").differenceTo("A")).as("inBOnly") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, inBOnly: { $setDifference: [ \"$B\", \"$A\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetIsSubset() { Document agg = project("A", "B").and("A").subsetOfArray("B").as("aIsSubsetOfB") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, aIsSubsetOfB: { $setIsSubset: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderSetIsSubsetAggregationExpresssion() { Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").isSubsetOf("B")).as("aIsSubsetOfB") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { A: 1, B: 1, aIsSubsetOfB: { $setIsSubset: [ \"$A\", \"$B\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderAnyElementTrue() { Document agg = project("responses").and("responses").anyElementInArrayTrue().as("isAnyTrue") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { responses: 1, isAnyTrue: { $anyElementTrue: [ \"$responses\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderAnyElementTrueAggregationExpresssion() { Document agg = project("responses").and(SetOperators.arrayAsSet("responses").anyElementTrue()).as("isAnyTrue") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { responses: 1, isAnyTrue: { $anyElementTrue: [ \"$responses\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderAllElementsTrue() { Document agg = project("responses").and("responses").allElementsInArrayTrue().as("isAllTrue") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { responses: 1, isAllTrue: { $allElementsTrue: [ \"$responses\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderAllElementsTrueAggregationExpresssion() { Document agg = project("responses").and(SetOperators.arrayAsSet("responses").allElementsTrue()).as("isAllTrue") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { responses: 1, isAllTrue: { $allElementsTrue: [ \"$responses\" ] }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderAbs() { Document agg = project().and("anyNumber").absoluteValue().as("absoluteValue") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { absoluteValue : { $abs: \"$anyNumber\" }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderAbsAggregationExpresssion() { Document agg = project() .and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).abs()) .as("delta").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { delta: { $abs: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderAddAggregationExpresssion() { Document agg = project().and(ArithmeticOperators.valueOf("price").add("fee")).as("total") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse(" { $project: { total: { $add: [ \"$price\", \"$fee\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderCeil() { Document agg = project().and("anyNumber").ceil().as("ceilValue").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { ceilValue : { $ceil: \"$anyNumber\" }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderCeilAggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ceil()) .as("delta").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { delta: { $ceil: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderDivide() { Document agg = project().and("value") .divide(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document .parse("{ $project: { result: { $divide: [ \"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderDivideAggregationExpresssion() { Document agg = project() .and(ArithmeticOperators.valueOf("anyNumber") .divideBy(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end")))) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document .parse("{ $project: { result: { $divide: [ \"$anyNumber\", { $subtract: [ \"$start\", \"$end\" ] }] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderExp() { Document agg = project().and("value").exp().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $exp: \"$value\" } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderExpAggregationExpresssion() { Document agg = project() .and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).exp()) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $exp: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderFloor() { Document agg = project().and("value").floor().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $floor: \"$value\" } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderFloorAggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).floor()) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $floor: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderLn() { Document agg = project().and("value").ln().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $ln: \"$value\"} }}"))); } @Test // DATAMONGO-1536 public void shouldRenderLnAggregationExpresssion() { Document agg = project() .and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ln()) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $ln: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderLog() { Document agg = project().and("value").log(2).as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $log: [ \"$value\", 2] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderLogAggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log(2)) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $log: [ { $subtract: [ \"$start\", \"$end\" ] }, 2] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderLog10() { Document agg = project().and("value").log10().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $log10: \"$value\" } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderLog10AggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log10()) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $log10: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderMod() { Document agg = project().and("value").mod(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $mod: [\"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderModAggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).mod(2)) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $mod: [{ $subtract: [ \"$start\", \"$end\" ] }, 2] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderMultiply() { Document agg = project().and("value") .multiply(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document .parse("{ $project: { result: { $multiply: [\"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderMultiplyAggregationExpresssion() { Document agg = project() .and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))) .multiplyBy(2).multiplyBy("refToAnotherNumber")) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse( "{ $project: { result: { $multiply: [{ $subtract: [ \"$start\", \"$end\" ] }, 2, \"$refToAnotherNumber\"] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderPow() { Document agg = project().and("value").pow(2).as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $pow: [\"$value\", 2] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderPowAggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).pow(2)) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $pow: [{ $subtract: [ \"$start\", \"$end\" ] }, 2] } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderSqrt() { Document agg = project().and("value").sqrt().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $sqrt: \"$value\" } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderSqrtAggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).sqrt()) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $sqrt: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderSubtract() { Document agg = project().and("numericField").minus(AggregationFunctionExpressions.SIZE.of(field("someArray"))) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is( Document.parse("{ $project: { result: { $subtract: [ \"$numericField\", { $size : [\"$someArray\"]}] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSubtractAggregationExpresssion() { Document agg = project() .and(ArithmeticOperators.valueOf("numericField") .subtract(AggregationFunctionExpressions.SIZE.of(field("someArray")))) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is( Document.parse("{ $project: { result: { $subtract: [ \"$numericField\", { $size : [\"$someArray\"]}] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderTrunc() { Document agg = project().and("value").trunc().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result : { $trunc: \"$value\" }}}"))); } @Test // DATAMONGO-1536 public void shouldRenderTruncAggregationExpresssion() { Document agg = project().and( ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).trunc()) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $trunc: { $subtract: [ \"$start\", \"$end\" ] } } }}"))); } @Test // DATAMONGO-1536 public void shouldRenderConcat() { Document agg = project().and("item").concat(" - ", field("description")).as("itemDescription") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { itemDescription: { $concat: [ \"$item\", \" - \", \"$description\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderConcatAggregationExpression() { Document agg = project().and(StringOperators.valueOf("item").concat(" - ").concatValueOf("description")) .as("itemDescription").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { itemDescription: { $concat: [ \"$item\", \" - \", \"$description\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSubstr() { Document agg = project().and("quarter").substring(0, 2).as("yearSubstring").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { yearSubstring: { $substr: [ \"$quarter\", 0, 2 ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSubstrAggregationExpression() { Document agg = project().and(StringOperators.valueOf("quarter").substring(0, 2)).as("yearSubstring") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { yearSubstring: { $substr: [ \"$quarter\", 0, 2 ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderToLower() { Document agg = project().and("item").toLower().as("item").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { item: { $toLower: \"$item\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderToLowerAggregationExpression() { Document agg = project().and(StringOperators.valueOf("item").toLower()).as("item") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { item: { $toLower: \"$item\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderToUpper() { Document agg = project().and("item").toUpper().as("item").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { item: { $toUpper: \"$item\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderToUpperAggregationExpression() { Document agg = project().and(StringOperators.valueOf("item").toUpper()).as("item") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { item: { $toUpper: \"$item\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderStrCaseCmp() { Document agg = project().and("quarter").strCaseCmp("13q4").as("comparisonResult") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { comparisonResult: { $strcasecmp: [ \"$quarter\", \"13q4\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderStrCaseCmpAggregationExpression() { Document agg = project().and(StringOperators.valueOf("quarter").strCaseCmp("13q4")).as("comparisonResult") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { comparisonResult: { $strcasecmp: [ \"$quarter\", \"13q4\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderArrayElementAt() { Document agg = project().and("favorites").arrayElementAt(0).as("first").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { first: { $arrayElemAt: [ \"$favorites\", 0 ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderArrayElementAtAggregationExpression() { Document agg = project().and(ArrayOperators.arrayOf("favorites").elementAt(0)).as("first") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { first: { $arrayElemAt: [ \"$favorites\", 0 ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderConcatArrays() { Document agg = project().and("instock").concatArrays("ordered").as("items").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { items: { $concatArrays: [ \"$instock\", \"$ordered\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderConcatArraysAggregationExpression() { Document agg = project().and(ArrayOperators.arrayOf("instock").concat("ordered")).as("items") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { items: { $concatArrays: [ \"$instock\", \"$ordered\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderIsArray() { Document agg = project().and("instock").isArray().as("isAnArray").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { isAnArray: { $isArray: \"$instock\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderIsArrayAggregationExpression() { Document agg = project().and(ArrayOperators.arrayOf("instock").isArray()).as("isAnArray") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { isAnArray: { $isArray: \"$instock\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSizeAggregationExpression() { Document agg = project().and(ArrayOperators.arrayOf("instock").length()).as("arraySize") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { arraySize: { $size: \"$instock\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSliceAggregationExpression() { Document agg = project().and(ArrayOperators.arrayOf("favorites").slice().itemCount(3)).as("threeFavorites") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { threeFavorites: { $slice: [ \"$favorites\", 3 ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSliceWithPositionAggregationExpression() { Document agg = project().and(ArrayOperators.arrayOf("favorites").slice().offset(2).itemCount(3)) .as("threeFavorites").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { threeFavorites: { $slice: [ \"$favorites\", 2, 3 ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderLiteral() { Document agg = project().and("$1").asLiteral().as("literalOnly").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { literalOnly: { $literal: \"$1\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderLiteralAggregationExpression() { Document agg = project().and(LiteralOperators.valueOf("$1").asLiteral()).as("literalOnly") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { literalOnly: { $literal: \"$1\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderDayOfYearAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").dayOfYear()).as("dayOfYear") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { dayOfYear: { $dayOfYear: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderDayOfMonthAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").dayOfMonth()).as("day") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { day: { $dayOfMonth: \"$date\" }} }"))); } @Test // DATAMONGO-1536 public void shouldRenderDayOfWeekAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").dayOfWeek()).as("dayOfWeek") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { dayOfWeek: { $dayOfWeek: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderYearAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").year()).as("year") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { year: { $year: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderMonthAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").month()).as("month") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { month: { $month: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderWeekAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").week()).as("week") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { week: { $week: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderHourAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").hour()).as("hour") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { hour: { $hour: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderMinuteAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").minute()).as("minute") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { minute: { $minute: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSecondAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").second()).as("second") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { second: { $second: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderMillisecondAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").millisecond()).as("msec") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { msec: { $millisecond: \"$date\" } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderDateToString() { Document agg = project().and("date").dateAsFormattedString("%H:%M:%S:%L").as("time") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderDateToStringAggregationExpression() { Document agg = project().and(DateOperators.dateOf("date").toString("%H:%M:%S:%L")).as("time") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSumAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("quizzes").sum()).as("quizTotal") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { quizTotal: { $sum: \"$quizzes\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderSumWithMultipleArgsAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("final").sum().and("midterm")).as("examTotal") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { examTotal: { $sum: [ \"$final\", \"$midterm\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderAvgAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("quizzes").avg()).as("quizAvg") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { quizAvg: { $avg: \"$quizzes\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderAvgWithMultipleArgsAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("final").avg().and("midterm")).as("examAvg") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { examAvg: { $avg: [ \"$final\", \"$midterm\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderMaxAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("quizzes").max()).as("quizMax") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { quizMax: { $max: \"$quizzes\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderMaxWithMultipleArgsAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("final").max().and("midterm")).as("examMax") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { examMax: { $max: [ \"$final\", \"$midterm\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderMinAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("quizzes").min()).as("quizMin") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { quizMin: { $min: \"$quizzes\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderMinWithMultipleArgsAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("final").min().and("midterm")).as("examMin") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { examMin: { $min: [ \"$final\", \"$midterm\" ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderStdDevPopAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("scores").stdDevPop()).as("stdDev") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { stdDev: { $stdDevPop: \"$scores\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderStdDevSampAggregationExpression() { Document agg = project().and(ArithmeticOperators.valueOf("scores").stdDevSamp()).as("stdDev") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { stdDev: { $stdDevSamp: \"$scores\"} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderCmpAggregationExpression() { Document agg = project().and(ComparisonOperators.valueOf("qty").compareToValue(250)).as("cmp250") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { cmp250: { $cmp: [\"$qty\", 250]} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderEqAggregationExpression() { Document agg = project().and(ComparisonOperators.valueOf("qty").equalToValue(250)).as("eq250") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { eq250: { $eq: [\"$qty\", 250]} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderGtAggregationExpression() { Document agg = project().and(ComparisonOperators.valueOf("qty").greaterThanValue(250)).as("gt250") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { gt250: { $gt: [\"$qty\", 250]} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderGteAggregationExpression() { Document agg = project().and(ComparisonOperators.valueOf("qty").greaterThanEqualToValue(250)).as("gte250") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { gte250: { $gte: [\"$qty\", 250]} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderLtAggregationExpression() { Document agg = project().and(ComparisonOperators.valueOf("qty").lessThanValue(250)).as("lt250") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { lt250: { $lt: [\"$qty\", 250]} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderLteAggregationExpression() { Document agg = project().and(ComparisonOperators.valueOf("qty").lessThanEqualToValue(250)).as("lte250") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { lte250: { $lte: [\"$qty\", 250]} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderNeAggregationExpression() { Document agg = project().and(ComparisonOperators.valueOf("qty").notEqualToValue(250)).as("ne250") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { ne250: { $ne: [\"$qty\", 250]} } }"))); } @Test // DATAMONGO-1536 public void shouldRenderLogicAndAggregationExpression() { Document agg = project() .and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(100)) .and(ComparisonOperators.valueOf("qty").lessThanValue(250))) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document .parse("{ $project: { result: { $and: [ { $gt: [ \"$qty\", 100 ] }, { $lt: [ \"$qty\", 250 ] } ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderLogicOrAggregationExpression() { Document agg = project() .and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(250)) .or(ComparisonOperators.valueOf("qty").lessThanValue(200))) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document .parse("{ $project: { result: { $or: [ { $gt: [ \"$qty\", 250 ] }, { $lt: [ \"$qty\", 200 ] } ] } } }"))); } @Test // DATAMONGO-1536 public void shouldRenderNotAggregationExpression() { Document agg = project().and(BooleanOperators.not(ComparisonOperators.valueOf("qty").greaterThanValue(250))) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $not: [ { $gt: [ \"$qty\", 250 ] } ] } } }"))); } @Test // DATAMONGO-1540 public void shouldRenderMapAggregationExpression() { Document agg = Aggregation.project() .and(VariableOperators.mapItemsOf("quizzes").as("grade") .andApply(AggregationFunctionExpressions.ADD.of(field("grade"), 2))) .as("adjustedGrades").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse( "{ $project:{ adjustedGrades:{ $map: { input: \"$quizzes\", as: \"grade\",in: { $add: [ \"$$grade\", 2 ] }}}}}"))); } @Test // DATAMONGO-1540 public void shouldRenderMapAggregationExpressionOnExpression() { Document agg = Aggregation.project() .and(VariableOperators.mapItemsOf(AggregationFunctionExpressions.SIZE.of("foo")).as("grade") .andApply(AggregationFunctionExpressions.ADD.of(field("grade"), 2))) .as("adjustedGrades").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse( "{ $project:{ adjustedGrades:{ $map: { input: { $size : [\"foo\"]}, as: \"grade\",in: { $add: [ \"$$grade\", 2 ] }}}}}"))); } @Test // DATAMONGO-861, DATAMONGO-1542 public void shouldRenderIfNullConditionAggregationExpression() { Document agg = project().and( ConditionalOperators.ifNull(ArrayOperators.arrayOf("array").elementAt(1)).then("a more sophisticated value")) .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse( "{ $project: { result: { $ifNull: [ { $arrayElemAt: [\"$array\", 1] }, \"a more sophisticated value\" ] } } }"))); } @Test // DATAMONGO-1542 public void shouldRenderIfNullValueAggregationExpression() { Document agg = project() .and(ConditionalOperators.ifNull("field").then(ArrayOperators.arrayOf("array").elementAt(1))).as("result") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $ifNull: [ \"$field\", { $arrayElemAt: [\"$array\", 1] } ] } } }"))); } @Test // DATAMONGO-861, DATAMONGO-1542 public void fieldReplacementIfNullShouldRenderCorrectly() { Document agg = project().and(ConditionalOperators.ifNull("optional").thenValueOf("$never-null")).as("result") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project: { result: { $ifNull: [ \"$optional\", \"$never-null\" ] } } }"))); } @Test // DATAMONGO-1538 public void shouldRenderLetExpressionCorrectly() { Document agg = Aggregation.project() .and(VariableOperators .define( newVariable("total") .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))), newVariable("discounted") .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D))) .andApply(AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted")))) // .as("finalTotal").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project:{ \"finalTotal\" : { \"$let\": {" + // "\"vars\": {" + // "\"total\": { \"$add\": [ \"$price\", \"$tax\" ] }," + // "\"discounted\": { \"$cond\": { \"if\": \"$applyDiscount\", \"then\": 0.9, \"else\": 1.0 } }" + // "}," + // "\"in\": { \"$multiply\": [ \"$$total\", \"$$discounted\" ] }" + // "}}}}"))); } @Test // DATAMONGO-1538 public void shouldRenderLetExpressionCorrectlyWhenUsingLetOnProjectionBuilder() { ExpressionVariable var1 = newVariable("total") .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))); ExpressionVariable var2 = newVariable("discounted") .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); Document agg = Aggregation.project().and("foo") .let(Arrays.asList(var1, var2), AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted"))) .as("finalTotal").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, is(Document.parse("{ $project:{ \"finalTotal\" : { \"$let\": {" + // "\"vars\": {" + // "\"total\": { \"$add\": [ \"$price\", \"$tax\" ] }," + // "\"discounted\": { \"$cond\": { \"if\": \"$applyDiscount\", \"then\": 0.9, \"else\": 1.0 } }" + // "}," + // "\"in\": { \"$multiply\": [ \"$$total\", \"$$discounted\" ] }" + // "}}}}"))); } @Test // DATAMONGO-1548 public void shouldRenderIndexOfBytesCorrectly() { Document agg = project().and(StringOperators.valueOf("item").indexOf("foo")).as("byteLocation") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project: { byteLocation: { $indexOfBytes: [ \"$item\", \"foo\" ] } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderIndexOfBytesWithRangeCorrectly() { Document agg = project().and(StringOperators.valueOf("item").indexOf("foo").within(new Range<Long>(5L, 9L))) .as("byteLocation").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, isBsonObject().containing("$project.byteLocation.$indexOfBytes.[2]", 5L) .containing("$project.byteLocation.$indexOfBytes.[3]", 9L)); } @Test // DATAMONGO-1548 public void shouldRenderIndexOfCPCorrectly() { Document agg = project().and(StringOperators.valueOf("item").indexOfCP("foo")).as("cpLocation") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project: { cpLocation: { $indexOfCP: [ \"$item\", \"foo\" ] } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderIndexOfCPWithRangeCorrectly() { Document agg = project().and(StringOperators.valueOf("item").indexOfCP("foo").within(new Range<Long>(5L, 9L))) .as("cpLocation").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, isBsonObject().containing("$project.cpLocation.$indexOfCP.[2]", 5L) .containing("$project.cpLocation.$indexOfCP.[3]", 9L)); } @Test // DATAMONGO-1548 public void shouldRenderSplitCorrectly() { Document agg = project().and(StringOperators.valueOf("city").split(", ")).as("city_state") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { city_state : { $split: [\"$city\", \", \"] }} }"))); } @Test // DATAMONGO-1548 public void shouldRenderStrLenBytesCorrectly() { Document agg = project().and(StringOperators.valueOf("name").length()).as("length") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { \"length\": { $strLenBytes: \"$name\" } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderStrLenCPCorrectly() { Document agg = project().and(StringOperators.valueOf("name").lengthCP()).as("length") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { \"length\": { $strLenCP: \"$name\" } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderSubstrCPCorrectly() { Document agg = project().and(StringOperators.valueOf("quarter").substringCP(0, 2)).as("yearSubstring") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { yearSubstring: { $substrCP: [ \"$quarter\", 0, 2 ] } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderIndexOfArrayCorrectly() { Document agg = project().and(ArrayOperators.arrayOf("items").indexOf(2)).as("index") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { index: { $indexOfArray: [ \"$items\", 2 ] } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderRangeCorrectly() { Document agg = project().and(ArrayOperators.RangeOperator.rangeStartingAt(0L).to("distance").withStepSize(25L)) .as("rest_stops").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, isBsonObject().containing("$project.rest_stops.$range.[0]", 0L) .containing("$project.rest_stops.$range.[1]", "$distance").containing("$project.rest_stops.$range.[2]", 25L)); } @Test // DATAMONGO-1548 public void shouldRenderReverseArrayCorrectly() { Document agg = project().and(ArrayOperators.arrayOf("favorites").reverse()).as("reverseFavorites") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { reverseFavorites: { $reverseArray: \"$favorites\" } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderReduceWithSimpleObjectCorrectly() { Document agg = project() .and(ArrayOperators.arrayOf("probabilityArr") .reduce(ArithmeticOperators.valueOf("$$value").multiplyBy("$$this")).startingWith(1)) .as("results").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse( "{ $project : { \"results\": { $reduce: { input: \"$probabilityArr\", initialValue: 1, in: { $multiply: [ \"$$value\", \"$$this\" ] } } } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderReduceWithComplexObjectCorrectly() { PropertyExpression sum = PropertyExpression.property("sum").definedAs( ArithmeticOperators.valueOf(Variable.VALUE.referringTo("sum").getName()).add(Variable.THIS.getName())); PropertyExpression product = PropertyExpression.property("product").definedAs(ArithmeticOperators .valueOf(Variable.VALUE.referringTo("product").getName()).multiplyBy(Variable.THIS.getName())); Document agg = project() .and(ArrayOperators.arrayOf("probabilityArr").reduce(sum, product) .startingWith(new Document().append("sum", 5).append("product", 2))) .as("results").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse( "{ $project : { \"results\": { $reduce: { input: \"$probabilityArr\", initialValue: { \"sum\" : 5 , \"product\" : 2} , in: { \"sum\": { $add : [\"$$value.sum\", \"$$this\"] }, \"product\": { $multiply: [ \"$$value.product\", \"$$this\" ] } } } } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderZipCorrectly() { AggregationExpression elemAt0 = ArrayOperators.arrayOf("matrix").elementAt(0); AggregationExpression elemAt1 = ArrayOperators.arrayOf("matrix").elementAt(1); AggregationExpression elemAt2 = ArrayOperators.arrayOf("matrix").elementAt(2); Document agg = project().and( ArrayOperators.arrayOf(elemAt0).zipWith(elemAt1, elemAt2).useLongestLength().defaultTo(new Object[] { 1, 2 })) .as("transposed").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse( "{ $project : { transposed: { $zip: { inputs: [ { $arrayElemAt: [ \"$matrix\", 0 ] }, { $arrayElemAt: [ \"$matrix\", 1 ] }, { $arrayElemAt: [ \"$matrix\", 2 ] } ], useLongestLength : true, defaults: [1,2] } } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderInCorrectly() { Document agg = project().and(ArrayOperators.arrayOf("in_stock").containsValue("bananas")).as("has_bananas") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { has_bananas : { $in : [\"bananas\", \"$in_stock\" ] } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderIsoDayOfWeekCorrectly() { Document agg = project().and(DateOperators.dateOf("birthday").isoDayOfWeek()).as("dayOfWeek") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { dayOfWeek: { $isoDayOfWeek: \"$birthday\" } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderIsoWeekCorrectly() { Document agg = project().and(DateOperators.dateOf("date").isoWeek()).as("weekNumber") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { weekNumber: { $isoWeek: \"$date\" } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderIsoWeekYearCorrectly() { Document agg = project().and(DateOperators.dateOf("date").isoWeekYear()).as("yearNumber") .toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { yearNumber: { $isoWeekYear: \"$date\" } } }"))); } @Test // DATAMONGO-1548 public void shouldRenderSwitchCorrectly() { String expected = "$switch:\n" + // "{\n" + // " branches: [\n" + // " {\n" + // " case: { $gte : [ { $avg : \"$scores\" }, 90 ] },\n" + // " then: \"Doing great!\"\n" + // " },\n" + // " {\n" + // " case: { $and : [ { $gte : [ { $avg : \"$scores\" }, 80 ] },\n" + // " { $lt : [ { $avg : \"$scores\" }, 90 ] } ] },\n" + // " then: \"Doing pretty well.\"\n" + // " },\n" + // " {\n" + // " case: { $lt : [ { $avg : \"$scores\" }, 80 ] },\n" + // " then: \"Needs improvement.\"\n" + // " }\n" + // " ],\n" + // " default: \"No scores found.\"\n" + // " }\n" + // "}"; CaseOperator cond1 = CaseOperator .when(ComparisonOperators.Gte.valueOf(AccumulatorOperators.Avg.avgOf("scores")).greaterThanEqualToValue(90)) .then("Doing great!"); CaseOperator cond2 = CaseOperator .when(BooleanOperators.And.and( ComparisonOperators.Gte.valueOf(AccumulatorOperators.Avg.avgOf("scores")).greaterThanEqualToValue(80), ComparisonOperators.Lt.valueOf(AccumulatorOperators.Avg.avgOf("scores")).lessThanValue(90))) .then("Doing pretty well."); CaseOperator cond3 = CaseOperator .when(ComparisonOperators.Lt.valueOf(AccumulatorOperators.Avg.avgOf("scores")).lessThanValue(80)) .then("Needs improvement."); Document agg = project().and(ConditionalOperators.switchCases(cond1, cond2, cond3).defaultTo("No scores found.")) .as("summary").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { summary: {" + expected + "} } }"))); } @Test // DATAMONGO-1548 public void shouldTypeCorrectly() { Document agg = project().and(DataTypeOperators.Type.typeOf("a")).as("a").toDocument(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(Document.parse("{ $project : { a: { $type: \"$a\" } } }"))); } private static Document exctractOperation(String field, Document fromProjectClause) { return (Document) fromProjectClause.get(field); } }