17
17
18
18
import static org .springframework .data .mongodb .core .query .Criteria .*;
19
19
20
+ import java .util .Arrays ;
20
21
import java .util .Collection ;
21
22
import java .util .Iterator ;
22
23
35
36
import org .springframework .data .mongodb .repository .query .ConvertingParameterAccessor .PotentiallyConvertingIterator ;
36
37
import org .springframework .data .repository .query .parser .AbstractQueryCreator ;
37
38
import org .springframework .data .repository .query .parser .Part ;
39
+ import org .springframework .data .repository .query .parser .Part .IgnoreCaseType ;
38
40
import org .springframework .data .repository .query .parser .Part .Type ;
39
41
import org .springframework .data .repository .query .parser .PartTree ;
40
42
import org .springframework .util .Assert ;
43
45
* Custom query creator to create Mongo criterias.
44
46
*
45
47
* @author Oliver Gierke
48
+ * @author Thomas Darimont
46
49
*/
47
50
class MongoQueryCreator extends AbstractQueryCreator <Query , Criteria > {
48
51
@@ -99,7 +102,7 @@ protected Criteria create(Part part, Iterator<Object> iterator) {
99
102
100
103
PersistentPropertyPath <MongoPersistentProperty > path = context .getPersistentPropertyPath (part .getProperty ());
101
104
MongoPersistentProperty property = path .getLeafProperty ();
102
- Criteria criteria = from (part . getType () , property ,
105
+ Criteria criteria = from (part , property ,
103
106
where (path .toDotPath (MongoPersistentProperty .PropertyToFieldNameConverter .INSTANCE )),
104
107
(PotentiallyConvertingIterator ) iterator );
105
108
@@ -120,7 +123,7 @@ protected Criteria and(Part part, Criteria base, Iterator<Object> iterator) {
120
123
PersistentPropertyPath <MongoPersistentProperty > path = context .getPersistentPropertyPath (part .getProperty ());
121
124
MongoPersistentProperty property = path .getLeafProperty ();
122
125
123
- return from (part . getType () , property ,
126
+ return from (part , property ,
124
127
base .and (path .toDotPath (MongoPersistentProperty .PropertyToFieldNameConverter .INSTANCE )),
125
128
(PotentiallyConvertingIterator ) iterator );
126
129
}
@@ -165,9 +168,11 @@ protected Query complete(Criteria criteria, Sort sort) {
165
168
* @param parameters
166
169
* @return
167
170
*/
168
- private Criteria from (Type type , MongoPersistentProperty property , Criteria criteria ,
171
+ private Criteria from (Part part , MongoPersistentProperty property , Criteria criteria ,
169
172
PotentiallyConvertingIterator parameters ) {
170
173
174
+ Type type = part .getType ();
175
+
171
176
switch (type ) {
172
177
case AFTER :
173
178
case GREATER_THAN :
@@ -193,8 +198,7 @@ private Criteria from(Type type, MongoPersistentProperty property, Criteria crit
193
198
case STARTING_WITH :
194
199
case ENDING_WITH :
195
200
case CONTAINING :
196
- String value = parameters .next ().toString ();
197
- return criteria .regex (toLikeRegex (value , type ));
201
+ return addAppropriateLikeRegexTo (criteria , part , parameters .next ().toString ());
198
202
case REGEX :
199
203
return criteria .regex (parameters .next ().toString ());
200
204
case EXISTS :
@@ -220,19 +224,103 @@ private Criteria from(Type type, MongoPersistentProperty property, Criteria crit
220
224
criteria .maxDistance (distance .getNormalizedValue ());
221
225
}
222
226
return criteria ;
223
-
224
227
case WITHIN :
228
+
225
229
Object parameter = parameters .next ();
226
230
return criteria .within ((Shape ) parameter );
227
231
case SIMPLE_PROPERTY :
228
- return criteria .is (parameters .nextConverted (property ));
232
+
233
+ return isSimpleComparisionPossible (part ) ? criteria .is (parameters .nextConverted (property ))
234
+ : createLikeRegexCriteriaOrThrow (part , property , criteria , parameters , false );
235
+
229
236
case NEGATING_SIMPLE_PROPERTY :
230
- return criteria .ne (parameters .nextConverted (property ));
237
+
238
+ return isSimpleComparisionPossible (part ) ? criteria .ne (parameters .nextConverted (property ))
239
+ : createLikeRegexCriteriaOrThrow (part , property , criteria , parameters , true );
231
240
default :
232
241
throw new IllegalArgumentException ("Unsupported keyword!" );
233
242
}
234
243
}
235
244
245
+ private boolean isSimpleComparisionPossible (Part part ) {
246
+
247
+ switch (part .shouldIgnoreCase ()) {
248
+ case NEVER :
249
+ return true ;
250
+ case WHEN_POSSIBLE :
251
+ return part .getProperty ().getType () != String .class ;
252
+ case ALWAYS :
253
+ return false ;
254
+ default :
255
+ return true ;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Creates and extends the given criteria with a like-regex if necessary.
261
+ *
262
+ * @param part
263
+ * @param property
264
+ * @param criteria
265
+ * @param parameters
266
+ * @param shouldNegateExpression
267
+ * @return the criteria extended with the like-regex.
268
+ */
269
+ private Criteria createLikeRegexCriteriaOrThrow (Part part , MongoPersistentProperty property , Criteria criteria ,
270
+ PotentiallyConvertingIterator parameters , boolean shouldNegateExpression ) {
271
+
272
+ switch (part .shouldIgnoreCase ()) {
273
+
274
+ case ALWAYS :
275
+ if (part .getProperty ().getType () != String .class ) {
276
+ throw new IllegalArgumentException (String .format ("part %s must be of type String but was %s" ,
277
+ part .getProperty (), part .getType ()));
278
+ }
279
+ // fall-through
280
+
281
+ case WHEN_POSSIBLE :
282
+ if (shouldNegateExpression ) {
283
+ criteria = criteria .not ();
284
+ }
285
+ return addAppropriateLikeRegexTo (criteria , part , parameters .nextConverted (property ).toString ());
286
+
287
+ case NEVER :
288
+ // intentional no-op
289
+ }
290
+
291
+ throw new IllegalArgumentException (String .format ("part.shouldCaseIgnore must be one of %s, but was %s" ,
292
+ Arrays .asList (IgnoreCaseType .ALWAYS , IgnoreCaseType .WHEN_POSSIBLE ), part .shouldIgnoreCase ()));
293
+ }
294
+
295
+ /**
296
+ * Creates an appropriate like-regex and appends it to the given criteria.
297
+ *
298
+ * @param criteria
299
+ * @param part
300
+ * @param value
301
+ * @return the criteria extended with the regex.
302
+ */
303
+ private Criteria addAppropriateLikeRegexTo (Criteria criteria , Part part , String value ) {
304
+
305
+ return criteria .regex (toLikeRegex (value , part ), toRegexOptions (part ));
306
+ }
307
+
308
+ /**
309
+ * @param part
310
+ * @return the regex options or {@literal null}.
311
+ */
312
+ private String toRegexOptions (Part part ) {
313
+
314
+ String regexOptions = null ;
315
+ switch (part .shouldIgnoreCase ()) {
316
+ case WHEN_POSSIBLE :
317
+ case ALWAYS :
318
+ regexOptions = "i" ;
319
+ case NEVER :
320
+ }
321
+ return regexOptions ;
322
+ }
323
+
236
324
/**
237
325
* Returns the next element from the given {@link Iterator} expecting it to be of a certain type.
238
326
*
@@ -265,7 +353,9 @@ private Object[] nextAsArray(PotentiallyConvertingIterator iterator, MongoPersis
265
353
return new Object [] { next };
266
354
}
267
355
268
- private String toLikeRegex (String source , Type type ) {
356
+ private String toLikeRegex (String source , Part part ) {
357
+
358
+ Type type = part .getType ();
269
359
270
360
switch (type ) {
271
361
case STARTING_WITH :
@@ -277,6 +367,9 @@ private String toLikeRegex(String source, Type type) {
277
367
case CONTAINING :
278
368
source = "*" + source + "*" ;
279
369
break ;
370
+ case SIMPLE_PROPERTY :
371
+ case NEGATING_SIMPLE_PROPERTY :
372
+ source = "^" + source + "$" ;
280
373
default :
281
374
}
282
375
0 commit comments