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