Skip to content

Commit 5fedbe9

Browse files
christophstroblmp911de
authored andcommitted
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: spring-projects#496.
1 parent faf7e36 commit 5fedbe9

File tree

3 files changed

+123
-6
lines changed

3 files changed

+123
-6
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java

+39-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import org.bson.Document;
3131
import org.springframework.data.domain.Example;
32+
import org.springframework.data.domain.ExampleMatcher;
3233
import org.springframework.data.domain.ExampleMatcher.NullHandler;
3334
import org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer;
3435
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
@@ -42,6 +43,7 @@
4243
import org.springframework.data.support.ExampleMatcherAccessor;
4344
import org.springframework.data.util.TypeInformation;
4445
import org.springframework.util.Assert;
46+
import org.springframework.util.ClassUtils;
4547
import org.springframework.util.ObjectUtils;
4648
import org.springframework.util.StringUtils;
4749

@@ -91,7 +93,7 @@ public Document getMappedExample(Example<?> example, MongoPersistentEntity<?> en
9193

9294
Document reference = (Document) converter.convertToMongoType(example.getProbe());
9395

94-
if (entity.getIdProperty() != null) {
96+
if (entity.getIdProperty() != null && ClassUtils.isAssignable(entity.getType(), example.getProbeType())) {
9597

9698
Object identifier = entity.getIdentifierAccessor(example.getProbe()).getIdentifier();
9799
if (identifier == null) {
@@ -107,9 +109,7 @@ public Document getMappedExample(Example<?> example, MongoPersistentEntity<?> en
107109
: new Document(SerializationUtils.flattenMap(reference));
108110
Document result = example.getMatcher().isAllMatching() ? flattened : orConcatenate(flattened);
109111

110-
this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example));
111-
112-
return result;
112+
return updateTypeRestrictions(result, example);
113113
}
114114

115115
private static Document orConcatenate(Document source) {
@@ -288,4 +288,39 @@ private static MatchMode toMatchMode(StringMatcher matcher) {
288288
return MatchMode.DEFAULT;
289289
}
290290
}
291+
292+
private Document updateTypeRestrictions(Document query, Example example) {
293+
294+
Document result = new Document();
295+
296+
if (isTypeRestricting(example.getMatcher())) {
297+
298+
result.putAll(query);
299+
this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example));
300+
return result;
301+
}
302+
303+
for (Map.Entry<String, Object> entry : query.entrySet()) {
304+
if (!this.converter.getTypeMapper().isTypeKey(entry.getKey())) {
305+
result.put(entry.getKey(), entry.getValue());
306+
}
307+
}
308+
309+
return result;
310+
}
311+
312+
private boolean isTypeRestricting(ExampleMatcher matcher) {
313+
314+
if (matcher.getIgnoredPaths().isEmpty()) {
315+
return true;
316+
}
317+
318+
for (String path : matcher.getIgnoredPaths()) {
319+
if (this.converter.getTypeMapper().isTypeKey(path)) {
320+
return false;
321+
}
322+
}
323+
324+
return true;
325+
}
291326
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java

+34
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,32 @@ public void findsExampleUsingAnyMatch() {
166166
assertThat(result, hasItems(p1, p2));
167167
}
168168

169+
@Test // DATAMONGO-1768
170+
public void typedExampleMatchesNothingIfTypesDoNotMatch() {
171+
172+
NotAPersonButStillMatchingFields probe = new NotAPersonButStillMatchingFields();
173+
probe.lastname = "stark";
174+
175+
Query query = new Query(new Criteria().alike(Example.of(probe)));
176+
List<Person> result = operations.find(query, Person.class);
177+
178+
assertThat(result, hasSize(0));
179+
}
180+
181+
@Test // DATAMONGO-1768
182+
public void untypedExampleMatchesCorrectly() {
183+
184+
NotAPersonButStillMatchingFields probe = new NotAPersonButStillMatchingFields();
185+
probe.lastname = "stark";
186+
187+
Query query = new Query(
188+
new Criteria().alike(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_class"))));
189+
List<Person> result = operations.find(query, Person.class);
190+
191+
assertThat(result, hasSize(2));
192+
assertThat(result, hasItems(p1, p3));
193+
}
194+
169195
@Document(collection = "dramatis-personae")
170196
@EqualsAndHashCode
171197
@ToString
@@ -175,4 +201,12 @@ static class Person {
175201
String firstname, middlename;
176202
@Field("last_name") String lastname;
177203
}
204+
205+
@EqualsAndHashCode
206+
@ToString
207+
static class NotAPersonButStillMatchingFields {
208+
209+
String firstname, middlename;
210+
@Field("last_name") String lastname;
211+
}
178212
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java

+50-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import java.util.Arrays;
2626
import java.util.List;
27+
import java.util.Set;
2728
import java.util.regex.Pattern;
2829

2930
import org.bson.conversions.Bson;
@@ -36,8 +37,7 @@
3637
import org.springframework.data.annotation.Id;
3738
import org.springframework.data.domain.Example;
3839
import org.springframework.data.domain.ExampleMatcher;
39-
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers;
40-
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
40+
import org.springframework.data.domain.ExampleMatcher.*;
4141
import org.springframework.data.geo.Point;
4242
import org.springframework.data.mongodb.MongoDbFactory;
4343
import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.ClassWithGeoTypes;
@@ -47,6 +47,7 @@
4747
import org.springframework.data.mongodb.core.mapping.Field;
4848
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
4949
import org.springframework.data.mongodb.test.util.IsBsonObject;
50+
import org.springframework.data.util.TypeInformation;
5051

5152
/**
5253
* @author Christoph Strobl
@@ -434,6 +435,53 @@ public void mapsAnyMatchingExampleCorrectly() {
434435
assertThat(mapper.getMappedExample(example), isBsonObject().containing("$or").containing("_class"));
435436
}
436437

438+
@Test // DATAMONGO-1768
439+
public void allowIgnoringTypeRestrictionBySettingUpTypeKeyAsAnIgnoredPath() {
440+
441+
WrapperDocument probe = new WrapperDocument();
442+
probe.flatDoc = new FlatDocument();
443+
probe.flatDoc.stringValue = "conflux";
444+
445+
org.bson.Document document = mapper
446+
.getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_class")));
447+
448+
assertThat(document, isBsonObject().notContaining("_class"));
449+
}
450+
451+
@Test // DATAMONGO-1768
452+
public void allowIgnoringTypeRestrictionBySettingUpTypeKeyAsAnIgnoredPathWhenUsingCustomTypeMapper() {
453+
454+
WrapperDocument probe = new WrapperDocument();
455+
probe.flatDoc = new FlatDocument();
456+
probe.flatDoc.stringValue = "conflux";
457+
458+
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context);
459+
mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper() {
460+
461+
@Override
462+
public boolean isTypeKey(String key) {
463+
return "_foo".equals(key);
464+
}
465+
466+
@Override
467+
public void writeTypeRestrictions(org.bson.Document result, Set<Class<?>> restrictedTypes) {
468+
result.put("_foo", "bar");
469+
}
470+
471+
@Override
472+
public void writeType(TypeInformation<?> info, Bson sink) {
473+
((org.bson.Document) sink).put("_foo", "bar");
474+
475+
}
476+
});
477+
mappingMongoConverter.afterPropertiesSet();
478+
479+
org.bson.Document document = new MongoExampleMapper(mappingMongoConverter)
480+
.getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_foo")));
481+
482+
assertThat(document, isBsonObject().notContaining("_class").notContaining("_foo"));
483+
}
484+
437485
static class FlatDocument {
438486

439487
@Id String id;

0 commit comments

Comments
 (0)