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 50726ba72..55a2b8217 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 @@ -28,6 +28,7 @@ import java.util.Stack; import java.util.regex.Pattern; 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; @@ -41,6 +42,7 @@ import org.springframework.data.repository.core.support.ExampleMatcherAccessor; import org.springframework.data.repository.query.parser.Part.Type; 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; @@ -99,8 +101,8 @@ public class MongoExampleMapper { DBObject reference = (DBObject) converter.convertToMongoType(example.getProbe()); - if (entity.hasIdProperty() && entity.getIdentifierAccessor(example.getProbe()).getIdentifier() == null) { - reference.removeField(entity.getIdProperty().getFieldName()); + if (entity.hasIdProperty() && ClassUtils.isAssignable(entity.getType(), example.getProbeType())) { + if (entity.getIdentifierAccessor(example.getProbe()).getIdentifier() == null) {reference.removeField(entity.getIdProperty().getFieldName());} } ExampleMatcherAccessor matcherAccessor = new ExampleMatcherAccessor(example.getMatcher()); @@ -111,9 +113,7 @@ public class MongoExampleMapper { : new BasicDBObject(SerializationUtils.flattenMap(reference)); DBObject result = example.getMatcher().isAllMatching() ? flattened : orConcatenate(flattened); - this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example)); - - return result; + return updateTypeRestrictions(result, example); } private static DBObject orConcatenate(DBObject source) { @@ -272,4 +272,39 @@ public class MongoExampleMapper { dbo.put("$options", "i"); } } + + private DBObject updateTypeRestrictions(DBObject query, Example example) { + + DBObject result = new BasicDBObject(); + + if (isTypeRestricting(example.getMatcher())) { + + result.putAll(query); + this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example)); + return result; + } + + for (String key : query.keySet()) { + if (!this.converter.getTypeMapper().isTypeKey(key)) { + result.put(key, query.get(key)); + } + } + + 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 b95f77a07..470c9b75f 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.BSONObject; @@ -36,8 +37,7 @@ import org.mockito.runners.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; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; @@ -426,6 +427,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"; + + DBObject dbo = mapper + .getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_class"))); + + assertThat(dbo, 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(DBObject dbo, Set> restrictedTypes) { + dbo.put("_foo", "bar"); + } + + @Override + public void writeType(TypeInformation info, DBObject sink) { + sink.put("_foo", "bar"); + + } + }); + mappingMongoConverter.afterPropertiesSet(); + + DBObject dbo = new MongoExampleMapper(mappingMongoConverter) + .getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_foo"))); + + assertThat(dbo, isBsonObject().notContaining("_class").notContaining("_foo")); + } + static class FlatDocument { @Id String id;