Browse Source

Allow single-element contains for derived AOT queries.

Closes #5123
Original pull request: #5124
pull/5151/head
Christoph Strobl 2 months ago committed by Mark Paluch
parent
commit
1ebf056232
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 33
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotPlaceholders.java
  2. 25
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotQueryCreator.java
  3. 20
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotStringQuery.java
  4. 4
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/QueryBlocks.java
  5. 22
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
  6. 3
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

33
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotPlaceholders.java

@ -111,6 +111,26 @@ class AotPlaceholders { @@ -111,6 +111,26 @@ class AotPlaceholders {
return new RegexPlaceholder(index, options);
}
/**
* Create a placeholder that indicates the value should be treated as list.
*
* @param index zero-based index referring to the bindable method parameter.
* @return new instance of {@link Placeholder}.
*/
static Placeholder asList(int index) {
return asList(indexed(index));
}
/**
* Create a placeholder that indicates the wrapped placeholder should be treated as list.
*
* @param source the target placeholder
* @return new instance of {@link Placeholder}.
*/
static Placeholder asList(Placeholder source) {
return new AsListPlaceholder(source);
}
/**
* A placeholder expression used when rending queries to JSON.
*
@ -295,4 +315,17 @@ class AotPlaceholders { @@ -295,4 +315,17 @@ class AotPlaceholders {
}
}
record AsListPlaceholder(Placeholder placeholder) implements Placeholder {
@Override
public String toString() {
return getValue();
}
@Override
public String getValue() {
return "[" + placeholder.getValue() + "]";
}
}
}

25
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotQueryCreator.java

@ -24,7 +24,6 @@ import java.util.regex.Pattern; @@ -24,7 +24,6 @@ import java.util.regex.Pattern;
import org.bson.conversions.Bson;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.data.core.TypeInformation;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
@ -48,6 +47,7 @@ import org.springframework.data.mongodb.core.query.Query; @@ -48,6 +47,7 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.repository.VectorSearch;
import org.springframework.data.mongodb.repository.aot.AotPlaceholders.AsListPlaceholder;
import org.springframework.data.mongodb.repository.aot.AotPlaceholders.Placeholder;
import org.springframework.data.mongodb.repository.aot.AotPlaceholders.RegexPlaceholder;
import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor;
@ -132,6 +132,10 @@ record AotQueryCreator(MappingContext<?, MongoPersistentProperty> mappingContext @@ -132,6 +132,10 @@ record AotQueryCreator(MappingContext<?, MongoPersistentProperty> mappingContext
return criteria.raw("$regex", param);
}
if (param instanceof AsListPlaceholder asListPlaceholder && !property.isCollectionLike()) {
return super.createContainingCriteria(part, property, criteria, asListPlaceholder.placeholder());
}
return super.createContainingCriteria(part, property, criteria, param);
}
}
@ -226,7 +230,24 @@ record AotQueryCreator(MappingContext<?, MongoPersistentProperty> mappingContext @@ -226,7 +230,24 @@ record AotQueryCreator(MappingContext<?, MongoPersistentProperty> mappingContext
partForIndex.shouldIgnoreCase().equals(IgnoreCaseType.ALWAYS)
|| partForIndex.shouldIgnoreCase().equals(IgnoreCaseType.WHEN_POSSIBLE) ? "i"
: null));
} else {
} else if (partForIndex != null && (partForIndex.getType().equals(Type.IN)
|| partForIndex.getType().equals(Type.NOT_IN) || partForIndex.getType().equals(Type.CONTAINING)
|| partForIndex.getType().equals(Type.NOT_CONTAINING))) {
if (partForIndex.getProperty().isCollection()
&& !TypeInformation.of(parameter.getType()).isCollectionLike()) {
if (partForIndex.shouldIgnoreCase().equals(IgnoreCaseType.ALWAYS)) {
placeholders.add(parameter.getIndex(),
AotPlaceholders.asList(AotPlaceholders.regex(parameter.getIndex(), "i")));
} else {
placeholders.add(parameter.getIndex(), AotPlaceholders.asList(parameter.getIndex()));
}
} else {
placeholders.add(parameter.getIndex(), AotPlaceholders.indexed(parameter.getIndex()));
}
}
else {
placeholders.add(parameter.getIndex(), AotPlaceholders.indexed(parameter.getIndex()));
}
}

20
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotStringQuery.java

@ -22,12 +22,12 @@ import java.util.Set; @@ -22,12 +22,12 @@ import java.util.Set;
import org.bson.Document;
import org.jspecify.annotations.Nullable;
import org.springframework.data.domain.KeysetScrollPosition;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Meta;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.aot.AotPlaceholders.AsListPlaceholder;
import org.springframework.data.mongodb.repository.aot.AotPlaceholders.RegexPlaceholder;
import org.springframework.util.StringUtils;
@ -83,7 +83,7 @@ class AotStringQuery extends Query { @@ -83,7 +83,7 @@ class AotStringQuery extends Query {
return false;
}
return this.placeholders.get(index) instanceof RegexPlaceholder;
return obtainAndPotentiallyUnwrapPlaceholder(index) instanceof RegexPlaceholder;
}
@Nullable
@ -92,7 +92,21 @@ class AotStringQuery extends Query { @@ -92,7 +92,21 @@ class AotStringQuery extends Query {
return null;
}
return this.placeholders.get(index) instanceof RegexPlaceholder rgp ? rgp.regexOptions() : null;
Object placeholderValue = obtainAndPotentiallyUnwrapPlaceholder(index);
return placeholderValue instanceof RegexPlaceholder rgp ? rgp.regexOptions() : null;
}
@Nullable Object obtainAndPotentiallyUnwrapPlaceholder(int index) {
if (this.placeholders.isEmpty()) {
return null;
}
Object placeholerValue = this.placeholders.get(index);
if (placeholerValue instanceof AsListPlaceholder asListPlaceholder) {
placeholerValue = asListPlaceholder.placeholder();
}
return placeholerValue;
}
@Override

4
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/QueryBlocks.java

@ -195,9 +195,9 @@ class QueryBlocks { @@ -195,9 +195,9 @@ class QueryBlocks {
String regexOptions = source.getQuery().getRegexOptions(i);
if (StringUtils.hasText(regexOptions)) {
formatted.add(CodeBlock.of("toRegex($L)", parameterName));
} else {
formatted.add(CodeBlock.of("toRegex($L, $S)", parameterName, regexOptions));
} else {
formatted.add(CodeBlock.of("toRegex($L)", parameterName));
}
} else {
formatted.add(CodeBlock.of("$L", parameterName));

22
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

@ -1341,6 +1341,20 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie @@ -1341,6 +1341,20 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie
assertThat(result).hasSize(1).contains(carter);
}
@Test // GH-5123
void findBySkillsContainsSingleElement() {
List<Person> result = repository.findBySkillsContains("Drums");
assertThat(result).hasSize(1).contains(carter);
}
@Test // GH-5123
void findBySkillsContainsSingleElementWithIgnoreCase() {
List<Person> result = repository.findBySkillsContainsIgnoreCase("drums");
assertThat(result).hasSize(1).contains(carter);
}
@Test // DATAMONGO-1425
void findBySkillsNotContains() {
@ -1349,6 +1363,14 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie @@ -1349,6 +1363,14 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie
assertThat(result).doesNotContain(carter);
}
@Test // GH-5123
void findBySkillsNotContainsSingleElement() {
List<Person> result = repository.findBySkillsNotContains("Drums");
assertThat(result).hasSize((int) (repository.count() - 1));
assertThat(result).doesNotContain(carter);
}
@Test // DATAMONGO-1424
void findsPersonsByFirstnameNotLike() {

3
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

@ -117,8 +117,11 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query @@ -117,8 +117,11 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
List<Person> findByFirstnameLikeOrderByLastnameAsc(Pattern firstname, Sort sort);
List<Person> findBySkillsContains(List<String> skills);
List<Person> findBySkillsContains(String skill);
List<Person> findBySkillsContainsIgnoreCase(String skill);
List<Person> findBySkillsNotContains(List<String> skills);
List<Person> findBySkillsNotContains(String skill);
@Query("{'age' : { '$lt' : ?0 } }")
List<Person> findByAgeLessThan(int age, Sort sort);

Loading…
Cancel
Save