How do I get distinct fields from MongoRepository/QueryDSL? - java

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);
}

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);
}
}

Spring Data REST QueryDSL arbitrary binding

I'm experimenting with Spring Data REST and so far it's going relatively well. I'm able to query and manipulate the entities, and I have reached a point where I'd like to filter the retrieved data by a variable number of parameters. For this purpose I've been reading and decided on QueryDSL which is integrated nicely with Spring, and it works (almost) flawlessly when using fields from the entities.
However, my filtering form contains some parameters which have no direct mapping to the entity, leading to this question. For the sake of brevity, I'll be using an over-simplified example, hence my using of a persons's age instead of birth-date & etc.
Supposing we have the following Person entity:
#Data
#NoArgsConstructor
#Entity
public class Person {
#Id
#GeneratedValue
private UUID id;
private String name;
private String lastName;
private Integer age;
}
... and the appropriate repo
#RepositoryRestResource
public interface PersonRepository extends CrudRepository<Person, UUID>, QuerydslPredicateExecutor<Person>, QuerydslBinderCustomizer<QPerson> {
#RestResource
Page<Person> findAll(#QuerydslPredicate Predicate predicate, Pageable pageable);
#Override
default void customize(QuerydslBindings bindings, QPerson person) {
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
}
}
... one can access and filter persons by name or last name (case insensitive) via http://<server>/persons?name=whatever, so far so good.
Next step, I would like to see only the people that are "pensionable", let's say over 65 years old, so the URL would look like http://<server>/persons?pensionable=true. However, pensionable is not an attribute in the Person entity, so adding it as a request param doesn't do anything.
I've been trying to figure out how this can be achieved or if this is currently a limitation of the framework(s), but my searches haven't been successful so far. Eventually via trial and error, I've come up with something that seems to work but feels more like a hack:
Create a different PersonExtendedFilter bean (not entity) which includes the extra/arbitrary params:
#Data
#NoArgsConstructor
public class PersonExtendedFilter{
private Boolean pensionable;
}
... create a BooleanPath using the above, and use it to define a binding inside the repo's customize method:
#Override
default void customize(QuerydslBindings bindings, QPerson person) {
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
BooleanPath pensionable = new PathBuilder<>(PersonExtendedFilter.class, "personExtendedFilter").getBoolean("pensionable");
bindings.bind(pensionable).first((path, value) -> new BooleanBuilder().and(value ? person.age.gt(65) : person.age.loe(65)));
}
Bottom line, I'm wondering whether there is an elegant way of doing this or if I missing something, be it from a logical POV, a RTFM one, or something else.

Java Spring data mongodb how to use wildcards?

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

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

How to delete items in MongoRepository using query annotation?

I'm using Spring Data with MongoDB using MongoRepository.
I was wondering if it is possible do a delete by filter using query annotation. I have been looking here and google and I cannot find any documentation.
#Query(value="{'id' : $0}", delete = true)
public Person deleteById (String id);
Maybe you can use repository delete queries. Here is an example from documentation:
public interface PersonRepository extends MongoRepository<Person, String> {
List <Person> deleteByLastname(String lastname);
Long deletePersonByLastname(String lastname);
}
Using return type List will retrieve and return all matching documents before actually deleting them. A numeric return type directly removes the matching documents returning the total number of documents removed.
Try this, it's work for me.
#Repository
public interface DepartmentDao extends MongoRepository<Department, String> {
#DeleteQuery
void deleteByDepartment(String department);
}
OR
#Query(value="{'_id' : ?0}", delete = true)
public void deleteById(String id);
Unfortunately spring data doesn't provides any method to delete documents based on a query. And the #Query annotation is only for find documents.
What you could do is implement a custom repository that deletes documents based on what you want.
How to delete a list of ids in the query ?
#Query(value="{idList : $0}", delete = true)
Repository:
#Component
public interface SomeRepository extends MongoRepository<SomeObject, String> {
#Query("{ '_id' : ?0 }")
SomeObject findById(String _id);
}
Code in some class:
#Autowired
private SomeRepository pRepo;
public void delete(String id) {
pRepo.delete(pRepo.findById(id));
}
#Repository
public interface DepartmentDao extends MongoRepository<Department, String> {
void deleteByDepartment(String department);
}
is clean and shorter.

Categories

Resources