Skip to content

Commit a394e1d

Browse files
DATAMONGO-784 - Add support for comparison aggregation operators to group & project.
We now directly support comparison aggregation operators ($cmp, $eq, $gt, $gte, $lt, $lte and $ne) on both group and project stages. Further more we added $stdDevPop and $stdDevSamp support to the GroupOperationBuilder.
1 parent 521b3f6 commit a394e1d

File tree

5 files changed

+243
-13
lines changed

5 files changed

+243
-13
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationFunctionExpressions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
*/
3434
public enum AggregationFunctionExpressions {
3535

36-
SIZE;
36+
SIZE, CMP, EQ, GT, GTE, LT, LTE, NE;
3737

3838
/**
3939
* Returns an {@link AggregationExpression} build from the current {@link Enum} name and the given parameters.

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,50 @@ public GroupOperationBuilder max(AggregationExpression expr) {
307307
return newBuilder(GroupOps.MAX, null, expr);
308308
}
309309

310+
/**
311+
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevPop}-expression that for the given field-reference.
312+
*
313+
* @param reference must not be {@literal null}.
314+
* @return never {@literal null}.
315+
* @since 1.10
316+
*/
317+
public GroupOperationBuilder stdDevPop(String reference) {
318+
return newBuilder(GroupOps.STD_DEV_POP, reference, null);
319+
}
320+
321+
/**
322+
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevPop}-expression that for the given {@link AggregationExpression}.
323+
*
324+
* @param expr must not be {@literal null}.
325+
* @return never {@literal null}.
326+
* @since 1.10
327+
*/
328+
public GroupOperationBuilder stdDevPop(AggregationExpression expr) {
329+
return newBuilder(GroupOps.STD_DEV_POP, null, expr);
330+
}
331+
332+
/**
333+
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevPop}-expression that for the given field-reference.
334+
*
335+
* @param reference must not be {@literal null}.
336+
* @return never {@literal null}.
337+
* @since 1.10
338+
*/
339+
public GroupOperationBuilder stdDevSamp(String reference) {
340+
return newBuilder(GroupOps.STD_DEV_SAMP, reference, null);
341+
}
342+
343+
/**
344+
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevSamp}-expression that for the given {@link AggregationExpression}.
345+
*
346+
* @param expr must not be {@literal null}.
347+
* @return never {@literal null}.
348+
* @since 1.10
349+
*/
350+
public GroupOperationBuilder stdDevSamp(AggregationExpression expr) {
351+
return newBuilder(GroupOps.STD_DEV_SAMP, null, expr);
352+
}
353+
310354
private GroupOperationBuilder newBuilder(Keyword keyword, String reference, Object value) {
311355
return new GroupOperationBuilder(this, new Operation(keyword, null, reference, value));
312356
}
@@ -371,21 +415,18 @@ interface Keyword {
371415

372416
private static enum GroupOps implements Keyword {
373417

374-
SUM, LAST, FIRST, PUSH, AVG, MIN, MAX, ADD_TO_SET, COUNT;
418+
SUM("$sum"), LAST("$last"), FIRST("$first"), PUSH("$push"), AVG("$avg"), MIN("$min"), MAX("$max"), ADD_TO_SET("$addToSet"), STD_DEV_POP("$stdDevPop"), STD_DEV_SAMP("$stdDevSamp");
375419

376-
@Override
377-
public String toString() {
420+
private String mongoOperator;
378421

379-
String[] parts = name().split("_");
380-
381-
StringBuilder builder = new StringBuilder();
422+
GroupOps(String mongoOperator) {
423+
this.mongoOperator = mongoOperator;
424+
}
382425

383-
for (String part : parts) {
384-
String lowerCase = part.toLowerCase(Locale.US);
385-
builder.append(builder.length() == 0 ? lowerCase : StringUtils.capitalize(lowerCase));
386-
}
387426

388-
return "$" + builder.toString();
427+
@Override
428+
public String toString() {
429+
return mongoOperator;
389430
}
390431
}
391432

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,78 @@ public ProjectionOperationBuilder size() {
616616
return project("size");
617617
}
618618

619+
/**
620+
* Generates a {@code $cmp} expression (compare to) that compares the value of the field to a given value or field.
621+
*
622+
* @return never {@literal null}.
623+
* @since 1.10
624+
*/
625+
public ProjectionOperationBuilder cmp(Object compareValue) {
626+
return project("cmp", compareValue);
627+
}
628+
629+
/**
630+
* Generates a {@code $eq} expression (equal) that compares the value of the field to a given value or field.
631+
*
632+
* @return never {@literal null}.
633+
* @since 1.10
634+
*/
635+
public ProjectionOperationBuilder eq(Object compareValue) {
636+
return project("eq", compareValue);
637+
}
638+
639+
/**
640+
* Generates a {@code $gt} expression (greater than) that compares the value of the field to a given value or field.
641+
*
642+
* @return never {@literal null}.
643+
* @since 1.10
644+
*/
645+
public ProjectionOperationBuilder gt(Object compareValue) {
646+
return project("gt", compareValue);
647+
}
648+
649+
/**
650+
* Generates a {@code $gte} expression (greater than equal) that compares the value of the field to a given value or
651+
* field.
652+
*
653+
* @return never {@literal null}.
654+
* @since 1.10
655+
*/
656+
public ProjectionOperationBuilder gte(Object compareValue) {
657+
return project("gte", compareValue);
658+
}
659+
660+
/**
661+
* Generates a {@code $lt} expression (less than) that compares the value of the field to a given value or field.
662+
*
663+
* @return never {@literal null}.
664+
* @since 1.10
665+
*/
666+
public ProjectionOperationBuilder lt(Object compareValue) {
667+
return project("lt", compareValue);
668+
}
669+
670+
/**
671+
* Generates a {@code $lte} expression (less than equal) that compares the value of the field to a given value or
672+
* field.
673+
*
674+
* @return never {@literal null}.
675+
* @since 1.10
676+
*/
677+
public ProjectionOperationBuilder lte(Object compareValue) {
678+
return project("lte", compareValue);
679+
}
680+
681+
/**
682+
* Generates a {@code $ne} expression (not equal) that compares the value of the field to a given value or field.
683+
*
684+
* @return never {@literal null}.
685+
* @since 1.10
686+
*/
687+
public ProjectionOperationBuilder ne(Object compareValue) {
688+
return project("ne", compareValue);
689+
}
690+
619691
/**
620692
* Generates a {@code $slice} expression that returns a subset of the array held by the given field. <br />
621693
* If {@literal n} is positive, $slice returns up to the first n elements in the array. <br />

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GroupOperationUnitTests.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2015 the original author or authors.
2+
* Copyright 2013-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import static org.junit.Assert.*;
2020
import static org.springframework.data.mongodb.core.aggregation.AggregationFunctionExpressions.*;
2121
import static org.springframework.data.mongodb.core.aggregation.Fields.*;
22+
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
2223

2324
import java.util.Arrays;
2425

@@ -33,6 +34,7 @@
3334
*
3435
* @author Oliver Gierke
3536
* @author Thomas Darimont
37+
* @author Christoph Strobl
3638
*/
3739
public class GroupOperationUnitTests {
3840

@@ -204,6 +206,34 @@ public void shouldRenderSizeExpressionInGroup() {
204206
assertThat(tagsCount.get("$first"), is((Object) new BasicDBObject("$size", Arrays.asList("$tags"))));
205207
}
206208

209+
/**
210+
* @see DATAMONGO-784
211+
*/
212+
@Test
213+
public void shouldRenderStdDevPopInGroup() {
214+
215+
GroupOperation groupOperation = Aggregation //
216+
.group("quiz") //
217+
.stdDevPop("score").as("stdDev");
218+
219+
assertThat(groupOperation.toDBObject(Aggregation.DEFAULT_CONTEXT),
220+
isBsonObject().containing("$group.stdDev.$stdDevPop", "$score"));
221+
}
222+
223+
/**
224+
* @see DATAMONGO-784
225+
*/
226+
@Test
227+
public void shouldRenderStdDevSampInGroup() {
228+
229+
GroupOperation groupOperation = Aggregation //
230+
.group() //
231+
.stdDevSamp("age").as("ageStdDev");
232+
233+
assertThat(groupOperation.toDBObject(Aggregation.DEFAULT_CONTEXT),
234+
isBsonObject().containing("$group.ageStdDev.$stdDevSamp", "$age"));
235+
}
236+
207237
private DBObject extractDbObjectFromGroupOperation(GroupOperation groupOperation) {
208238
DBObject dbObject = groupOperation.toDBObject(Aggregation.DEFAULT_CONTEXT);
209239
DBObject groupClause = DBObjectTestUtils.getAsDBObject(dbObject, "$group");

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.junit.Test;
2828
import org.springframework.data.mongodb.core.DBObjectTestUtils;
2929
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder;
30+
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
3031

3132
import com.mongodb.BasicDBObject;
3233
import com.mongodb.DBObject;
@@ -402,6 +403,92 @@ public void shouldRenderSliceWithPositionCorrectly() throws Exception {
402403
is((Object) new BasicDBObject("$slice", Arrays.<Object> asList("$field", 5, 10))));
403404
}
404405

406+
/**
407+
* @see DATAMONGO-784
408+
*/
409+
@Test
410+
public void shouldRenderCmpCorrectly() {
411+
412+
ProjectionOperation operation = Aggregation.project().and("field").cmp(10).as("cmp10");
413+
414+
assertThat(operation.toDBObject(Aggregation.DEFAULT_CONTEXT),
415+
isBsonObject().containing("$project.cmp10.$cmp.[0]", "$field").containing("$project.cmp10.$cmp.[1]", 10));
416+
}
417+
418+
/**
419+
* @see DATAMONGO-784
420+
*/
421+
@Test
422+
public void shouldRenderEqCorrectly() {
423+
424+
ProjectionOperation operation = Aggregation.project().and("field").eq(10).as("eq10");
425+
426+
assertThat(operation.toDBObject(Aggregation.DEFAULT_CONTEXT),
427+
isBsonObject().containing("$project.eq10.$eq.[0]", "$field").containing("$project.eq10.$eq.[1]", 10));
428+
}
429+
430+
/**
431+
* @see DATAMONGO-784
432+
*/
433+
@Test
434+
public void shouldRenderGtCorrectly() {
435+
436+
ProjectionOperation operation = Aggregation.project().and("field").gt(10).as("gt10");
437+
438+
assertThat(operation.toDBObject(Aggregation.DEFAULT_CONTEXT),
439+
isBsonObject().containing("$project.gt10.$gt.[0]", "$field").containing("$project.gt10.$gt.[1]", 10));
440+
}
441+
442+
/**
443+
* @see DATAMONGO-784
444+
*/
445+
@Test
446+
public void shouldRenderGteCorrectly() {
447+
448+
ProjectionOperation operation = Aggregation.project().and("field").gte(10).as("gte10");
449+
450+
assertThat(operation.toDBObject(Aggregation.DEFAULT_CONTEXT),
451+
isBsonObject().containing("$project.gte10.$gte.[0]", "$field").containing("$project.gte10.$gte.[1]", 10));
452+
}
453+
454+
/**
455+
* @see DATAMONGO-784
456+
*/
457+
@Test
458+
public void shouldRenderLtCorrectly() {
459+
460+
ProjectionOperation operation = Aggregation.project().and("field").lt(10).as("lt10");
461+
462+
assertThat(operation.toDBObject(Aggregation.DEFAULT_CONTEXT),
463+
isBsonObject().containing("$project.lt10.$lt.[0]", "$field").containing("$project.lt10.$lt.[1]", 10));
464+
}
465+
466+
/**
467+
* @see DATAMONGO-784
468+
*/
469+
@Test
470+
public void shouldRenderLteCorrectly() {
471+
472+
ProjectionOperation operation = Aggregation.project().and("field").lte(10).as("lte10");
473+
474+
assertThat(operation.toDBObject(Aggregation.DEFAULT_CONTEXT),
475+
isBsonObject().containing("$project.lte10.$lte.[0]", "$field").containing("$project.lte10.$lte.[1]", 10));
476+
}
477+
478+
/**
479+
* @see DATAMONGO-784
480+
*/
481+
@Test
482+
public void shouldRenderNeCorrectly() {
483+
484+
ProjectionOperation operation = Aggregation.project().and("field").ne(10).as("ne10");
485+
486+
assertThat(operation.toDBObject(Aggregation.DEFAULT_CONTEXT),
487+
isBsonObject().containing("$project.ne10.$ne.[0]", "$field").containing("$project.ne10.$ne.[1]", 10));
488+
}
489+
490+
491+
405492
private static DBObject exctractOperation(String field, DBObject fromProjectClause) {
406493
return (DBObject) fromProjectClause.get(field);
407494
}

0 commit comments

Comments
 (0)