17
17
18
18
import java .util .ArrayList ;
19
19
import java .util .List ;
20
- import java .util .stream .Collectors ;
20
+ import java .util .function .LongUnaryOperator ;
21
+ import java .util .stream .Stream ;
21
22
22
23
import org .bson .Document ;
24
+
23
25
import org .springframework .data .domain .Pageable ;
24
26
import org .springframework .data .domain .SliceImpl ;
25
27
import org .springframework .data .mapping .model .SpELExpressionEvaluator ;
42
44
import org .springframework .util .ClassUtils ;
43
45
44
46
/**
47
+ * {@link AbstractMongoQuery} implementation to run string-based aggregations using
48
+ * {@link org.springframework.data.mongodb.repository.Aggregation}.
49
+ *
45
50
* @author Christoph Strobl
51
+ * @author Divya Srivastava
52
+ * @author Mark Paluch
46
53
* @since 2.2
47
54
*/
48
55
public class StringBasedAggregation extends AbstractMongoQuery {
@@ -64,6 +71,12 @@ public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOper
64
71
ExpressionParser expressionParser , QueryMethodEvaluationContextProvider evaluationContextProvider ) {
65
72
super (method , mongoOperations , expressionParser , evaluationContextProvider );
66
73
74
+ if (method .isPageQuery ()) {
75
+ throw new InvalidMongoDbApiUsageException (String .format (
76
+ "Repository aggregation method '%s' does not support '%s' return type. Please use 'Slice' or 'List' instead." ,
77
+ method .getName (), method .getReturnType ().getType ().getSimpleName ()));
78
+ }
79
+
67
80
this .mongoOperations = mongoOperations ;
68
81
this .mongoConverter = mongoOperations .getConverter ();
69
82
this .expressionParser = expressionParser ;
@@ -83,10 +96,11 @@ protected Object doExecute(MongoQueryMethod method, ResultProcessor resultProces
83
96
84
97
List <AggregationOperation > pipeline = computePipeline (method , accessor );
85
98
AggregationUtils .appendSortIfPresent (pipeline , accessor , typeToRead );
86
-
99
+
87
100
if (method .isSliceQuery ()) {
88
- AggregationUtils .appendModifiedLimitAndOffsetIfPresent (pipeline , accessor );
89
- }else {
101
+ AggregationUtils .appendLimitAndOffsetIfPresent (pipeline , accessor , LongUnaryOperator .identity (),
102
+ limit -> limit + 1 );
103
+ } else {
90
104
AggregationUtils .appendLimitAndOffsetIfPresent (pipeline , accessor );
91
105
}
92
106
@@ -96,47 +110,63 @@ protected Object doExecute(MongoQueryMethod method, ResultProcessor resultProces
96
110
if (isSimpleReturnType ) {
97
111
targetType = Document .class ;
98
112
} else if (isRawAggregationResult ) {
113
+
114
+ // 🙈
99
115
targetType = method .getReturnType ().getRequiredActualType ().getRequiredComponentType ().getType ();
100
116
}
101
117
102
118
AggregationOptions options = computeOptions (method , accessor );
103
119
TypedAggregation <?> aggregation = new TypedAggregation <>(sourceType , pipeline , options );
104
120
105
- AggregationResults <?> result = mongoOperations .aggregate (aggregation , targetType );
121
+ if (method .isStreamQuery ()) {
122
+
123
+ Stream <?> stream = mongoOperations .aggregateStream (aggregation , targetType ).stream ();
124
+
125
+ if (isSimpleReturnType ) {
126
+ return stream .map (it -> AggregationUtils .extractSimpleTypeResult ((Document ) it , typeToRead , mongoConverter ));
127
+ }
128
+
129
+ return stream ;
130
+ }
131
+
132
+ AggregationResults <Object > result = (AggregationResults <Object >) mongoOperations .aggregate (aggregation , targetType );
106
133
107
134
if (isRawAggregationResult ) {
108
135
return result ;
109
136
}
110
137
138
+ List <Object > results = result .getMappedResults ();
111
139
if (method .isCollectionQuery ()) {
140
+ return isSimpleReturnType ? convertResults (typeToRead , results ) : results ;
141
+ }
112
142
113
- if (isSimpleReturnType ) {
114
-
115
- return result .getMappedResults ().stream ()
116
- .map (it -> AggregationUtils .extractSimpleTypeResult ((Document ) it , typeToRead , mongoConverter ))
117
- .collect (Collectors .toList ());
118
- }
143
+ if (method .isSliceQuery ()) {
119
144
120
- return result .getMappedResults ();
121
- }
122
-
123
- List mappedResults = result .getMappedResults ();
124
-
125
- if (method .isSliceQuery ()) {
126
-
127
145
Pageable pageable = accessor .getPageable ();
128
146
int pageSize = pageable .getPageSize ();
129
- boolean hasNext = mappedResults .size () > pageSize ;
130
- return new SliceImpl <Object >(hasNext ? mappedResults .subList (0 , pageSize ) : mappedResults , pageable , hasNext );
147
+ List <Object > resultsToUse = isSimpleReturnType ? convertResults (typeToRead , results ) : results ;
148
+ boolean hasNext = resultsToUse .size () > pageSize ;
149
+ return new SliceImpl <>(hasNext ? resultsToUse .subList (0 , pageSize ) : resultsToUse , pageable , hasNext );
131
150
}
132
-
151
+
133
152
Object uniqueResult = result .getUniqueMappedResult ();
134
153
135
154
return isSimpleReturnType
136
155
? AggregationUtils .extractSimpleTypeResult ((Document ) uniqueResult , typeToRead , mongoConverter )
137
156
: uniqueResult ;
138
157
}
139
158
159
+ private List <Object > convertResults (Class <?> typeToRead , List <Object > mappedResults ) {
160
+
161
+ List <Object > list = new ArrayList <>(mappedResults .size ());
162
+ for (Object it : mappedResults ) {
163
+ Object extractSimpleTypeResult = AggregationUtils .extractSimpleTypeResult ((Document ) it , typeToRead ,
164
+ mongoConverter );
165
+ list .add (extractSimpleTypeResult );
166
+ }
167
+ return list ;
168
+ }
169
+
140
170
private boolean isSimpleReturnType (Class <?> targetType ) {
141
171
return MongoSimpleTypes .HOLDER .isSimpleType (targetType );
142
172
}
0 commit comments