Browse Source

DATAMONGO-1768 - Allow ignoring type restriction when issuing QBE.

We now allow to remove the type restriction inferred by the QBE mapping via an ignored path expression on the ExampleMatcher. This allows to create untyped QBE expressions returning all entities matching the query without limiting the result to types assignable to the probe itself.

Original pull request: #496.
pull/499/head
Christoph Strobl 8 years ago committed by Mark Paluch
parent
commit
c4af78d81d
  1. 45
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java
  2. 34
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java
  3. 52
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java

45
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 java.util.regex.Pattern;
import org.springframework.data.domain.Example; 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.NullHandler;
import org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer; import org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer;
import org.springframework.data.domain.ExampleMatcher.StringMatcher; 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.repository.query.parser.Part.Type;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -99,8 +101,8 @@ public class MongoExampleMapper {
DBObject reference = (DBObject) converter.convertToMongoType(example.getProbe()); DBObject reference = (DBObject) converter.convertToMongoType(example.getProbe());
if (entity.hasIdProperty() && entity.getIdentifierAccessor(example.getProbe()).getIdentifier() == null) { if (entity.hasIdProperty() && ClassUtils.isAssignable(entity.getType(), example.getProbeType())) {
reference.removeField(entity.getIdProperty().getFieldName()); if (entity.getIdentifierAccessor(example.getProbe()).getIdentifier() == null) {reference.removeField(entity.getIdProperty().getFieldName());}
} }
ExampleMatcherAccessor matcherAccessor = new ExampleMatcherAccessor(example.getMatcher()); ExampleMatcherAccessor matcherAccessor = new ExampleMatcherAccessor(example.getMatcher());
@ -111,9 +113,7 @@ public class MongoExampleMapper {
: new BasicDBObject(SerializationUtils.flattenMap(reference)); : new BasicDBObject(SerializationUtils.flattenMap(reference));
DBObject result = example.getMatcher().isAllMatching() ? flattened : orConcatenate(flattened); DBObject result = example.getMatcher().isAllMatching() ? flattened : orConcatenate(flattened);
this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example)); return updateTypeRestrictions(result, example);
return result;
} }
private static DBObject orConcatenate(DBObject source) { private static DBObject orConcatenate(DBObject source) {
@ -272,4 +272,39 @@ public class MongoExampleMapper {
dbo.put("$options", "i"); 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;
}
} }

34
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)); 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<Person> 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<Person> result = operations.find(query, Person.class);
assertThat(result, hasSize(2));
assertThat(result, hasItems(p1, p3));
}
@Document(collection = "dramatis-personae") @Document(collection = "dramatis-personae")
@EqualsAndHashCode @EqualsAndHashCode
@ToString @ToString
@ -175,4 +201,12 @@ public class QueryByExampleTests {
String firstname, middlename; String firstname, middlename;
@Field("last_name") String lastname; @Field("last_name") String lastname;
} }
@EqualsAndHashCode
@ToString
static class NotAPersonButStillMatchingFields {
String firstname, middlename;
@Field("last_name") String lastname;
}
} }

52
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.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.bson.BSONObject; import org.bson.BSONObject;
@ -36,8 +37,7 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers; import org.springframework.data.domain.ExampleMatcher.*;
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
import org.springframework.data.geo.Point; import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.ClassWithGeoTypes; 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.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.test.util.IsBsonObject; import org.springframework.data.mongodb.test.util.IsBsonObject;
import org.springframework.data.util.TypeInformation;
import com.mongodb.BasicDBList; import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObject;
@ -426,6 +427,53 @@ public class MongoExampleMapperUnitTests {
assertThat(mapper.getMappedExample(example), isBsonObject().containing("$or").containing("_class")); 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<Class<?>> 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 { static class FlatDocument {
@Id String id; @Id String id;

Loading…
Cancel
Save