Abordagem com MongoRepository
public interface MediaRepository extends MongoRepository<MediaItem, String> {
MediaItem findByIdentifier(String identifier);
Page<MediaItem> findByLevelAndSubject(Level level, Subject subject, Pageable pagination);
}
Limitações encontradas:
- Como construir consultas com filtros dinâmicos?
- Como projetar apenas campos específicos na resposta?
Projeção de campos com @Query
@Query(fields = "{'title':1, 'thumbnail':1, 'subject':1}")
Page<MediaItem> searchByCriteria(Criteria filter, Pageable pagination);
Construindo filtros dinâmicos com DBObject
BasicDBObject filterDoc = new BasicDBObject();
filterDoc.append("ownerId", new BasicDBObject("$gte", 2));
BasicDBObject projectionDoc = new BasicDBObject();
projectionDoc.append("ownerId", 1);
projectionDoc.append("displayName", 1);
Query dynamicQuery = new BasicQuery(filterDoc.toJson(), projectionDoc.toJson());
Exemplo prático com MongoTemplate
@Override
public PageResponse<MediaItem> fetchMediaPage(Pageable pagination, Long levelId, Long subjectId) {
BasicDBObject conditions = new BasicDBObject();
if (levelId != null && levelId != 0) {
conditions.append("level.id", levelId);
}
if (subjectId != null && subjectId != 0) {
conditions.append("subject.id", subjectId);
}
BasicDBObject projection = new BasicDBObject();
projection.append("title", 1);
projection.append("thumbnail", 1);
projection.append("subject", 1);
Query searchQuery = new BasicQuery(conditions.toJson(), projection.toJson());
searchQuery.skip((int) pagination.getOffset()).limit(pagination.getPageSize());
List<MediaItem> items = mongoTemplate.find(searchQuery, MediaItem.class);
long total = mongoTemplate.count(searchQuery, MediaItem.class);
return new PageResponse<>(items, pagination, total);
}
Consulta em propriedades aninhadas (objetos) pode não funcionar como epserado com essa abordagem.
Estratégia híbrida: MongoTemplate + Example
O MongoRepository permite projeção de campos via @Query, porém não suporta filtros dinâmicos. O MongoTemplate oferece flexibilidade na construção de Query com projeção dinâmica, mas consultas em propriedades de objetos são limitadas. A solução é combinar Example (para condições dinâmicas) com Query (para prrojeção de campos), usando o método addCriteria com Criteria.alike(example).
public interface MediaCustomOperations {
Page<MediaItem> searchWithProjection(Example<MediaItem> example, DBObject projection, Pageable pagination);
}
public interface MediaRepository extends MongoRepository<MediaItem, String>, MediaCustomOperations {
MediaItem findByIdentifier(String identifier);
}
public class MediaRepositoryImpl implements MediaCustomOperations {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public Page<MediaItem> searchWithProjection(Example<MediaItem> example, DBObject projection, Pageable pagination) {
Query searchQuery = new BasicQuery(new BasicDBObject(), projection);
searchQuery.addCriteria(new Criteria().alike(example));
searchQuery.with(pagination);
List<MediaItem> results = mongoTemplate.find(searchQuery, example.getProbeType());
return PageableExecutionUtils.getPage(results, pagination,
() -> mongoTemplate.count(searchQuery, example.getProbeType()));
}
}
Referência de palavras-chave do Spring Data MongoDB
| Palavra-chave | Exemplo | Equivalente MongoDB |
|---|---|---|
After |
findByBirthdateAfter(Date date) |
{"birthdate" : {"$gt" : date}} |
GreaterThan |
findByAgeGreaterThan(int age) |
{"age" : {"$gt" : age}} |
GreaterThanEqual |
findByAgeGreaterThanEqual(int age) |
{"age" : {"$gte" : age}} |
Before |
findByBirthdateBefore(Date date) |
{"birthdate" : {"$lt" : date}} |
LessThan |
findByAgeLessThan(int age) |
{"age" : {"$lt" : age}} |
LessThanEqual |
findByAgeLessThanEqual(int age) |
{"age" : {"$lte" : age}} |
Between |
findByAgeBetween(int from, int to) |
{"age" : {"$gt" : from, "$lt" : to}} |
In |
findByAgeIn(Collection ages) |
{"age" : {"$in" : [ages…]}} |
NotIn |
findByAgeNotIn(Collection ages) |
{"age" : {"$nin" : [ages…]}} |
IsNotNull, NotNull |
findByFirstnameNotNull() |
{"firstname" : {"$ne" : null}} |
IsNull, Null |
findByFirstnameNull() |
{"firstname" : null} |
Like, StartingWith, EndingWith |
findByFirstnameLike(String name) |
{"firstname" : name} (regex) |
NotLike, IsNotLike |
findByFirstnameNotLike(String name) |
{"firstname" : { "$not" : name }} (regex) |
Containing (String) |
findByFirstnameContaining(String name) |
{"firstname" : name} (regex) |
NotContaining (String) |
findByFirstnameNotContaining(String name) |
{"firstname" : { "$not" : name}} (regex) |
Containing (Collecsion) |
findByAddressesContaining(Address address) |
{"addresses" : { "$in" : address}} |
NotContaining (Collection) |
findByAddressesNotContaining(Address address) |
{"addresses" : { "$not" : { "$in" : address}}} |
Regex |
findByFirstnameRegex(String firstname) |
{"firstname" : {"$regex" : firstname}} |
(Sem keyword) |
findByFirstname(String name) |
{"firstname" : name} |
Not |
findByFirstnameNot(String name) |
{"firstname" : {"$ne" : name}} |
Near |
findByLocationNear(Point point) |
{"location" : {"$near" : [x,y]}} |
Near |
findByLocationNear(Point point, Distance max) |
{"location" : {"$near" : [x,y], "$maxDistance" : max}} |
Near |
findByLocationNear(Point point, Distance min, Distance max) |
{"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}} |
Within |
findByLocationWithin(Circle circle) |
{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}} |
Within |
findByLocationWithin(Box box) |
{"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}} |
IsTrue, True |
findByActiveIsTrue() |
{"active" : true} |
IsFalse, False |
findByActiveIsFalse() |
{"active" : false} |
Exists |
findByLocationExists(boolean exists) |
{"location" : {"$exists" : exists}} |