From f140f3ba9abe1a175f3b07271272ee5cb8fd0c2f Mon Sep 17 00:00:00 2001 From: "gyungrai.wang" <003213@naver.com> Date: Sat, 23 Sep 2023 21:37:17 +0900 Subject: [PATCH] Fix string to objectId mapping error when using query method. --- .../repository/query/MongoQueryCreator.java | 83 +++++++--- .../repository/query/PartTreeMongoQuery.java | 7 +- .../query/ReactivePartTreeMongoQuery.java | 6 +- .../query/MongoQueryCreatorUnitTests.java | 142 +++++++++++------- 4 files changed, 157 insertions(+), 81 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java index 1784700b5f..e284dc3124 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java @@ -17,15 +17,19 @@ import static org.springframework.data.mongodb.core.query.Criteria.*; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Optional; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.BsonRegularExpression; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.data.domain.Range; import org.springframework.data.domain.Range.Bound; import org.springframework.data.domain.Sort; @@ -64,6 +68,7 @@ * @author Thomas Darimont * @author Christoph Strobl * @author Edward Prentice + * @author Gyungrai Wang */ class MongoQueryCreator extends AbstractQueryCreator { @@ -72,6 +77,7 @@ class MongoQueryCreator extends AbstractQueryCreator { private final MongoParameterAccessor accessor; private final MappingContext context; private final boolean isGeoNearQuery; + private final ConversionService conversionService; /** * Creates a new {@link MongoQueryCreator} from the given {@link PartTree}, {@link ConvertingParameterAccessor} and @@ -82,8 +88,8 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param context */ public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, - MappingContext context) { - this(tree, accessor, context, false); + MappingContext context, ConversionService conversionService) { + this(tree, accessor, context, false, conversionService); } /** @@ -96,7 +102,7 @@ public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, * @param isGeoNearQuery */ public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, - MappingContext context, boolean isGeoNearQuery) { + MappingContext context, boolean isGeoNearQuery, ConversionService conversionService) { super(tree, accessor); @@ -105,6 +111,7 @@ public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, this.accessor = accessor; this.isGeoNearQuery = isGeoNearQuery; this.context = context; + this.conversionService = conversionService; } @Override @@ -168,24 +175,24 @@ private Criteria from(Part part, MongoPersistentProperty property, Criteria crit switch (type) { case AFTER: case GREATER_THAN: - return criteria.gt(parameters.next()); + return criteria.gt(next(parameters, property.getFieldType())); case GREATER_THAN_EQUAL: - return criteria.gte(parameters.next()); + return criteria.gte(next(parameters, property.getFieldType())); case BEFORE: case LESS_THAN: - return criteria.lt(parameters.next()); + return criteria.lt(next(parameters, property.getFieldType())); case LESS_THAN_EQUAL: - return criteria.lte(parameters.next()); + return criteria.lte(next(parameters, property.getFieldType())); case BETWEEN: - return computeBetweenPart(criteria, parameters); + return computeBetweenPart(criteria, parameters, property); case IS_NOT_NULL: return criteria.ne(null); case IS_NULL: return criteria.is(null); case NOT_IN: - return criteria.nin(nextAsList(parameters, part)); + return criteria.nin(nextAsList(parameters, part, property)); case IN: - return criteria.in(nextAsList(parameters, part)); + return criteria.in(nextAsList(parameters, part, property)); case LIKE: case STARTING_WITH: case ENDING_WITH: @@ -200,7 +207,7 @@ private Criteria from(Part part, MongoPersistentProperty property, Criteria crit Object param = parameters.next(); return param instanceof Pattern pattern ? criteria.regex(pattern) : criteria.regex(param.toString()); case EXISTS: - return criteria.exists((Boolean) parameters.next()); + return criteria.exists((Boolean) next(parameters, property.getFieldType())); case TRUE: return criteria.is(true); case FALSE: @@ -212,7 +219,7 @@ private Criteria from(Part part, MongoPersistentProperty property, Criteria crit Optional minDistance = range.getLowerBound().getValue(); Point point = accessor.getGeoNearLocation(); - Point pointToUse = point == null ? nextAs(parameters, Point.class) : point; + Point pointToUse = point == null ? nextAs(parameters, Point.class, property) : point; boolean isSpherical = isSpherical(property); @@ -239,22 +246,48 @@ private Criteria from(Part part, MongoPersistentProperty property, Criteria crit case WITHIN: - Object parameter = parameters.next(); + Object parameter = next(parameters, property.getFieldType()); return criteria.within((Shape) parameter); case SIMPLE_PROPERTY: - return isSimpleComparisionPossible(part) ? criteria.is(parameters.next()) + return isSimpleComparisionPossible(part) ? criteria.is(next(parameters, property.getFieldType())) : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, false); case NEGATING_SIMPLE_PROPERTY: - return isSimpleComparisionPossible(part) ? criteria.ne(parameters.next()) + return isSimpleComparisionPossible(part) ? criteria.ne(next(parameters, property.getFieldType())) : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, true); default: throw new IllegalArgumentException("Unsupported keyword"); } } + private Object nextAsList(Iterator parameters, Class targetType) { + Object nextValue = parameters.next(); + + if(nextValue instanceof Iterable) { + List convertedList = new ArrayList<>(); + for(Object item : (Iterable) nextValue) { + convertedList.add(convert(item, targetType)); + } + return convertedList; + } + + return nextValue; + } + + private Object next(Iterator parameters, Class targetType) { + return convert(parameters.next(), targetType); + } + + private Object convert(Object value, Class targetType) { + if(conversionService.canConvert(TypeDescriptor.forObject(value), TypeDescriptor.valueOf(targetType))) { + return conversionService.convert(value, targetType); + } + return value; + } + + private boolean isSimpleComparisionPossible(Part part) { switch (part.shouldIgnoreCase()) { @@ -299,7 +332,7 @@ private Criteria createLikeRegexCriteriaOrThrow(Part part, MongoPersistentProper criteria = criteria.not(); } - return addAppropriateLikeRegexTo(criteria, part, parameters.next()); + return addAppropriateLikeRegexTo(criteria, part, next(parameters, property.getFieldType())); case NEVER: // intentional no-op @@ -324,10 +357,10 @@ private Criteria createContainingCriteria(Part part, MongoPersistentProperty pro Iterator parameters) { if (property.isCollectionLike()) { - return criteria.in(nextAsList(parameters, part)); + return criteria.in(nextAsList(parameters, part, property)); } - return addAppropriateLikeRegexTo(criteria, part, parameters.next()); + return addAppropriateLikeRegexTo(criteria, part, next(parameters, property.getFieldType())); } /** @@ -376,9 +409,9 @@ private String toRegexOptions(Part part) { * @return */ @SuppressWarnings("unchecked") - private T nextAs(Iterator iterator, Class type) { + private T nextAs(Iterator iterator, Class type, MongoPersistentProperty property) { - Object parameter = iterator.next(); + Object parameter = next(iterator, property.getFieldType()); if (ClassUtils.isAssignable(type, parameter.getClass())) { return (T) parameter; @@ -388,9 +421,9 @@ private T nextAs(Iterator iterator, Class type) { String.format("Expected parameter type of %s but got %s", type, parameter.getClass())); } - private java.util.List nextAsList(Iterator iterator, Part part) { + private java.util.List nextAsList(Iterator iterator, Part part, MongoPersistentProperty property) { - Streamable streamable = asStreamable(iterator.next()); + Streamable streamable = asStreamable(nextAsList(iterator, property.getFieldType())); if (!isSimpleComparisionPossible(part)) { MatchMode matchMode = toMatchMode(part.getType()); @@ -446,11 +479,11 @@ private boolean isSpherical(MongoPersistentProperty property) { * @return * @since 2.2 */ - private static Criteria computeBetweenPart(Criteria criteria, Iterator parameters) { + private Criteria computeBetweenPart(Criteria criteria, Iterator parameters, MongoPersistentProperty property) { - Object value = parameters.next(); + Object value = next(parameters, property.getFieldType()); if (!(value instanceof Range range)) { - return criteria.gt(value).lt(parameters.next()); + return criteria.gt(value).lt(next(parameters, property.getFieldType())); } Optional min = range.getLowerBound().getValue(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java index d470d07ec7..a29f84f38c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java @@ -18,6 +18,7 @@ import org.bson.Document; import org.bson.json.JsonParseException; +import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.MongoTemplate; @@ -49,6 +50,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { private final boolean isGeoNearQuery; private final MappingContext context; private final ResultProcessor processor; + private final ConversionService conversionService; /** * Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. @@ -67,6 +69,7 @@ public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperatio this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); this.isGeoNearQuery = method.isGeoNearQuery(); this.context = mongoOperations.getConverter().getMappingContext(); + this.conversionService = mongoOperations.getConverter().getConversionService(); } /** @@ -81,7 +84,7 @@ public PartTree getTree() { @Override protected Query createQuery(ConvertingParameterAccessor accessor) { - MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery); + MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery, conversionService); Query query = creator.createQuery(); if (tree.isLimiting()) { @@ -126,7 +129,7 @@ protected Query createQuery(ConvertingParameterAccessor accessor) { @Override protected Query createCountQuery(ConvertingParameterAccessor accessor) { - return new MongoQueryCreator(tree, accessor, context, false).createQuery(); + return new MongoQueryCreator(tree, accessor, context, false, conversionService).createQuery(); } @Override diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java index fcd4e09932..32cc581a31 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; +import org.springframework.core.convert.ConversionService; import reactor.core.publisher.Mono; import org.bson.Document; @@ -49,6 +50,8 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { private final MappingContext context; private final ResultProcessor processor; + private final ConversionService conversionService; + /** * Creates a new {@link ReactivePartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. * @@ -66,6 +69,7 @@ public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongo this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); this.isGeoNearQuery = method.isGeoNearQuery(); this.context = mongoOperations.getConverter().getMappingContext(); + this.conversionService = mongoOperations.getConverter().getConversionService(); } /** @@ -89,7 +93,7 @@ protected Mono createCountQuery(ConvertingParameterAccessor accessor) { private Query createQueryInternal(ConvertingParameterAccessor accessor, boolean isCountQuery) { - MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isCountQuery ? false : isGeoNearQuery); + MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isCountQuery ? false : isGeoNearQuery, conversionService); Query query = creator.createQuery(); if (isCountQuery) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java index e24cfc7a8d..50c30db387 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java @@ -29,6 +29,7 @@ import org.bson.types.ObjectId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.core.convert.ConversionService; import org.springframework.data.domain.Range; import org.springframework.data.domain.Range.Bound; import org.springframework.data.geo.Distance; @@ -48,6 +49,8 @@ import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.Field; +import org.springframework.data.mongodb.core.mapping.FieldType; +import org.springframework.data.mongodb.core.mapping.MongoId; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; @@ -64,17 +67,40 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Gyungrai Wang */ class MongoQueryCreatorUnitTests { private MappingContext, MongoPersistentProperty> context; private MongoConverter converter; + private ConversionService conversionService; @BeforeEach void beforeEach() { context = new MongoMappingContext(); converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, context); + conversionService = converter.getConversionService(); + } + + @Test // ISSUE-4490 + void shouldCreateQueryWithObjectIdAccordingToMongoIdAnnotation() { + PartTree tree = new PartTree("findByIdGreaterThan", CollectionWithObjectIdTypeId.class); + + String criteriaId = "000000000000000000000000"; + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, criteriaId), context, conversionService); + Query query = creator.createQuery(); + assertThat(query).isEqualTo(query(where("id").gt(new ObjectId(criteriaId)))); + } + + @Test // ISSUE-4490 + void shouldCreateQueryWithStringIdAccordingToMongoIdAnnotation() { + PartTree tree = new PartTree("findByIdGreaterThan", CollectionWithStringTypeId.class); + + String criteriaId = "000000000000000000000000"; + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, criteriaId), context, conversionService); + Query query = creator.createQuery(); + assertThat(query).isEqualTo(query(where("id").gt(criteriaId))); } @Test @@ -82,7 +108,7 @@ void createsQueryCorrectly() { PartTree tree = new PartTree("findByFirstName", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Oliver"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Oliver"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName").is("Oliver"))); } @@ -92,7 +118,7 @@ void createsAndQueryCorrectly() { Person person = new Person(); MongoQueryCreator creator = new MongoQueryCreator(new PartTree("findByFirstNameAndFriend", Person.class), - getAccessor(converter, "Oliver", person), context); + getAccessor(converter, "Oliver", person), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName").is("Oliver").and("friend").is(person))); @@ -102,7 +128,7 @@ void createsAndQueryCorrectly() { void createsNotNullQueryCorrectly() { PartTree tree = new PartTree("findByFirstNameNotNull", Person.class); - Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery(); + Query query = new MongoQueryCreator(tree, getAccessor(converter), context, conversionService).createQuery(); assertThat(query).isEqualTo(new Query(Criteria.where("firstName").ne(null))); } @@ -111,7 +137,7 @@ void createsNotNullQueryCorrectly() { void createsIsNullQueryCorrectly() { PartTree tree = new PartTree("findByFirstNameIsNull", Person.class); - Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery(); + Query query = new MongoQueryCreator(tree, getAccessor(converter), context, conversionService).createQuery(); assertThat(query).isEqualTo(new Query(Criteria.where("firstName").is(null))); } @@ -142,7 +168,7 @@ void bindsDistanceParameterToNearCorrectly() throws Exception { void createsLessThanEqualQueryCorrectly() { PartTree tree = new PartTree("findByAgeLessThanEqual", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context, conversionService); Query reference = query(where("age").lte(18)); assertThat(creator.createQuery()).isEqualTo(reference); @@ -152,7 +178,7 @@ void createsLessThanEqualQueryCorrectly() { void createsGreaterThanEqualQueryCorrectly() { PartTree tree = new PartTree("findByAgeGreaterThanEqual", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context, conversionService); Query reference = query(where("age").gte(18)); assertThat(creator.createQuery()).isEqualTo(reference); @@ -162,7 +188,7 @@ void createsGreaterThanEqualQueryCorrectly() { void createsExistsClauseCorrectly() { PartTree tree = new PartTree("findByAgeExists", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, true), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, true), context, conversionService); Query query = query(where("age").exists(true)); assertThat(creator.createQuery()).isEqualTo(query); } @@ -171,7 +197,7 @@ void createsExistsClauseCorrectly() { void createsRegexClauseCorrectly() { PartTree tree = new PartTree("findByFirstNameRegex", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, ".*"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, ".*"), context, conversionService); Query query = query(where("firstName").regex(".*")); assertThat(creator.createQuery()).isEqualTo(query); } @@ -180,7 +206,7 @@ void createsRegexClauseCorrectly() { void createsTrueClauseCorrectly() { PartTree tree = new PartTree("findByActiveTrue", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context, conversionService); Query query = query(where("active").is(true)); assertThat(creator.createQuery()).isEqualTo(query); } @@ -189,7 +215,7 @@ void createsTrueClauseCorrectly() { void createsFalseClauseCorrectly() { PartTree tree = new PartTree("findByActiveFalse", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context, conversionService); Query query = query(where("active").is(false)); assertThat(creator.createQuery()).isEqualTo(query); } @@ -198,7 +224,7 @@ void createsFalseClauseCorrectly() { void createsOrQueryCorrectly() { PartTree tree = new PartTree("findByFirstNameOrAge", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Dave", 42), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Dave", 42), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(new Criteria().orOperator(where("firstName").is("Dave"), where("age").is(42)))); @@ -211,7 +237,7 @@ void createsQueryReferencingADBRefCorrectly() { user.id = new ObjectId(); PartTree tree = new PartTree("findByCreator", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, user), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, user), context, conversionService); Document queryObject = creator.createQuery().getQueryObject(); assertThat(queryObject.get("creator")).isEqualTo(user); @@ -221,7 +247,7 @@ void createsQueryReferencingADBRefCorrectly() { void createsQueryWithStartingWithPredicateCorrectly() { PartTree tree = new PartTree("findByUsernameStartingWith", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Matt"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Matt"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("username").regex("^Matt"))); @@ -231,7 +257,7 @@ void createsQueryWithStartingWithPredicateCorrectly() { void createsQueryWithEndingWithPredicateCorrectly() { PartTree tree = new PartTree("findByUsernameEndingWith", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "ews"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "ews"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("username").regex("ews$"))); @@ -241,7 +267,7 @@ void createsQueryWithEndingWithPredicateCorrectly() { void createsQueryWithContainingPredicateCorrectly() { PartTree tree = new PartTree("findByUsernameContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("username").regex(".*thew.*"))); @@ -258,7 +284,7 @@ private void assertBindsDistanceToQuery(Point point, Distance distance, Query re MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] { point, distance, "Dave" }); - Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor), context) + Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor), context, conversionService) .createQuery(); assertThat(query).isEqualTo(query); } @@ -267,7 +293,7 @@ private void assertBindsDistanceToQuery(Point point, Distance distance, Query re void createsQueryWithFindByIgnoreCaseCorrectly() { PartTree tree = new PartTree("findByfirstNameIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName").regex("^dave$", "i"))); @@ -277,7 +303,7 @@ void createsQueryWithFindByIgnoreCaseCorrectly() { void createsQueryWithFindByInClauseHavingIgnoreCaseCorrectly() { PartTree tree = new PartTree("findAllByFirstNameInIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, List.of("da've", "carter")), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, List.of("da've", "carter")), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName") @@ -288,7 +314,7 @@ void createsQueryWithFindByInClauseHavingIgnoreCaseCorrectly() { void createsQueryWithFindByNotIgnoreCaseCorrectly() { PartTree tree = new PartTree("findByFirstNameNotIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context, conversionService); Query query = creator.createQuery(); assertThat(query.toString()).isEqualTo(query(where("firstName").not().regex("^dave$", "i")).toString()); @@ -298,7 +324,7 @@ void createsQueryWithFindByNotIgnoreCaseCorrectly() { void createsQueryWithFindByStartingWithIgnoreCaseCorrectly() { PartTree tree = new PartTree("findByFirstNameStartingWithIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName").regex("^dave", "i"))); @@ -308,7 +334,7 @@ void createsQueryWithFindByStartingWithIgnoreCaseCorrectly() { void createsQueryWithFindByEndingWithIgnoreCaseCorrectly() { PartTree tree = new PartTree("findByFirstNameEndingWithIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName").regex("dave$", "i"))); @@ -318,7 +344,7 @@ void createsQueryWithFindByEndingWithIgnoreCaseCorrectly() { void createsQueryWithFindByContainingIgnoreCaseCorrectly() { PartTree tree = new PartTree("findByFirstNameContainingIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName").regex(".*dave.*", "i"))); @@ -328,7 +354,7 @@ void createsQueryWithFindByContainingIgnoreCaseCorrectly() { void shouldThrowExceptionForQueryWithFindByIgnoreCaseOnNonStringProperty() { PartTree tree = new PartTree("findByFirstNameAndAgeIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "foo", 42), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "foo", 42), context, conversionService); assertThatIllegalArgumentException().isThrownBy(creator::createQuery) .withMessageContaining("must be of type String"); @@ -338,7 +364,7 @@ void shouldThrowExceptionForQueryWithFindByIgnoreCaseOnNonStringProperty() { void shouldOnlyGenerateLikeExpressionsForStringPropertiesIfAllIgnoreCase() { PartTree tree = new PartTree("findByFirstNameAndAgeAllIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("firstName").regex("^dave$", "i").and("age").is(42))); @@ -348,7 +374,7 @@ void shouldOnlyGenerateLikeExpressionsForStringPropertiesIfAllIgnoreCase() { void shouldCreateDeleteByQueryCorrectly() { PartTree tree = new PartTree("deleteByFirstName", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context, conversionService); Query query = creator.createQuery(); @@ -360,7 +386,7 @@ void shouldCreateDeleteByQueryCorrectly() { void shouldCreateDeleteByQueryCorrectlyForMultipleCriteriaAndCaseExpressions() { PartTree tree = new PartTree("deleteByFirstNameAndAgeAllIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context, conversionService); Query query = creator.createQuery(); @@ -372,7 +398,7 @@ void shouldCreateDeleteByQueryCorrectlyForMultipleCriteriaAndCaseExpressions() { void shouldCreateInClauseWhenUsingContainsOnCollectionLikeProperty() { PartTree tree = new PartTree("findByEmailAddressesContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context, conversionService); Query query = creator.createQuery(); @@ -383,7 +409,7 @@ void shouldCreateInClauseWhenUsingContainsOnCollectionLikeProperty() { void shouldCreateInClauseWhenUsingNotContainsOnCollectionLikeProperty() { PartTree tree = new PartTree("findByEmailAddressesNotContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context, conversionService); Query query = creator.createQuery(); @@ -394,7 +420,7 @@ void shouldCreateInClauseWhenUsingNotContainsOnCollectionLikeProperty() { void shouldCreateRegexWhenUsingNotContainsOnStringProperty() { PartTree tree = new PartTree("findByUsernameNotContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context, conversionService); Query query = creator.createQuery(); assertThat(query.getQueryObject().toJson()) @@ -408,7 +434,7 @@ void createsNonSphericalNearForDistanceWithDefaultMetric() { Distance distance = new Distance(1.0); PartTree tree = new PartTree("findByLocationNear", Venue.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("location").near(point).maxDistance(1.0))); @@ -423,7 +449,7 @@ void shouldCreateWithinQueryCorrectly() { Shape shape = new Polygon(first, second, third); PartTree tree = new PartTree("findByAddress_GeoWithin", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, shape), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, shape), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("address.geo").within(shape))); @@ -435,7 +461,7 @@ void shouldCreateNearSphereQueryForSphericalProperty() { Point point = new Point(10, 20); PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("address2dSphere.geo").nearSphere(point))); @@ -448,7 +474,7 @@ void shouldCreateNearSphereQueryForSphericalPropertyHavingDistanceWithDefaultMet Distance distance = new Distance(1.0); PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("address2dSphere.geo").nearSphere(point).maxDistance(1.0))); @@ -461,7 +487,7 @@ void shouldCreateNearQueryForMinMaxDistance() { Range range = Distance.between(new Distance(10), new Distance(20)); PartTree tree = new PartTree("findByAddress_GeoNear", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, range), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, range), context, conversionService); Query query = creator.createQuery(); assertThat(query).isEqualTo(query(where("address.geo").near(point).minDistance(10D).maxDistance(20D))); @@ -473,7 +499,7 @@ void appliesIgnoreCaseToLeafProperty() { PartTree tree = new PartTree("findByAddressStreetIgnoreCase", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "Street"); - assertThat(new MongoQueryCreator(tree, accessor, context).createQuery()).isNotNull(); + assertThat(new MongoQueryCreator(tree, accessor, context, conversionService).createQuery()).isNotNull(); } @Test // DATAMONGO-1232 @@ -482,7 +508,7 @@ void ignoreCaseShouldEscapeSource() { PartTree tree = new PartTree("findByUsernameIgnoreCase", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "con.flux+"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("username").regex("^\\Qcon.flux+\\E$", "i"))); } @@ -493,7 +519,7 @@ void ignoreCaseShouldEscapeSourceWhenUsedForStartingWith() { PartTree tree = new PartTree("findByUsernameStartingWithIgnoreCase", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "dawns.light+"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("username").regex("^\\Qdawns.light+\\E", "i"))); } @@ -504,7 +530,7 @@ void ignoreCaseShouldEscapeSourceWhenUsedForEndingWith() { PartTree tree = new PartTree("findByUsernameEndingWithIgnoreCase", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "new.ton+"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("username").regex("\\Qnew.ton+\\E$", "i"))); } @@ -515,7 +541,7 @@ void likeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() { PartTree tree = new PartTree("findByUsernameLike", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("username").regex(".*\\Qfire.fight+\\E.*"))); } @@ -526,7 +552,7 @@ void likeShouldEscapeSourceWhenUsedWithLeadingWildcard() { PartTree tree = new PartTree("findByUsernameLike", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("username").regex(".*\\Qsteel.heart+\\E"))); } @@ -537,7 +563,7 @@ void likeShouldEscapeSourceWhenUsedWithTrailingWildcard() { PartTree tree = new PartTree("findByUsernameLike", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "cala.mity+*"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("username").regex("\\Qcala.mity+\\E.*"))); } @@ -547,7 +573,7 @@ void likeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() { PartTree tree = new PartTree("findByUsernameLike", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "*"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("username").regex(".*"))); } @@ -557,7 +583,7 @@ void bindsNullValueToContainsClause() { PartTree partTree = new PartTree("emailAddressesContains", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, new Object[] { null }); - Query query = new MongoQueryCreator(partTree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(partTree, accessor, context, conversionService).createQuery(); assertThat(query).isEqualTo(query(where("emailAddresses").in((Object) null))); } @@ -568,7 +594,7 @@ void notLikeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() { PartTree tree = new PartTree("findByUsernameNotLike", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query.getQueryObject().toJson()) .isEqualTo(query(where("username").not().regex(".*\\Qfire.fight+\\E.*")).getQueryObject().toJson()); @@ -580,7 +606,7 @@ void notLikeShouldEscapeSourceWhenUsedWithLeadingWildcard() { PartTree tree = new PartTree("findByUsernameNotLike", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query.getQueryObject().toJson()) .isEqualTo(query(where("username").not().regex(".*\\Qsteel.heart+\\E")).getQueryObject().toJson()); @@ -590,7 +616,7 @@ void notLikeShouldEscapeSourceWhenUsedWithLeadingWildcard() { void notLikeShouldEscapeSourceWhenUsedWithTrailingWildcard() { PartTree tree = new PartTree("findByUsernameNotLike", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "cala.mity+*"), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "cala.mity+*"), context, conversionService); Query query = creator.createQuery(); assertThat(query.getQueryObject().toJson()) @@ -603,7 +629,7 @@ void notLikeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() { PartTree tree = new PartTree("findByUsernameNotLike", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, "*"); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query.getQueryObject().toJson()) .isEqualTo(query(where("username").not().regex(".*")).getQueryObject().toJson()); } @@ -614,7 +640,7 @@ void queryShouldAcceptSubclassOfDeclaredArgument() { PartTree tree = new PartTree("findByLocationNear", User.class); ConvertingParameterAccessor accessor = getAccessor(converter, new GeoJsonPoint(-74.044502D, 40.689247D)); - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); + Query query = new MongoQueryCreator(tree, accessor, context, conversionService).createQuery(); assertThat(query.getQueryObject()).containsKey("location"); } @@ -625,7 +651,7 @@ void queryShouldThrowExceptionWhenArgumentDoesNotMatchDeclaration() { ConvertingParameterAccessor accessor = getAccessor(converter, new GeoJsonLineString(new Point(-74.044502D, 40.689247D), new Point(-73.997330D, 40.730824D))); - assertThatIllegalArgumentException().isThrownBy(() -> new MongoQueryCreator(tree, accessor, context).createQuery()) + assertThatIllegalArgumentException().isThrownBy(() -> new MongoQueryCreator(tree, accessor, context, conversionService).createQuery()) .withMessageContaining("Expected parameter type of " + Point.class); } @@ -633,7 +659,7 @@ void queryShouldThrowExceptionWhenArgumentDoesNotMatchDeclaration() { void createsRegexQueryForPatternCorrectly() { PartTree tree = new PartTree("findByFirstNameRegex", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, Pattern.compile(".*")), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, Pattern.compile(".*")), context, conversionService); assertThat(creator.createQuery()).isEqualTo(query(where("firstName").regex(".*"))); } @@ -644,7 +670,7 @@ void createsRegexQueryForPatternWithOptionsCorrectly() { Pattern pattern = Pattern.compile(".*", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); PartTree tree = new PartTree("findByFirstNameRegex", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, pattern), context); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, pattern), context, conversionService); assertThat(creator.createQuery()).isEqualTo(query(where("firstName").regex(".*", "iu"))); } @@ -653,7 +679,7 @@ void betweenShouldAllowSingleRageParameter() { PartTree tree = new PartTree("findByAgeBetween", Person.class); MongoQueryCreator creator = new MongoQueryCreator(tree, - getAccessor(converter, Range.of(Bound.exclusive(10), Bound.exclusive(11))), context); + getAccessor(converter, Range.of(Bound.exclusive(10), Bound.exclusive(11))), context, conversionService); assertThat(creator.createQuery()).isEqualTo(query(where("age").gt(10).lt(11))); } @@ -664,7 +690,7 @@ void nearShouldUseMetricDistanceForGeoJsonTypes() { GeoJsonPoint point = new GeoJsonPoint(27.987901, 86.9165379); PartTree tree = new PartTree("findByLocationNear", User.class); MongoQueryCreator creator = new MongoQueryCreator(tree, - getAccessor(converter, point, new Distance(1, Metrics.KILOMETERS)), context); + getAccessor(converter, point, new Distance(1, Metrics.KILOMETERS)), context, conversionService); assertThat(creator.createQuery()).isEqualTo(query(where("location").nearSphere(point).maxDistance(1000.0D))); } @@ -704,4 +730,14 @@ static class Address2dSphere { @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) Point geo; } + + static class CollectionWithStringTypeId { + @MongoId(FieldType.STRING) + String id; + } + + static class CollectionWithObjectIdTypeId { + @MongoId(FieldType.OBJECT_ID) + String id; + } }