Browse Source

Add support for property placeholder in expireAfter for MongoDB indexes.

Closes: #4980
Original Pull Request: #5049

Signed-off-by: Sangbeen Moon <jjabi22@naver.com>
issue/aot-doc
Sangbeen Moon 4 months ago committed by Christoph Strobl
parent
commit
7fd979296c
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 35
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java
  2. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java
  3. 24
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java

35
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java

@ -27,7 +27,6 @@ import java.util.List; @@ -27,7 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
@ -35,9 +34,11 @@ import org.apache.commons.logging.LogFactory; @@ -35,9 +34,11 @@ import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.core.TypeInformation;
import org.springframework.data.domain.Sort;
import org.springframework.data.expression.ValueEvaluationContext;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.MappingException;
@ -78,6 +79,7 @@ import org.springframework.util.StringUtils; @@ -78,6 +79,7 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @author Dave Perryman
* @author Stefan Tirea
* @author Sangbeen Moon
* @since 1.5
*/
public class MongoPersistentEntityIndexResolver implements IndexResolver {
@ -493,7 +495,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -493,7 +495,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
return new org.bson.Document(dotPath, 1);
}
Object keyDefToUse = ExpressionUtils.evaluate(keyDefinitionString, () -> getEvaluationContextForProperty(entity));
Object keyDefToUse = ExpressionUtils.evaluate(keyDefinitionString, () -> getValueEvaluationContextForProperty(entity));
org.bson.Document dbo = (keyDefToUse instanceof org.bson.Document document) ? document
: org.bson.Document.parse(ObjectUtils.nullSafeToString(keyDefToUse));
@ -561,7 +563,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -561,7 +563,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
}
Duration timeout = computeIndexTimeout(index.expireAfter(),
() -> getEvaluationContextForProperty(persistentProperty.getOwner()));
getValueEvaluationContextForProperty(persistentProperty.getOwner()));
if (!timeout.isNegative()) {
indexDefinition.expire(timeout);
}
@ -577,7 +579,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -577,7 +579,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
private PartialIndexFilter evaluatePartialFilter(String filterExpression, @Nullable PersistentEntity<?, ?> entity) {
Object result = ExpressionUtils.evaluate(filterExpression, () -> getEvaluationContextForProperty(entity));
Object result = ExpressionUtils.evaluate(filterExpression, () -> getValueEvaluationContextForProperty(entity));
if (result instanceof org.bson.Document document) {
return PartialIndexFilter.of(document);
@ -588,7 +590,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -588,7 +590,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
private org.bson.Document evaluateWildcardProjection(String projectionExpression, @Nullable PersistentEntity<?, ?> entity) {
Object result = ExpressionUtils.evaluate(projectionExpression, () -> getEvaluationContextForProperty(entity));
Object result = ExpressionUtils.evaluate(projectionExpression, () -> getValueEvaluationContextForProperty(entity));
if (result instanceof org.bson.Document document) {
return document;
@ -599,7 +601,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -599,7 +601,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
private Collation evaluateCollation(String collationExpression, @Nullable PersistentEntity<?, ?> entity) {
Object result = ExpressionUtils.evaluate(collationExpression, () -> getEvaluationContextForProperty(entity));
Object result = ExpressionUtils.evaluate(collationExpression, () -> getValueEvaluationContextForProperty(entity));
if (result instanceof org.bson.Document document) {
return Collation.from(document);
}
@ -650,24 +652,19 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -650,24 +652,19 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
}
/**
* Get the {@link EvaluationContext} for a given {@link PersistentEntity entity} the default one.
* Get the {@link ValueEvaluationContext} for a given {@link PersistentEntity entity} the default one.
*
* @param persistentEntity can be {@literal null}
* @return
*/
private EvaluationContext getEvaluationContextForProperty(@Nullable PersistentEntity<?, ?> persistentEntity) {
private ValueEvaluationContext getValueEvaluationContextForProperty(@Nullable PersistentEntity<?, ?> persistentEntity) {
if (persistentEntity == null || !(persistentEntity instanceof BasicMongoPersistentEntity)) {
return getEvaluationContext();
if (persistentEntity instanceof BasicMongoPersistentEntity<?> mongoEntity) {
return mongoEntity.getValueEvaluationContext(null);
}
EvaluationContext contextFromEntity = ((BasicMongoPersistentEntity<?>) persistentEntity).getEvaluationContext(null);
if (contextFromEntity != null && !EvaluationContextProvider.DEFAULT.equals(contextFromEntity)) {
return contextFromEntity;
}
return getEvaluationContext();
return ValueEvaluationContext.of(
new StandardEnvironment(), getEvaluationContext());
}
/**
@ -719,7 +716,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -719,7 +716,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
String nameToUse = "";
if (StringUtils.hasText(indexName)) {
Object result = ExpressionUtils.evaluate(indexName, () -> getEvaluationContextForProperty(entity));
Object result = ExpressionUtils.evaluate(indexName, () -> getValueEvaluationContextForProperty(entity));
if (result != null) {
nameToUse = ObjectUtils.nullSafeToString(result);
@ -780,7 +777,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -780,7 +777,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
* @since 2.2
* @throws IllegalArgumentException for invalid duration values.
*/
private static Duration computeIndexTimeout(String timeoutValue, Supplier<EvaluationContext> evaluationContext) {
private static Duration computeIndexTimeout(String timeoutValue, ValueEvaluationContext evaluationContext) {
return DurationUtil.evaluate(timeoutValue, evaluationContext);
}

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java

@ -18,6 +18,7 @@ package org.springframework.data.mongodb.util.spel; @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.util.spel;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.springframework.data.expression.ValueEvaluationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
@ -52,7 +53,7 @@ public final class ExpressionUtils { @@ -52,7 +53,7 @@ public final class ExpressionUtils {
return expression instanceof LiteralExpression ? null : expression;
}
public static @Nullable Object evaluate(String value, Supplier<EvaluationContext> evaluationContext) {
public static @Nullable Object evaluate(String value, Supplier<ValueEvaluationContext> evaluationContext) {
Expression expression = detectExpression(value);
if (expression == null) {

24
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java

@ -33,6 +33,8 @@ import org.junit.runners.Suite; @@ -33,6 +33,8 @@ import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
import org.springframework.data.core.TypeInformation;
@ -106,6 +108,23 @@ public class MongoPersistentEntityIndexResolverUnitTests { @@ -106,6 +108,23 @@ public class MongoPersistentEntityIndexResolverUnitTests {
assertThat(definitions).isNotEmpty();
}
@Test // GH-4980
public void shouldSupportPropertyPlaceholderInExpireAfter() {
StandardEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addFirst(new MapPropertySource("test", Map.of("ttl.timeout", "10m")));
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setEnvironment(environment);
MongoPersistentEntityIndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext);
List<IndexDefinitionHolder> indexDefinitions = (List<IndexDefinitionHolder>) resolver
.resolveIndexFor(WithExpireAfterAsPropertyPlaceholder.class);
assertThat(indexDefinitions).hasSize(1);
assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 600L);
}
@Test // DATAMONGO-899
public void deeplyNestedIndexPathIsResolvedCorrectly() {
@ -432,6 +451,11 @@ public class MongoPersistentEntityIndexResolverUnitTests { @@ -432,6 +451,11 @@ public class MongoPersistentEntityIndexResolverUnitTests {
class WithPartialFilter {
@Indexed(partialFilter = "{'value': {'$exists': true}}") String withPartialFilter;
}
@Document
class WithExpireAfterAsPropertyPlaceholder {
@Indexed(expireAfter = "${ttl.timeout}") String withTimeout;
}
}
@Target({ ElementType.FIELD })

Loading…
Cancel
Save