diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index a3a22a016..834d2c687 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -914,9 +914,7 @@ public class QueryMapper { MongoPersistentProperty idProperty = entity.getIdProperty(); if (idProperty != null) { - - return name.equals(idProperty.getName()) || name.equals(idProperty.getFieldName()) - || name.endsWith("." + idProperty.getName()) || name.endsWith("." + idProperty.getFieldName()); + return name.equals(idProperty.getName()) || name.equals(idProperty.getFieldName()); } return DEFAULT_ID_NAMES.contains(name); @@ -1004,13 +1002,14 @@ public class QueryMapper { @Nullable private PersistentPropertyPath getPath(String pathExpression) { - try { + String rawPath = pathExpression.replaceAll("\\.\\d+", ""); - PropertyPath path = PropertyPath.from(pathExpression.replaceAll("\\.\\d+", ""), entity.getTypeInformation()); + PropertyPath path = forName(rawPath); + if (path == null || isPathToJavaLangClassProperty(path)) { + return null; + } - if (isPathToJavaLangClassProperty(path)) { - return null; - } + try { PersistentPropertyPath propertyPath = mappingContext.getPersistentPropertyPath(path); @@ -1032,7 +1031,30 @@ public class QueryMapper { } return propertyPath; + } catch (InvalidPersistentPropertyPath e) { + return null; + } + } + + /** + * Querydsl happens to map id fields directly to {@literal _id} which breaks {@link PropertyPath} resolution. So if + * the first attempt fails we try to replace {@literal _id} with just {@literal id} and see if we can resolve if + * then. + * + * @param path + * @return the path or {@literal null} + */ + @Nullable + private PropertyPath forName(String path) { + + try { + return PropertyPath.from(path, entity.getTypeInformation()); } catch (PropertyReferenceException | InvalidPersistentPropertyPath e) { + + if (path.endsWith("_id")) { + return forName(path.substring(0, path.length() - 3) + "id"); + } + return null; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java index 6ab98b1aa..a4335db6a 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java @@ -831,6 +831,30 @@ public class QueryMapperUnitTests { assertThat(document).isEqualTo(new org.bson.Document("nested.id", idHex)); } + @Test // DATAMONGO-2221 + public void shouldNotConvertHexStringToObjectIdForRenamedDeeplyNestedIdField() { + + String idHex = new ObjectId().toHexString(); + Query query = new Query(where("nested.deeplyNested.id").is(idHex)); + + org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), + context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class)); + + assertThat(document).isEqualTo(new org.bson.Document("nested.deeplyNested.id", idHex)); + } + + @Test // DATAMONGO-2221 + public void shouldNotConvertHexStringToObjectIdForUnresolvablePath() { + + String idHex = new ObjectId().toHexString(); + Query query = new Query(where("nested.unresolvablePath.id").is(idHex)); + + org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), + context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class)); + + assertThat(document).isEqualTo(new org.bson.Document("nested.unresolvablePath.id", idHex)); + } + @Document public class Foo { @Id private ObjectId id; @@ -926,6 +950,11 @@ public class QueryMapperUnitTests { static class ClassWithExplicitlyRenamedField { @Field("id") String id; + DeeplyNestedClassWithExplicitlyRenamedField deeplyNested; + } + + static class DeeplyNestedClassWithExplicitlyRenamedField { + @Field("id") String id; } static class ClassWithGeoTypes {