1515 */
1616package org .springframework .data .mongodb .core .index ;
1717
18+ import java .lang .annotation .Annotation ;
1819import java .time .Duration ;
1920import java .util .ArrayList ;
2021import java .util .Arrays ;
2324import java .util .HashSet ;
2425import java .util .Iterator ;
2526import java .util .List ;
27+ import java .util .Map ;
2628import java .util .Set ;
2729import java .util .concurrent .TimeUnit ;
30+ import java .util .function .Supplier ;
2831import java .util .stream .Collectors ;
2932
3033import org .apache .commons .logging .Log ;
3134import org .apache .commons .logging .LogFactory ;
32-
35+ import org . springframework . core . annotation . MergedAnnotation ;
3336import org .springframework .dao .InvalidDataAccessApiUsageException ;
3437import org .springframework .data .domain .Sort ;
3538import org .springframework .data .mapping .Association ;
5053import org .springframework .data .mongodb .core .query .Collation ;
5154import org .springframework .data .mongodb .util .BsonUtils ;
5255import org .springframework .data .mongodb .util .DotPath ;
56+ import org .springframework .data .mongodb .util .spel .ExpressionUtils ;
5357import org .springframework .data .spel .EvaluationContextProvider ;
5458import org .springframework .data .util .TypeInformation ;
5559import org .springframework .expression .EvaluationContext ;
56- import org .springframework .expression .Expression ;
57- import org .springframework .expression .ParserContext ;
58- import org .springframework .expression .common .LiteralExpression ;
5960import org .springframework .expression .spel .standard .SpelExpressionParser ;
6061import org .springframework .lang .Nullable ;
6162import org .springframework .util .Assert ;
@@ -454,10 +455,7 @@ protected IndexDefinitionHolder createCompoundIndexDefinition(String dotPath, St
454455 indexDefinition .partial (evaluatePartialFilter (index .partialFilter (), entity ));
455456 }
456457
457- if (StringUtils .hasText (index .collation ())) {
458- indexDefinition .collation (evaluateCollation (index .collation (), entity ));
459- }
460-
458+ indexDefinition .collation (resolveCollation (index , entity ));
461459 return new IndexDefinitionHolder (dotPath , indexDefinition , collection );
462460 }
463461
@@ -478,12 +476,7 @@ protected IndexDefinitionHolder createWildcardIndexDefinition(String dotPath, St
478476 indexDefinition .partial (evaluatePartialFilter (index .partialFilter (), entity ));
479477 }
480478
481- if (StringUtils .hasText (index .collation ())) {
482- indexDefinition .collation (evaluateCollation (index .collation (), entity ));
483- } else if (entity != null && entity .hasCollation ()) {
484- indexDefinition .collation (entity .getCollation ());
485- }
486-
479+ indexDefinition .collation (resolveCollation (index , entity ));
487480 return new IndexDefinitionHolder (dotPath , indexDefinition , collection );
488481 }
489482
@@ -498,7 +491,7 @@ private org.bson.Document resolveCompoundIndexKeyFromStringDefinition(String dot
498491 return new org .bson .Document (dotPath , 1 );
499492 }
500493
501- Object keyDefToUse = evaluate (keyDefinitionString , getEvaluationContextForProperty (entity ));
494+ Object keyDefToUse = ExpressionUtils . evaluate (keyDefinitionString , () -> getEvaluationContextForProperty (entity ));
502495
503496 org .bson .Document dbo = (keyDefToUse instanceof org .bson .Document ) ? (org .bson .Document ) keyDefToUse
504497 : org .bson .Document .parse (ObjectUtils .nullSafeToString (keyDefToUse ));
@@ -567,7 +560,7 @@ protected IndexDefinitionHolder createIndexDefinition(String dotPath, String col
567560 }
568561
569562 Duration timeout = computeIndexTimeout (index .expireAfter (),
570- getEvaluationContextForProperty (persistentProperty .getOwner ()));
563+ () -> getEvaluationContextForProperty (persistentProperty .getOwner ()));
571564 if (!timeout .isZero () && !timeout .isNegative ()) {
572565 indexDefinition .expire (timeout );
573566 }
@@ -577,16 +570,13 @@ protected IndexDefinitionHolder createIndexDefinition(String dotPath, String col
577570 indexDefinition .partial (evaluatePartialFilter (index .partialFilter (), persistentProperty .getOwner ()));
578571 }
579572
580- if (StringUtils .hasText (index .collation ())) {
581- indexDefinition .collation (evaluateCollation (index .collation (), persistentProperty .getOwner ()));
582- }
583-
573+ indexDefinition .collation (resolveCollation (index , persistentProperty .getOwner ()));
584574 return new IndexDefinitionHolder (dotPath , indexDefinition , collection );
585575 }
586576
587577 private PartialIndexFilter evaluatePartialFilter (String filterExpression , PersistentEntity <?, ?> entity ) {
588578
589- Object result = evaluate (filterExpression , getEvaluationContextForProperty (entity ));
579+ Object result = ExpressionUtils . evaluate (filterExpression , () -> getEvaluationContextForProperty (entity ));
590580
591581 if (result instanceof org .bson .Document ) {
592582 return PartialIndexFilter .of ((org .bson .Document ) result );
@@ -597,7 +587,7 @@ private PartialIndexFilter evaluatePartialFilter(String filterExpression, Persis
597587
598588 private org .bson .Document evaluateWildcardProjection (String projectionExpression , PersistentEntity <?, ?> entity ) {
599589
600- Object result = evaluate (projectionExpression , getEvaluationContextForProperty (entity ));
590+ Object result = ExpressionUtils . evaluate (projectionExpression , () -> getEvaluationContextForProperty (entity ));
601591
602592 if (result instanceof org .bson .Document ) {
603593 return (org .bson .Document ) result ;
@@ -608,7 +598,7 @@ private org.bson.Document evaluateWildcardProjection(String projectionExpression
608598
609599 private Collation evaluateCollation (String collationExpression , PersistentEntity <?, ?> entity ) {
610600
611- Object result = evaluate (collationExpression , getEvaluationContextForProperty (entity ));
601+ Object result = ExpressionUtils . evaluate (collationExpression , () -> getEvaluationContextForProperty (entity ));
612602 if (result instanceof org .bson .Document ) {
613603 return Collation .from ((org .bson .Document ) result );
614604 }
@@ -618,6 +608,9 @@ private Collation evaluateCollation(String collationExpression, PersistentEntity
618608 if (result instanceof String ) {
619609 return Collation .parse (result .toString ());
620610 }
611+ if (result instanceof Map ) {
612+ return Collation .from (new org .bson .Document ((Map <String , ?>) result ));
613+ }
621614 throw new IllegalStateException ("Cannot parse collation " + result );
622615
623616 }
@@ -726,7 +719,7 @@ private String pathAwareIndexName(String indexName, String dotPath, @Nullable Pe
726719 String nameToUse = "" ;
727720 if (StringUtils .hasText (indexName )) {
728721
729- Object result = evaluate (indexName , getEvaluationContextForProperty (entity ));
722+ Object result = ExpressionUtils . evaluate (indexName , () -> getEvaluationContextForProperty (entity ));
730723
731724 if (result != null ) {
732725 nameToUse = ObjectUtils .nullSafeToString (result );
@@ -787,9 +780,9 @@ private void resolveAndAddIndexesForAssociation(Association<MongoPersistentPrope
787780 * @since 2.2
788781 * @throws IllegalArgumentException for invalid duration values.
789782 */
790- private static Duration computeIndexTimeout (String timeoutValue , EvaluationContext evaluationContext ) {
783+ private static Duration computeIndexTimeout (String timeoutValue , Supplier < EvaluationContext > evaluationContext ) {
791784
792- Object evaluatedTimeout = evaluate (timeoutValue , evaluationContext );
785+ Object evaluatedTimeout = ExpressionUtils . evaluate (timeoutValue , evaluationContext );
793786
794787 if (evaluatedTimeout == null ) {
795788 return Duration .ZERO ;
@@ -808,15 +801,25 @@ private static Duration computeIndexTimeout(String timeoutValue, EvaluationConte
808801 return DurationStyle .detectAndParse (val );
809802 }
810803
804+ /**
805+ * Resolve the "collation" attribute from a given {@link Annotation} if present.
806+ *
807+ * @param annotation
808+ * @param entity
809+ * @return the collation present on either the annotation or the entity as a fallback. Might be {@literal null}.
810+ * @since 4.0
811+ */
811812 @ Nullable
812- private static Object evaluate (String value , EvaluationContext evaluationContext ) {
813+ private Collation resolveCollation (Annotation annotation , @ Nullable PersistentEntity <?, ?> entity ) {
814+ return MergedAnnotation .from (annotation ).getValue ("collation" , String .class ).filter (StringUtils ::hasText )
815+ .map (it -> evaluateCollation (it , entity )).orElseGet (() -> {
813816
814- Expression expression = PARSER . parseExpression ( value , ParserContext . TEMPLATE_EXPRESSION );
815- if ( expression instanceof LiteralExpression ) {
816- return value ;
817- }
818-
819- return expression . getValue ( evaluationContext , Object . class );
817+ if ( entity instanceof MongoPersistentEntity <?> mongoPersistentEntity
818+ && mongoPersistentEntity . hasCollation () ) {
819+ return mongoPersistentEntity . getCollation () ;
820+ }
821+ return null ;
822+ } );
820823 }
821824
822825 private static boolean isMapWithoutWildcardIndex (MongoPersistentProperty property ) {
0 commit comments