Skip to content

Commit 1f6f0d2

Browse files
committed
DATAMONGO-1245 - Extend Query by Example API to typed and untyped specs.
1 parent 1692268 commit 1f6f0d2

File tree

10 files changed

+178
-79
lines changed

10 files changed

+178
-79
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ public <T> T findById(Object id, Class<T> entityClass, String collectionName) {
644644
* @see org.springframework.data.mongodb.core.MongoOperations#findByExample(java.lang.Object)
645645
*/
646646
public <S extends T, T> List<T> findByExample(S sample) {
647-
return findByExample(new Example<S>(sample));
647+
return findByExample(Example.of(sample));
648648
}
649649

650650
/*

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

+28-12
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,33 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.Arrays;
20+
import java.util.HashMap;
21+
import java.util.HashSet;
2022
import java.util.Iterator;
2123
import java.util.List;
2224
import java.util.Map;
25+
import java.util.Set;
2326
import java.util.Stack;
2427
import java.util.regex.Pattern;
2528

2629
import org.springframework.data.domain.Example;
27-
import org.springframework.data.domain.ExampleSpec;
30+
import org.springframework.data.domain.ExampleSpec.NullHandler;
2831
import org.springframework.data.domain.ExampleSpec.PropertyValueTransformer;
29-
import org.springframework.data.domain.ExampleSpecAccessor;
32+
import org.springframework.data.domain.ExampleSpec.StringMatcher;
3033
import org.springframework.data.mapping.PropertyHandler;
3134
import org.springframework.data.mapping.context.MappingContext;
3235
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
3336
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
3437
import org.springframework.data.mongodb.core.query.MongoRegexCreator;
3538
import org.springframework.data.mongodb.core.query.SerializationUtils;
39+
import org.springframework.data.repository.core.support.ExampleSpecAccessor;
40+
import org.springframework.data.repository.query.parser.Part.Type;
3641
import org.springframework.util.ObjectUtils;
3742
import org.springframework.util.StringUtils;
3843

3944
import com.mongodb.BasicDBObject;
4045
import com.mongodb.DBObject;
4146

42-
import static org.springframework.data.domain.ExampleSpec.*;
43-
4447
/**
4548
* @author Christoph Strobl
4649
* @author Mark Paluch
@@ -50,11 +53,18 @@ public class MongoExampleMapper {
5053

5154
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
5255
private final MongoConverter converter;
56+
private final Map<StringMatcher, Type> stringMatcherPartMapping = new HashMap<StringMatcher, Type>();
5357

5458
public MongoExampleMapper(MongoConverter converter) {
5559

5660
this.converter = converter;
5761
this.mappingContext = converter.getMappingContext();
62+
63+
stringMatcherPartMapping.put(StringMatcher.EXACT, Type.SIMPLE_PROPERTY);
64+
stringMatcherPartMapping.put(StringMatcher.CONTAINING, Type.CONTAINING);
65+
stringMatcherPartMapping.put(StringMatcher.STARTING, Type.STARTING_WITH);
66+
stringMatcherPartMapping.put(StringMatcher.ENDING, Type.ENDING_WITH);
67+
stringMatcherPartMapping.put(StringMatcher.REGEX, Type.REGEX);
5868
}
5969

6070
/**
@@ -90,11 +100,17 @@ public DBObject getMappedExample(Example<?> example, MongoPersistentEntity<?> en
90100

91101
applyPropertySpecs("", reference, example.getProbeType(), exampleSpecAccessor);
92102

93-
return ObjectUtils.nullSafeEquals( NullHandler.INCLUDE, exampleSpecAccessor.getNullHandler()) ? reference
103+
if (exampleSpecAccessor.isTyped()) {
104+
Set<Class<?>> restrictedTypes = new HashSet<Class<?>>();
105+
restrictedTypes.add(example.getResultType());
106+
this.converter.getTypeMapper().writeTypeRestrictions(reference, restrictedTypes);
107+
}
108+
109+
return ObjectUtils.nullSafeEquals(NullHandler.INCLUDE, exampleSpecAccessor.getNullHandler()) ? reference
94110
: new BasicDBObject(SerializationUtils.flatMap(reference));
95111
}
96112

97-
private String getMappedPropertyPath(String path, Class<?> probeType, ExampleSpecAccessor exampleSpecAccessor) {
113+
private String getMappedPropertyPath(String path, Class<?> probeType) {
98114

99115
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(probeType);
100116

@@ -161,7 +177,7 @@ private void applyPropertySpecs(String path, DBObject source, Class<?> probeType
161177

162178
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey();
163179

164-
String mappedPropertyPath = getMappedPropertyPath(propertyPath, probeType, exampleSpecAccessor);
180+
String mappedPropertyPath = getMappedPropertyPath(propertyPath, probeType);
165181
if (exampleSpecAccessor.isIgnoredPath(propertyPath) || exampleSpecAccessor.isIgnoredPath(mappedPropertyPath)) {
166182
iter.remove();
167183
continue;
@@ -174,7 +190,7 @@ private void applyPropertySpecs(String path, DBObject source, Class<?> probeType
174190
if (exampleSpecAccessor.hasPropertySpecifiers()) {
175191

176192
mappedPropertyPath = exampleSpecAccessor.hasPropertySpecifier(propertyPath) ? propertyPath
177-
: getMappedPropertyPath(propertyPath, probeType, exampleSpecAccessor);
193+
: getMappedPropertyPath(propertyPath, probeType);
178194

179195
stringMatcher = exampleSpecAccessor.getStringMatcherForPath(mappedPropertyPath);
180196
ignoreCase = exampleSpecAccessor.isIgnoreCaseForPath(mappedPropertyPath);
@@ -201,8 +217,7 @@ private void applyPropertySpecs(String path, DBObject source, Class<?> probeType
201217
}
202218
}
203219

204-
private void applyStringMatcher(Map.Entry<String, Object> entry, StringMatcher stringMatcher,
205-
boolean ignoreCase) {
220+
private void applyStringMatcher(Map.Entry<String, Object> entry, StringMatcher stringMatcher, boolean ignoreCase) {
206221

207222
BasicDBObject dbo = new BasicDBObject();
208223

@@ -214,8 +229,8 @@ private void applyStringMatcher(Map.Entry<String, Object> entry, StringMatcher s
214229
}
215230
} else {
216231

217-
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(),
218-
stringMatcher.getPartType());
232+
Type type = stringMatcherPartMapping.get(stringMatcher);
233+
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(), type);
219234
dbo.put("$regex", expression);
220235
entry.setValue(dbo);
221236
}
@@ -224,4 +239,5 @@ private void applyStringMatcher(Map.Entry<String, Object> entry, StringMatcher s
224239
dbo.put("$options", "i");
225240
}
226241
}
242+
227243
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public static Criteria where(String key) {
9898
* @since 1.8
9999
*/
100100
public static Criteria byExample(Object example) {
101-
return byExample(new Example<Object>(example));
101+
return byExample(Example.of(example));
102102
}
103103

104104
/**

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoRepository.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,16 @@ public interface MongoRepository<T, ID extends Serializable>
7979
*/
8080
<S extends T> List<S> insert(Iterable<S> entities);
8181

82-
/*
83-
* (non-Javadoc)
82+
/* (non-Javadoc)
8483
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
8584
*/
8685
@Override
87-
<S extends T> List<T> findAll(Example<S> example);
86+
<S extends T> List<S> findAll(Example<S> example);
8887

89-
/*
90-
* (non-Javadoc)
88+
/* (non-Javadoc)
9189
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
9290
*/
9391
@Override
94-
<S extends T> List<T> findAll(Example<S> example, Sort sort);
92+
<S extends T> List<S> findAll(Example<S> example, Sort sort);
9593

9694
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java

+40-18
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.data.domain.PageImpl;
3131
import org.springframework.data.domain.Pageable;
3232
import org.springframework.data.domain.Sort;
33+
import org.springframework.data.domain.TypedExampleSpec;
3334
import org.springframework.data.mongodb.core.MongoOperations;
3435
import org.springframework.data.mongodb.core.MongoTemplate;
3536
import org.springframework.data.mongodb.core.query.Criteria;
@@ -70,6 +71,7 @@ public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperat
7071
* (non-Javadoc)
7172
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
7273
*/
74+
@Override
7375
public <S extends T> S save(S entity) {
7476

7577
Assert.notNull(entity, "Entity must not be null!");
@@ -87,6 +89,7 @@ public <S extends T> S save(S entity) {
8789
* (non-Javadoc)
8890
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
8991
*/
92+
@Override
9093
public <S extends T> List<S> save(Iterable<S> entities) {
9194

9295
Assert.notNull(entities, "The given Iterable of entities not be null!");
@@ -116,6 +119,7 @@ public <S extends T> List<S> save(Iterable<S> entities) {
116119
* (non-Javadoc)
117120
* @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable)
118121
*/
122+
@Override
119123
public T findOne(ID id) {
120124

121125
Assert.notNull(id, "The given id must not be null!");
@@ -135,6 +139,7 @@ private Criteria getIdCriteria(Object id) {
135139
* (non-Javadoc)
136140
* @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable)
137141
*/
142+
@Override
138143
public boolean exists(ID id) {
139144

140145
Assert.notNull(id, "The given id must not be null!");
@@ -147,6 +152,7 @@ public boolean exists(ID id) {
147152
* (non-Javadoc)
148153
* @see org.springframework.data.repository.CrudRepository#count()
149154
*/
155+
@Override
150156
public long count() {
151157
return mongoOperations.getCollection(entityInformation.getCollectionName()).count();
152158
}
@@ -155,6 +161,7 @@ public long count() {
155161
* (non-Javadoc)
156162
* @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
157163
*/
164+
@Override
158165
public void delete(ID id) {
159166

160167
Assert.notNull(id, "The given id must not be null!");
@@ -166,6 +173,7 @@ public void delete(ID id) {
166173
* (non-Javadoc)
167174
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
168175
*/
176+
@Override
169177
public void delete(T entity) {
170178

171179
Assert.notNull(entity, "The given entity must not be null!");
@@ -177,6 +185,7 @@ public void delete(T entity) {
177185
* (non-Javadoc)
178186
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
179187
*/
188+
@Override
180189
public void delete(Iterable<? extends T> entities) {
181190

182191
Assert.notNull(entities, "The given Iterable of entities not be null!");
@@ -190,6 +199,7 @@ public void delete(Iterable<? extends T> entities) {
190199
* (non-Javadoc)
191200
* @see org.springframework.data.repository.CrudRepository#deleteAll()
192201
*/
202+
@Override
193203
public void deleteAll() {
194204
mongoOperations.remove(new Query(), entityInformation.getCollectionName());
195205
}
@@ -198,6 +208,7 @@ public void deleteAll() {
198208
* (non-Javadoc)
199209
* @see org.springframework.data.repository.CrudRepository#findAll()
200210
*/
211+
@Override
201212
public List<T> findAll() {
202213
return findAll(new Query());
203214
}
@@ -206,6 +217,7 @@ public List<T> findAll() {
206217
* (non-Javadoc)
207218
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
208219
*/
220+
@Override
209221
public Iterable<T> findAll(Iterable<ID> ids) {
210222

211223
Set<ID> parameters = new HashSet<ID>(tryDetermineRealSizeOrReturn(ids, 10));
@@ -220,6 +232,7 @@ public Iterable<T> findAll(Iterable<ID> ids) {
220232
* (non-Javadoc)
221233
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable)
222234
*/
235+
@Override
223236
public Page<T> findAll(final Pageable pageable) {
224237

225238
Long count = count();
@@ -232,6 +245,7 @@ public Page<T> findAll(final Pageable pageable) {
232245
* (non-Javadoc)
233246
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
234247
*/
248+
@Override
235249
public List<T> findAll(Sort sort) {
236250
return findAll(new Query().with(sort));
237251
}
@@ -273,26 +287,26 @@ public <S extends T> List<S> insert(Iterable<S> entities) {
273287
* @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example, org.springframework.data.domain.Pageable)
274288
*/
275289
@Override
276-
public <S extends T> Page<T> findAll(Example<S> example, Pageable pageable) {
290+
public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
277291

278292
Assert.notNull(example, "Sample must not be null!");
279293

280294
Query q = new Query(new Criteria().alike(example)).with(pageable);
281295

282-
long count = mongoOperations.count(q, entityInformation.getJavaType(), entityInformation.getCollectionName());
296+
long count = mongoOperations.count(q, getResultType(example), getCollectionName(example));
283297
if (count == 0) {
284-
return new PageImpl<T>(Collections.<T> emptyList());
298+
return new PageImpl<S>(Collections.<S> emptyList());
285299
}
286-
return new PageImpl<T>(mongoOperations.find(q, entityInformation.getJavaType(),
287-
entityInformation.getCollectionName()), pageable, count);
300+
return new PageImpl<S>(mongoOperations.find(q, getResultType(example), getCollectionName(example)),
301+
pageable, count);
288302
}
289303

290304
/*
291305
* (non-Javadoc)
292306
* @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
293307
*/
294308
@Override
295-
public <S extends T> List<T> findAll(Example<S> example, Sort sort) {
309+
public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
296310

297311
Assert.notNull(example, "Sample must not be null!");
298312

@@ -302,30 +316,25 @@ public <S extends T> List<T> findAll(Example<S> example, Sort sort) {
302316
q.with(sort);
303317
}
304318

305-
return findAll(q);
319+
return mongoOperations.find(q, getResultType(example), getCollectionName(example));
306320
}
307321

308322
/*
309323
* (non-Javadoc)
310324
* @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example)
311325
*/
312326
@Override
313-
public <S extends T> List<T> findAll(Example<S> example) {
314-
315-
Assert.notNull(example, "Sample must not be null!");
316-
317-
Query q = new Query(new Criteria().alike(example));
318-
319-
return findAll(q);
327+
public <S extends T> List<S> findAll(Example<S> example) {
328+
return findAll(example, (Sort) null);
320329
}
321330

322331
@Override
323-
public <S extends T> T findOne(Example<S> example) {
332+
public <S extends T> S findOne(Example<S> example) {
324333

325334
Assert.notNull(example, "Sample must not be null!");
326335

327336
Query q = new Query(new Criteria().alike(example));
328-
return mongoOperations.findOne(q, entityInformation.getJavaType(), entityInformation.getCollectionName());
337+
return mongoOperations.findOne(q, getResultType(example), getCollectionName(example));
329338
}
330339

331340
@Override
@@ -334,7 +343,7 @@ public <S extends T> long count(Example<S> example) {
334343
Assert.notNull(example, "Sample must not be null!");
335344

336345
Query q = new Query(new Criteria().alike(example));
337-
return mongoOperations.count(q, entityInformation.getJavaType(), entityInformation.getCollectionName());
346+
return mongoOperations.count(q, getResultType(example), getCollectionName(example));
338347
}
339348

340349
@Override
@@ -343,7 +352,20 @@ public <S extends T> boolean exists(Example<S> example) {
343352
Assert.notNull(example, "Sample must not be null!");
344353

345354
Query q = new Query(new Criteria().alike(example));
346-
return mongoOperations.exists(q, entityInformation.getJavaType(), entityInformation.getCollectionName());
355+
return mongoOperations.exists(q, getResultType(example), getCollectionName(example));
356+
}
357+
358+
private <S extends T> Class<S> getResultType(Example<S> example) {
359+
360+
if(example.getExampleSpec() instanceof TypedExampleSpec<?>){
361+
return example.getResultType();
362+
}
363+
364+
return (Class<S>) entityInformation.getJavaType();
365+
}
366+
367+
private String getCollectionName(Example<?> example) {
368+
return entityInformation.getCollectionName();
347369
}
348370

349371
private List<T> findAll(Query query) {

0 commit comments

Comments
 (0)