How does Spring create its similar * queries in mango?
I have the following documents in my Mongo database (_ids missing):
> db.names.find({})
{ "name": "John" }
{ "name": "Jack" }
{ "name": "Johny" }
{ "name": "Jenny" }
I created a Spring data repository for this collection:
public interface NameRepository extends MongoRepository<Name, ObjectId> {
Collection<Name> findByNameLike(String name);
}
The results are as follows:
nameRepository.findByNameLike("John"); // John, Johny
nameRepository.findByNameLike("John$"); // John
nameRepository.findByNameLike("J*ny"); // Johny, Jenny
nameRepository.findByNameLike("Jo?*ny"); // Johny, Jenny
nameRepository.findByNameLike("Jo[]]?*ny"); // empty, without error
Thus, it behaves like a smart regex. Now I want to implement this behavior myself:
class MyNameRepository {
MongoOperations mongoOperations; // injected
public Collection<Name> findByName(String like) {
Query query = new Query();
query.addCriteria(Criteria.where("name").regex(like));
return mongoOperations.find(query, Name.class)
}
}
But the behavior of my method is different:
myNameRepository.findByName("John"); // John, Johny
myNameRepository.findByName("John$"); // John
myNameRepository.findByName("J*ny"); // empty, without error
myNameRepository.findByName("Jo?*ny"); // error (invalid regex)
myNameRepository.findByName("Jo[]]?*ny"); // error (invalid regex)
So my custom methods work exactly the same as I would define a method findByNameRegex
in the Spring data repository. How do I implement the behavior findByNameLike
?
Why do I need this: When I need to filter my collection by many attributes (for example, first name only, first name and last name, first name and zip code, first name, last name and zip code, etc.), I find it easier to maintain queries as parameters rather than creating many multivalued methods in the repository.
source to share
There is actually a simple toLikeRegex method that converts everything *
to .*
. Along with looking for invalid regexps, my custom implementation might look like this:
class MyNameRepository {
MongoOperations mongoOperations; // injected
public Collection<Name> findByName(String like) {
try{
Query query = new Query();
query.addCriteria(Criteria.where("name").regex(toLikeRegex(like)));
return mongoOperations.find(query, Name.class);
} catch(PatternSyntaxException e) {
return Collections.emptyList();
}
}
private String toLikeRegex(String source) {
return source.replaceAll("\\*", ".*");
}
}
and behaves just like the Spring Data Repository.
Update April 19, 2016
There is a class MongoRegexCreator
as of 1.9.0.RELEASE that provides a method toRegularExpression
.
String regex = MongoRegexCreator.INSTANCE
.toRegularExpression(userString, Part.Type.EXISTS);
source to share