diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java index 5411a43fc..4352e4fbd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; import org.bson.Document; import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.ExampleMatcher.NullHandler; import org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer; import org.springframework.data.domain.ExampleMatcher.StringMatcher; @@ -42,6 +43,7 @@ import org.springframework.data.mongodb.core.query.SerializationUtils; import org.springframework.data.support.ExampleMatcherAccessor; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -91,7 +93,7 @@ public class MongoExampleMapper { Document reference = (Document) converter.convertToMongoType(example.getProbe()); - if (entity.getIdProperty() != null) { + if (entity.getIdProperty() != null && ClassUtils.isAssignable(entity.getType(), example.getProbeType())) { Object identifier = entity.getIdentifierAccessor(example.getProbe()).getIdentifier(); if (identifier == null) { @@ -107,9 +109,7 @@ public class MongoExampleMapper { : new Document(SerializationUtils.flattenMap(reference)); Document result = example.getMatcher().isAllMatching() ? flattened : orConcatenate(flattened); - this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example)); - - return result; + return updateTypeRestrictions(result, example); } private static Document orConcatenate(Document source) { @@ -288,4 +288,39 @@ public class MongoExampleMapper { return MatchMode.DEFAULT; } } + + private Document updateTypeRestrictions(Document query, Example example) { + + Document result = new Document(); + + if (isTypeRestricting(example.getMatcher())) { + + result.putAll(query); + this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example)); + return result; + } + + for (Map.Entry entry : query.entrySet()) { + if (!this.converter.getTypeMapper().isTypeKey(entry.getKey())) { + result.put(entry.getKey(), entry.getValue()); + } + } + + return result; + } + + private boolean isTypeRestricting(ExampleMatcher matcher) { + + if (matcher.getIgnoredPaths().isEmpty()) { + return true; + } + + for (String path : matcher.getIgnoredPaths()) { + if (this.converter.getTypeMapper().isTypeKey(path)) { + return false; + } + } + + return true; + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java index 11e2be573..c0eeec632 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java @@ -166,6 +166,32 @@ public class QueryByExampleTests { assertThat(result, hasItems(p1, p2)); } + @Test // DATAMONGO-1768 + public void typedExampleMatchesNothingIfTypesDoNotMatch() { + + NotAPersonButStillMatchingFields probe = new NotAPersonButStillMatchingFields(); + probe.lastname = "stark"; + + Query query = new Query(new Criteria().alike(Example.of(probe))); + List result = operations.find(query, Person.class); + + assertThat(result, hasSize(0)); + } + + @Test // DATAMONGO-1768 + public void untypedExampleMatchesCorrectly() { + + NotAPersonButStillMatchingFields probe = new NotAPersonButStillMatchingFields(); + probe.lastname = "stark"; + + Query query = new Query( + new Criteria().alike(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_class")))); + List result = operations.find(query, Person.class); + + assertThat(result, hasSize(2)); + assertThat(result, hasItems(p1, p3)); + } + @Document(collection = "dramatis-personae") @EqualsAndHashCode @ToString @@ -175,4 +201,12 @@ public class QueryByExampleTests { String firstname, middlename; @Field("last_name") String lastname; } + + @EqualsAndHashCode + @ToString + static class NotAPersonButStillMatchingFields { + + String firstname, middlename; + @Field("last_name") String lastname; + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java index 9b9a912e6..f6fb12502 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java @@ -24,6 +24,7 @@ import static org.springframework.data.mongodb.test.util.IsBsonObject.*; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.regex.Pattern; import org.bson.conversions.Bson; @@ -36,8 +37,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers; -import org.springframework.data.domain.ExampleMatcher.StringMatcher; +import org.springframework.data.domain.ExampleMatcher.*; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.ClassWithGeoTypes; @@ -47,6 +47,7 @@ import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.test.util.IsBsonObject; +import org.springframework.data.util.TypeInformation; /** * @author Christoph Strobl @@ -434,6 +435,53 @@ public class MongoExampleMapperUnitTests { assertThat(mapper.getMappedExample(example), isBsonObject().containing("$or").containing("_class")); } + @Test // DATAMONGO-1768 + public void allowIgnoringTypeRestrictionBySettingUpTypeKeyAsAnIgnoredPath() { + + WrapperDocument probe = new WrapperDocument(); + probe.flatDoc = new FlatDocument(); + probe.flatDoc.stringValue = "conflux"; + + org.bson.Document document = mapper + .getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_class"))); + + assertThat(document, isBsonObject().notContaining("_class")); + } + + @Test // DATAMONGO-1768 + public void allowIgnoringTypeRestrictionBySettingUpTypeKeyAsAnIgnoredPathWhenUsingCustomTypeMapper() { + + WrapperDocument probe = new WrapperDocument(); + probe.flatDoc = new FlatDocument(); + probe.flatDoc.stringValue = "conflux"; + + MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context); + mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper() { + + @Override + public boolean isTypeKey(String key) { + return "_foo".equals(key); + } + + @Override + public void writeTypeRestrictions(org.bson.Document result, Set> restrictedTypes) { + result.put("_foo", "bar"); + } + + @Override + public void writeType(TypeInformation info, Bson sink) { + ((org.bson.Document) sink).put("_foo", "bar"); + + } + }); + mappingMongoConverter.afterPropertiesSet(); + + org.bson.Document document = new MongoExampleMapper(mappingMongoConverter) + .getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_foo"))); + + assertThat(document, isBsonObject().notContaining("_class").notContaining("_foo")); + } + static class FlatDocument { @Id String id;