Skip to content

Commit 6f5075c

Browse files
DATAMONGO-1536 - Add array operators (aggregation)
1 parent edb2f76 commit 6f5075c

File tree

3 files changed

+543
-125
lines changed

3 files changed

+543
-125
lines changed

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

Lines changed: 319 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222
import java.util.Map;
2323

24+
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.AsBuilder;
2425
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
2526
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
2627
import org.springframework.util.Assert;
@@ -662,7 +663,7 @@ private Substr createSubstr() {
662663
*
663664
* @return
664665
*/
665-
private ToLower toLower() {
666+
public ToLower toLower() {
666667
return fieldRef != null ? ToLower.lowerValueOf(fieldRef) : ToLower.lowerValueOf(expression);
667668
}
668669

@@ -671,7 +672,7 @@ private ToLower toLower() {
671672
*
672673
* @return
673674
*/
674-
private ToUpper toUpper() {
675+
public ToUpper toUpper() {
675676
return fieldRef != null ? ToUpper.upperValueOf(fieldRef) : ToUpper.upperValueOf(expression);
676677
}
677678

@@ -682,7 +683,7 @@ private ToUpper toUpper() {
682683
* @param value must not be {@literal null}.
683684
* @return
684685
*/
685-
private StrCaseCmp strCaseCmp(String value) {
686+
public StrCaseCmp strCaseCmp(String value) {
686687

687688
Assert.notNull(value, "Value must not be null!");
688689
return createStrCaseCmp().strcasecmp(value);
@@ -695,7 +696,7 @@ private StrCaseCmp strCaseCmp(String value) {
695696
* @param fieldRef must not be {@literal null}.
696697
* @return
697698
*/
698-
private StrCaseCmp strCaseCmpValueOf(String fieldRef) {
699+
public StrCaseCmp strCaseCmpValueOf(String fieldRef) {
699700

700701
Assert.notNull(fieldRef, "FieldRef must not be null!");
701702
return createStrCaseCmp().strcasecmpValueOf(fieldRef);
@@ -708,7 +709,7 @@ private StrCaseCmp strCaseCmpValueOf(String fieldRef) {
708709
* @param expression must not be {@literal null}.
709710
* @return
710711
*/
711-
private StrCaseCmp strCaseCmpValueOf(AggregationExpression expression) {
712+
public StrCaseCmp strCaseCmpValueOf(AggregationExpression expression) {
712713

713714
Assert.notNull(expression, "Expression must not be null!");
714715
return createStrCaseCmp().strcasecmpValueOf(expression);
@@ -720,6 +721,155 @@ private StrCaseCmp createStrCaseCmp() {
720721
}
721722
}
722723

724+
/**
725+
* @author Christoph Strobl
726+
*/
727+
class ArrayOperators {
728+
729+
/**
730+
* Take the array referenced by given {@literal fieldRef}.
731+
*
732+
* @param fieldRef must not be {@literal null}.
733+
* @return
734+
*/
735+
public static ArrayOperatorFactory arrayOf(String fieldRef) {
736+
return new ArrayOperatorFactory(fieldRef);
737+
}
738+
739+
/**
740+
* Take the array referenced by given {@literal fieldRef}.
741+
*
742+
* @param fieldRef must not be {@literal null}.
743+
* @return
744+
*/
745+
public static ArrayOperatorFactory arrayOf(AggregationExpression expression) {
746+
return new ArrayOperatorFactory(expression);
747+
}
748+
749+
public static class ArrayOperatorFactory {
750+
751+
private final String fieldRef;
752+
private final AggregationExpression expression;
753+
754+
public ArrayOperatorFactory(String fieldRef) {
755+
this.fieldRef = fieldRef;
756+
this.expression = null;
757+
}
758+
759+
public ArrayOperatorFactory(AggregationExpression expression) {
760+
this.fieldRef = null;
761+
this.expression = expression;
762+
}
763+
764+
/**
765+
* Takes the associated array and returns the element at the specified array {@literal position}.
766+
*
767+
* @param position
768+
* @return
769+
*/
770+
public ArrayElemtAt elementAt(int position) {
771+
return createArrayElemAt().elementAt(position);
772+
}
773+
774+
/**
775+
* Takes the associated array and returns the element at the position resulting form the given
776+
* {@literal expression}.
777+
*
778+
* @param expression must not be {@literal null}.
779+
* @return
780+
*/
781+
public ArrayElemtAt elementAt(AggregationExpression expression) {
782+
783+
Assert.notNull(expression, "Expression must not be null!");
784+
return createArrayElemAt().elementAt(expression);
785+
}
786+
787+
/**
788+
* Takes the associated array and returns the element at the position defined by the referenced {@literal field}.
789+
*
790+
* @param fieldRef must not be {@literal null}.
791+
* @return
792+
*/
793+
public ArrayElemtAt elementAt(String fieldRef) {
794+
795+
Assert.notNull(fieldRef, "FieldRef must not be null!");
796+
return createArrayElemAt().elementAt(fieldRef);
797+
}
798+
799+
private ArrayElemtAt createArrayElemAt() {
800+
return usesFieldRef() ? ArrayElemtAt.arrayOf(fieldRef) : ArrayElemtAt.arrayOf(expression);
801+
}
802+
803+
/**
804+
* Takes the associated array and concats the given {@literal arrayFieldReference} to it.
805+
*
806+
* @param arrayFieldReference must not be {@literal null}.
807+
* @return
808+
*/
809+
public ConcatArrays concat(String arrayFieldReference) {
810+
811+
Assert.notNull(arrayFieldReference, "ArrayFieldReference must not be null!");
812+
return createConcatArrays().concat(arrayFieldReference);
813+
}
814+
815+
/**
816+
* Takes the associated array and concats the array resulting form the given {@literal expression} to it.
817+
*
818+
* @param expression must not be {@literal null}.
819+
* @return
820+
*/
821+
public ConcatArrays concat(AggregationExpression expression) {
822+
823+
Assert.notNull(expression, "Expression must not be null!");
824+
return createConcatArrays().concat(expression);
825+
}
826+
827+
private ConcatArrays createConcatArrays() {
828+
return usesFieldRef() ? ConcatArrays.arrayOf(fieldRef) : ConcatArrays.arrayOf(expression);
829+
}
830+
831+
/**
832+
* Takes the associated array and selects a subset of the array to return based on the specified condition.
833+
*
834+
* @return
835+
*/
836+
public AsBuilder filter() {
837+
return Filter.filter(fieldRef);
838+
}
839+
840+
/**
841+
* Takes the associated array and an check if its an array.
842+
*
843+
* @return
844+
*/
845+
public IsArray isArray() {
846+
return usesFieldRef() ? IsArray.isArray(fieldRef) : IsArray.isArray(expression);
847+
}
848+
849+
/**
850+
* Takes the associated array and retrieves its length.
851+
*
852+
* @return
853+
*/
854+
public Size length() {
855+
return usesFieldRef() ? Size.lengthOfArray(fieldRef) : Size.lengthOfArray(expression);
856+
}
857+
858+
/**
859+
* Takes the associated array and selects a subset from it.
860+
*
861+
* @return
862+
*/
863+
public Slice slice() {
864+
return usesFieldRef() ? Slice.sliceArrayOf(fieldRef) : Slice.sliceArrayOf(expression);
865+
}
866+
867+
private boolean usesFieldRef() {
868+
return fieldRef != null;
869+
}
870+
}
871+
}
872+
723873
/**
724874
* @author Christoph Strobl
725875
*/
@@ -1679,7 +1829,76 @@ public StrCaseCmp strcasecmpValueOf(String fieldRef) {
16791829
public StrCaseCmp strcasecmpValueOf(AggregationExpression expression) {
16801830
return new StrCaseCmp(append(expression));
16811831
}
1832+
}
16821833

1834+
/**
1835+
* {@link AggregationExpression} for {@code $arrayElementAt}.
1836+
*
1837+
* @author Christoph Strobl
1838+
*/
1839+
class ArrayElemtAt extends AbstractAggregationExpression {
1840+
1841+
private ArrayElemtAt(List<?> value) {
1842+
super(value);
1843+
}
1844+
1845+
@Override
1846+
public String getMongoMethod() {
1847+
return "$arrayElemAt";
1848+
}
1849+
1850+
public static ArrayElemtAt arrayOf(String fieldRef) {
1851+
return new ArrayElemtAt(asFields(fieldRef));
1852+
}
1853+
1854+
public static ArrayElemtAt arrayOf(AggregationExpression expression) {
1855+
return new ArrayElemtAt(Collections.singletonList(expression));
1856+
}
1857+
1858+
public ArrayElemtAt elementAt(int index) {
1859+
return new ArrayElemtAt(append(index));
1860+
}
1861+
1862+
public ArrayElemtAt elementAt(AggregationExpression expression) {
1863+
return new ArrayElemtAt(append(expression));
1864+
}
1865+
1866+
public ArrayElemtAt elementAt(String numericFieldRef) {
1867+
return new ArrayElemtAt(append(Fields.field(numericFieldRef)));
1868+
}
1869+
}
1870+
1871+
/**
1872+
* {@link AggregationExpression} for {@code $concatArrays}.
1873+
*
1874+
* @author Christoph Strobl
1875+
*/
1876+
class ConcatArrays extends AbstractAggregationExpression {
1877+
1878+
private ConcatArrays(List<?> value) {
1879+
super(value);
1880+
}
1881+
1882+
@Override
1883+
public String getMongoMethod() {
1884+
return "$concatArrays";
1885+
}
1886+
1887+
public static ConcatArrays arrayOf(String fieldRef) {
1888+
return new ConcatArrays(asFields(fieldRef));
1889+
}
1890+
1891+
public static ConcatArrays arrayOf(AggregationExpression expression) {
1892+
return new ConcatArrays(Collections.singletonList(expression));
1893+
}
1894+
1895+
public ConcatArrays concat(String arrayFieldReference) {
1896+
return new ConcatArrays(append(Fields.field(arrayFieldReference)));
1897+
}
1898+
1899+
public ConcatArrays concat(AggregationExpression expression) {
1900+
return new ConcatArrays(append(expression));
1901+
}
16831902
}
16841903

16851904
/**
@@ -1939,4 +2158,99 @@ public Filter by(DBObject expression) {
19392158
}
19402159
}
19412160
}
2161+
2162+
/**
2163+
* {@link AggregationExpression} for {@code $isArray}.
2164+
*
2165+
* @author Christoph Strobl
2166+
*/
2167+
class IsArray extends AbstractAggregationExpression {
2168+
2169+
private IsArray(Object value) {
2170+
super(value);
2171+
}
2172+
2173+
@Override
2174+
public String getMongoMethod() {
2175+
return "$isArray";
2176+
}
2177+
2178+
public static IsArray isArray(String fieldRef) {
2179+
return new IsArray(Fields.field(fieldRef));
2180+
}
2181+
2182+
public static IsArray isArray(AggregationExpression expression) {
2183+
return new IsArray(expression);
2184+
}
2185+
}
2186+
2187+
/**
2188+
* {@link AggregationExpression} for {@code $size}.
2189+
*
2190+
* @author Christoph Strobl
2191+
*/
2192+
class Size extends AbstractAggregationExpression {
2193+
2194+
private Size(Object value) {
2195+
super(value);
2196+
}
2197+
2198+
@Override
2199+
public String getMongoMethod() {
2200+
return "$size";
2201+
}
2202+
2203+
public static Size lengthOfArray(String fieldRef) {
2204+
return new Size(Fields.field(fieldRef));
2205+
}
2206+
2207+
public static Size lengthOfArray(AggregationExpression expression) {
2208+
return new Size(expression);
2209+
}
2210+
}
2211+
2212+
/**
2213+
* {@link AggregationExpression} for {@code $slice}.
2214+
*
2215+
* @author Christoph Strobl
2216+
*/
2217+
class Slice extends AbstractAggregationExpression {
2218+
2219+
private Slice(List<?> value) {
2220+
super(value);
2221+
}
2222+
2223+
@Override
2224+
public String getMongoMethod() {
2225+
return "$slice";
2226+
}
2227+
2228+
public static Slice sliceArrayOf(String fieldRef) {
2229+
return new Slice(asFields(fieldRef));
2230+
}
2231+
2232+
public static Slice sliceArrayOf(AggregationExpression expression) {
2233+
return new Slice(Collections.singletonList(expression));
2234+
}
2235+
2236+
public Slice itemCount(int nrElements) {
2237+
return new Slice(append(nrElements));
2238+
}
2239+
2240+
public SliceElementsBuilder offset(final int position) {
2241+
2242+
return new SliceElementsBuilder() {
2243+
2244+
@Override
2245+
public Slice itemCount(int nrElements) {
2246+
return new Slice(append(position)).itemCount(nrElements);
2247+
}
2248+
};
2249+
}
2250+
2251+
public interface SliceElementsBuilder {
2252+
Slice itemCount(int nrElements);
2253+
}
2254+
}
2255+
19422256
}

0 commit comments

Comments
 (0)