Java Spring data mongodb how to use wildcards? - java

I have a field in my mongodb called "name". I am using annotations in spring data to support querying. My question is, is there a way to support wildcard? i.e. If I have values for "name" called "Robert", "Roberto" "Ramano", I could support queries that allow me to pass say "R" to a function, and it will match on everything that starts with R? Right now I have to basically do an "exact spelling" of Robert, or any one of those names to get an exact match.
I know how to do wildcard search with mongodb directly, but am not sure how to do it in java with spring data. I have a model class representing my Student documents that I use annotations to describe how to query.
db.users.find({"name": /.*m.*/})
I don't know how to translate that into java as I want to pass in a variable. For example:
Pseudocode:
String myvar = "R";
db.users.find({/.*<variable here>*/})
The following is what I have on my "MongoRepository" implementation:
public interface UserRepository extends MongoRepository<UserId, String> {
{
#Query("{'name' : {$regex : ?0}}")
public List<Users> findByName(String username);
}
When I pass in the full name "Robert", then it is able to find "Robert". However, if I put "R", it does not find anything.

Did you try it with query method?
public interface UserRepository extends MongoRepository<UserId, String> {
{
public List<Users> findByNameLike(String username);
}

Why dont you use regex like you're trying to in the following manner :
public interface UserRepository extends MongoRepository<UserId, String> {
{
#Query("{'name' : {$regex : ?0}}")
public List<Users> findByName(String regexp);
}
and in your service form a query somewhat like this :
#Mock
UserRepository userRepository;
String queryInput = "^R.*$";
List<Users> users = userRepository.findByName(queryInput);
find more detailed answers in here in section 4.2

Related

Mapping JPA native query to DTO returns column name instead of column value [duplicate]

Consider the following method on a Spring Data JPA interface:
#Query("select distinct :columnName from Item i")
List<Item> findByName(#Param("columnName") String columnName);
I would like to use such a method for performing queries dynamically using different column names on the same entity. How can this be done?
You can't. You'll have to implement such a method by yourself. And you won't be able to use parameters: you'll have to use String concatenation or the criteria API. What you'll pass won't be a column name but a field/property name. And it won't return a List<Item>, since you only select one field.
You can use QueryDSL support built into Spring Data. See this tutorial to get started.
First of all you must implement custom Spring Data repository by adding interface:
public interface ItemCustomRepository {
List<Item> findBy(String columnName, String columnValue);
}
then you must extend your current Spring Data repository interface with newly created i.e.:
public interface ItemRepository extends JpaRepository<Item, Long>, ItemCustomRepository, QueryDslPredicateExecutor {
}
and then you must implement your interface using Query DSL dynamic expression feature (the name ItemRepositoryImpl is crucial - it will let you use original Spring Data repository implementation):
public class ItemRepositoryImpl implements ItemCustomRepository {
#Autowired
private ItemRepository itemRepository;
public List<Item> findBy(final String columnName, final String columnValue) {
Path<Item> item = Expressions.path(Item.class, "item");
Path<String> itemColumnName = Expressions.path(String.class, item, columnName);
Expression<String> itemValueExpression = Expressions.constant(columnValue);
BooleanExpression fieldEqualsExpression = Expressions.predicate(Ops.EQ, itemColumnName, itemValueExpression);
return itemRepository.findAll(fieldEqualsExpression);
}
}

How to use createQuery with spring boot

Normally I use annotiations:#Query("SELECT c FROM Country c") with JpaRepositoryor predefined methods like findAll
but in my case I want to generate dynamic query.
String baseQuery =SELECT c FROM Country c`
if(age!=null)
baseQuery+="WHERE c.age=20"
I need to perform same query from code level like this:
Query q1 = em.createQuery("SELECT c FROM Country c");
but I dont use EntityManager in spring boot
How can I generate query from code level?
If you would like to create dynamic queries from code you can take advantage of Spring's JdbcTemplate. Using spring boot it is as simple as injecting JdbcOperations bean to your repository class (assuming you have provided spring-boot-starter-jdbc module to your project).
But remember! This solution uses SQL, not JPQL. That's why you have to use proper tables and columns names in queries and properly map result to objects (i.e. using RowMapper)
This simple example worked fine for me (with different entity, but in same manner - I've adapted it to your example):
#Repository
public class CountryRepository {
#Autowired
private JdbcOperations jdbcOperations;
private static String BASIC_QUERY = "SELECT * FROM COUNTRY";
public List<Country> selectCoutry(Long age){
String query = BASIC_QUERY;
if (age != null){
query += " WHERE AGE = ";
query += age.toString();
}
//let's pretend that Country has constructor Conutry(String name, int age)
return jdbcOperations.query(query, (rs, rowNum) ->
{ return new Country(rs.getString("NAME"), rs.getInt("AGE");}
);
};
}
Then in service or whatever you inject CountryRepository and call method.
Since you're using Spring Boot, you can use Spring Data to create queries in your repository:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
}
Not a 100% on syntax, but should be something similar.
Now you can autowire this class:
#Autowired
public CountryRepository countryRepo;
And all basic methods are already available to you like:
countryRepo.findOne(id);
countryRepo.find();
If you want to make more advanced queries, you can use Spring Data e.g.:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
public Country findByNameAndContinent(String name, String continent);
}
This is just an example (a stupid one) of course and assumes your Country class has the field names 'name' and 'continent' and both are strings. More is available here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Section 5.3 more specifically.
PS: Make sure your Country class has the #Entity annotation

How do I get distinct fields from MongoRepository/QueryDSL?

My Document is
#QueryEntity #Data #Document(collection = "MyCol") public class MyCol {
#Id private String _id;
private String version;
I want to get all distinct version stored in the db.
My attempts:
public interface MyColDao extends MongoRepository<MyCol, String>, QueryDslPredicateExecutor<MyCol> {
#Query("{ distinct : 'MyCol', key : 'version'}")
List<String> findDistinctVersion();
}
Or just findDistinctVersion without the query annotation.
Most of the examples of github have a By-field like
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
I don't need a By field.
Another example I found here.
#Query("{ distinct : 'channel', key : 'game'}")
public JSONArray listDistinctGames();
This doesn't seem to work for me.
I can't seem to find queryDSL/Morphia's documentation to do this.
public interface MyColDao extends MongoRepository<MyCol, String>, QueryDslPredicateExecutor<MyCol> {
#Query("{'yourdbfieldname':?0}")
List<String> findDistinctVersion(String version);
}
here version replaces your your db field name
more you can see here
This spring documentation provide the details, how to form a expression when you are want to fetch distinct values.
Link
I had a similar problem, but I couldn't work out how to do it within the MongoRepository (as far as I can tell, it's not currently possible) so ended up using MongoTemplate instead.
I believe the following would meet your requirement.
#AutoWired
MongoTemplate mongoTemplate
public List<String> getVersions(){
return mongoTemplate.findDistinct("version", MyCol.class, String.class);
}

Spring Data - Spring Repository: selecting distinct values for a class attribute

I have the following
#Entity
public class Restaurant{
#ManyToOne
private City c;
// more
}
#Entity
public class City{
private String name;
// more
}
I also have a repository
public interface RestaurantRepository extends JPARepository<Restaurant, Long> {
// something to put here
}
===EDIT====
I have the list of ALL cities, but only some of them are associated to restaurants.
I need to write a method in such respository to extract all cities that are referred by a Restaurant.
In SQL I would just do the following:
SELECT CITY.id, CITY.name FROM CITY WHERE ID NOT IN (SELECT DISTINCT(city_id) FROM RESTAURANT)
Is there a way to obtain the same result using the name conventions as of http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html ?
Thanks.
As I do not see any support for the IS [NOT] EMPTY operator listed in Spring Data's documentation around query creation strategy, you can try manually defining a JPQL query:
public interface CityRepository extends JPARepository<City, Long> {
#Query(value="SELECT c FROM CITY c WHERE c.restaurants IS EMPTY")
List<City> findCitiesWithNoRestaurants();
}
which I'm guessing will translate to the native SQL you gave as an example.
Otherwise looks like you would need to find a list of cities with Restraunts and then pass that to a method which used the NotIn pattern. This will obviously be less performant than the above.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

Spring repository auto casts entities with different class types

I'm using MongoRepository interface to extend my custom repositories for different entities. Now I faced with problem, let's assume an example:
I have 2 entities:
#Document(collection = "person")
public class Employee {
private String position;
}
and
#Document(collection = "person")
public class Manager {
private String major;
}
repositories for both:
#Repository
public interface ManagerRepository extends MongoRepository<Manager, String> {}
and
#Repository
public interface EmployeeRepository extends MongoRepository<Employee, String> {}
Everything goes well when I saving 2 models:
{
"_id" : ObjectId("5541f988d4c603ebac18a147"),
"_class" : "com.igmtechnology.gravity.core.init.test.Manager",
"major" : "majority"
}
{
"_id" : ObjectId("5541f988d4c603ebac18a148"),
"_class" : "com.igmtechnology.gravity.core.init.test.Employee",
"position" : "developer"
}
But when I'm doing findAll() from one of repositories I'm getting 2 objects and one of them spring is automatically casting to another one.
How can avoid this auto casting? Or how can specify which class I need to get?
For both of the repositories, you can use the #Query annotation to specify a MongoDB JSON query string that will be used instead of query derived from the method's name (you must know that there's a convention for parsing the repository's method names and for building MongoDB queries).
So, by using #Query, you can do:
#Repository
public interface ManagerRepository extends MongoRepository<Employee, String>
#Query(value="{ '_class' : 'com.igmtechnology.gravity.core.init.test.Manager' }")
List<Person> findAllManagers();
}
Behind the scenes, this will generate a query, similar to this one:
db.person.findAll({'_class' ; 'com.igmtechnology.gravity.core.init.test.Manager'});
However, there's a minor problem with this code. If you change the fully-qualified class name of Manager, then the query would not throw a RuntimeException, but would return nothing. In this case you can use a wildcard within the #Query.
#Query(value="{ '_class' : ?0 }")
List<Person> findAllManagers(String className);
Then, when you invoke the method, you can just do:
managerRepository.findAllManagers(Manager.class.getName());
The provided Manager.class.getName() will replace the ?0 wildcard and your query will built properly.
Same goes for the Employee repository with the difference that you have to provide the fully-qualified class name of Employee in the #Query's value attribute.
More info:
Spring-data MongoDB repositories

Categories

Resources