1717
1818import static org .springframework .data .mongodb .core .query .Criteria .*;
1919
20+ import java .util .Arrays ;
2021import java .util .Collection ;
2122import java .util .Iterator ;
2223
3536import org .springframework .data .mongodb .repository .query .ConvertingParameterAccessor .PotentiallyConvertingIterator ;
3637import org .springframework .data .repository .query .parser .AbstractQueryCreator ;
3738import org .springframework .data .repository .query .parser .Part ;
39+ import org .springframework .data .repository .query .parser .Part .IgnoreCaseType ;
3840import org .springframework .data .repository .query .parser .Part .Type ;
3941import org .springframework .data .repository .query .parser .PartTree ;
4042import org .springframework .util .Assert ;
@@ -99,7 +101,7 @@ protected Criteria create(Part part, Iterator<Object> iterator) {
99101
100102 PersistentPropertyPath <MongoPersistentProperty > path = context .getPersistentPropertyPath (part .getProperty ());
101103 MongoPersistentProperty property = path .getLeafProperty ();
102- Criteria criteria = from (part . getType () , property ,
104+ Criteria criteria = from (part , property ,
103105 where (path .toDotPath (MongoPersistentProperty .PropertyToFieldNameConverter .INSTANCE )),
104106 (PotentiallyConvertingIterator ) iterator );
105107
@@ -120,7 +122,7 @@ protected Criteria and(Part part, Criteria base, Iterator<Object> iterator) {
120122 PersistentPropertyPath <MongoPersistentProperty > path = context .getPersistentPropertyPath (part .getProperty ());
121123 MongoPersistentProperty property = path .getLeafProperty ();
122124
123- return from (part . getType () , property ,
125+ return from (part , property ,
124126 base .and (path .toDotPath (MongoPersistentProperty .PropertyToFieldNameConverter .INSTANCE )),
125127 (PotentiallyConvertingIterator ) iterator );
126128 }
@@ -165,9 +167,11 @@ protected Query complete(Criteria criteria, Sort sort) {
165167 * @param parameters
166168 * @return
167169 */
168- private Criteria from (Type type , MongoPersistentProperty property , Criteria criteria ,
170+ private Criteria from (Part part , MongoPersistentProperty property , Criteria criteria ,
169171 PotentiallyConvertingIterator parameters ) {
170172
173+ Type type = part .getType ();
174+
171175 switch (type ) {
172176 case AFTER :
173177 case GREATER_THAN :
@@ -193,8 +197,7 @@ private Criteria from(Type type, MongoPersistentProperty property, Criteria crit
193197 case STARTING_WITH :
194198 case ENDING_WITH :
195199 case CONTAINING :
196- String value = parameters .next ().toString ();
197- return criteria .regex (toLikeRegex (value , type ));
200+ return addAppropriateLikeRegexTo (criteria , part , parameters .next ().toString ());
198201 case REGEX :
199202 return criteria .regex (parameters .next ().toString ());
200203 case EXISTS :
@@ -220,19 +223,103 @@ private Criteria from(Type type, MongoPersistentProperty property, Criteria crit
220223 criteria .maxDistance (distance .getNormalizedValue ());
221224 }
222225 return criteria ;
223-
224226 case WITHIN :
227+
225228 Object parameter = parameters .next ();
226229 return criteria .within ((Shape ) parameter );
227230 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+
229235 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 );
231239 default :
232240 throw new IllegalArgumentException ("Unsupported keyword!" );
233241 }
234242 }
235243
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+
236323 /**
237324 * Returns the next element from the given {@link Iterator} expecting it to be of a certain type.
238325 *
@@ -265,7 +352,9 @@ private Object[] nextAsArray(PotentiallyConvertingIterator iterator, MongoPersis
265352 return new Object [] { next };
266353 }
267354
268- private String toLikeRegex (String source , Type type ) {
355+ private String toLikeRegex (String source , Part part ) {
356+
357+ Type type = part .getType ();
269358
270359 switch (type ) {
271360 case STARTING_WITH :
@@ -277,6 +366,9 @@ private String toLikeRegex(String source, Type type) {
277366 case CONTAINING :
278367 source = "*" + source + "*" ;
279368 break ;
369+ case SIMPLE_PROPERTY :
370+ case NEGATING_SIMPLE_PROPERTY :
371+ source = "^" + source + "$" ;
280372 default :
281373 }
282374
0 commit comments