MongoDb Query In springBoot using MongoRepository - java

I am having Multiple Query Parameters (like name , age , gender, location etc...n numbers) in my GET api. Now I need to query my mongo Database using these query values . Now user can send from 0 to n query parameters.
I am trying to use Something Like
findByNameAndAge(String Name , String Age)
Or
findByNameAndAgeAndGender(String Name , String Age, String Gender)
But the problem is I will have to write multiple queries considering all the permutation and combination user can send.
Is there any better way to do it?

You will need to make your hands dirty with Mongo operations class and build the criteria query dynamically based on passed model parameters. To make things easier you can stick to parameter name maps to entity property convention.

In your case you can use
<S extends T> Iterable<S> findAll(Example<S> example);
So first step is to create an example from your request parameters, to do that you need to create an instance of your entity that have the value of desired attributes to be queried and null for other attributes.
For example if you have in query name and age as parameters. So you just set the name and age of the entity and let null the rest attributes. If you have your entity you should just create the example and query it.
Example<ENTITY_CLASS> example = Example.of(createdEntity);
return repository.findAll((example);
There is some other options to use Example and ExampleMatcher to make case-sensitive startwith contains and regex. You can also set if he need to use OR or AND operators between fields.
You can take a look here

Related

How to accept multiple query parameters with different parameter name in one class

Consider a use case where I have a URL like
/api/v1?credentials="test"&age=20&gender=male
Now how can I have 2 different type of cases
USE CASE-1
How can I accept the above query parameters in the form a class from swagger, I know we can define this in swagger as different individual parameters something like this
parameters:
- in: query
name: credentials
schema:
type: string
- in: query
name: age
schema:
type: integer
- in: query
name: gender
schema:
type: string
but with this swagger creates a rest method with string or integer parameters, and having so many multiple parameters might not be a good idea so what I strongly expect is that it creates a class something like shown below, and my rest methods are generated with this class object.
And how can I leverage this class into my controller layer?
class Query{
String name;
Integer age;
String gender;
}
USE CASE-2
Is there some way I can accept all these query params into the form of a hashMap or multiValueMap
I know there is another integer in the above url query params, but for now, lets consider I will accept all these params into the form of a string and will later typecast them as per my requirement.
NOTE - that I don't want the same name parameter with multiple values, I am expecting all the query parameters with or without the same names to be mapped into one string-to-string key-value pair.
So let's say when I had to access them I will directly use something like map.get("age")
To have these parameters as query parameters is just fine according to REST. Your controller method to handle the request will be good accepting 3 values as int or Strings. For logging in, a better choice would be to use POST method, depending on what you wish to achieve.
From your example, you really should check state-of-the-art authentication. You could consider JWT, it can wrap all these values securely and is actually designed for doing this. After you log-in a user with credentials, the JWT will be created and can then be used by the client for succeeding interactions as long as it is valid.
Edit:
The Swagger notation also supports a deepObject notation as suggested in https://swagger.io/docs/specification/describing-parameters/, which indeed is not exactly what you are looking for. Still, it would allow to send query accordingly: /api/v1?query[credentials]="test"&query[age]=20&query[gender]=male
In Swagger codegen, you could still consider your own code-generator that extends the basic capabilities.

How to retrieve only part (few) of pojo fields in Hibernate?

I have a situation where I need to return only few fields of a POJO.
Here is a SOF Question: retrieve-single-field-rather-than-whole-pojo-in-hibernate question regarding the same, but few things still seems to be obscure.
1) The answer suggests to use -
String employeeName = session.createQuery("select empMaster.name from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
So, here is my concern - Every pojo field is normally private, so "empMaster.name" will simply not work. And am not sure if empMaster.getName() is the solution for this. Will calling the getter methods work?
2) If i am querying multiple fields, (which is my case) (assuming getter methods work) the query will be some thing like -
List<String> employeeDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
Note the return type has changed from String to List<String>.
2(a) Hope this is right?
2(b) what if i am interested in age/salary of employee which will be of int type. I think the return type will be List<String> or List<Object>. Well any how in the application i can recast the String or Object to the proper type int or float etc. So this should not be a problem.
3) Now what if I am querying multiple employee details (no where clause), so the query will be something like - (not sure if the part of query after from is correct)
List<List<<String>> employeesDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster;
Anyway, point here is to emphasise the change in the return type to : List<List<<String>> employeesDetails. Does it work this way ???.
(The question quoted above also has answers pointing to use Projections. I have questions about it but will post them on another question, don't want to mixup.)
I will list the points in the order you mentioned them:
The query has nothing to do with the POJO's field visibility. You are doing a simple query to the database, as if you were doing a query using SQL, and columns in a table have nothing to do with the fact that their mapped POJOs' fields in an application are public or private. The difference is only the language that you're using: now you're using the Hibernate Query Language (HQL), which allows you to express your query with respect to the POJOs' definitions instead of the database's tables' definitions. In fact, doing
session.createQuery("select empMaster.getName() from EmployeeMaster...");
will throw a syntax error: there can be no parenthesis in an object's field name.
By the way, you have to parse your query result to a String, otherwise there would be a compiler semantics error.
String name = (String) session.createQuery("select empMaster.name from EmployeeMaster...").setYourParameters().uniqueResult();
When you do a query whose SELECT clause contains more than one field, and you call uniqueResult(), you're obtaining an array of Objects (Object[]). You iterate through each element in the array and cast it to the corresponding type.
First of all, you probably forgot to add a .list() method call at the end of your query.
session.createQuery("select empMaster.name, empMaster.designation from EmployeeMaster").list();
Otherwise, the query will not be executed, and you would just have a QueryImpl object waiting to be triggered.
Now, when you're expecting multiple results in your query and selecting several fields, you're getting back a List<Object[]>. The elements of the list will be the array of fields for a specific record, and the array per se will be the fields.

Validate Search Criteria : Java

I have to implement search based on almost 12 different fields.
For validation and processing, I am facing a lot of challenges. Following are those with which I am seeking inputs/help.
Classes:
SearchCriteria
(has) UserCriteria and AddressCriteria
PS. A few fields are related to user e.g. First Name, Date of Birth etc. which goes in UserCriteria and a Few fields are related to address e.g. Street Name, Building Number etc. which will go into AddressCriteria
Based on these criteria I have to search users.
Validate that at least one parameter is not null/not empty. I do not
want to keep checking each and every field for null and emptiness.
Before search validate based on which criteria you need to initiate
search. e.g. User or Address?
Implement validations such a way that you need not to worry about
specific field validation before adding it in query criteria
EDIT:
NOTE : I need to prepare a message with all the valid fields which will be passed to the module which helps me finding all records, I am not dealing with DB directly.
What if both of your objects (UserCriteria and AddressCriteria) has a method like,
public ArrayList<String> getSearchableFields() {
//return a ArrayList of searchable fields
}
Then you'll be able to call getSearchableFields() on each method and see what fields are available to use for your search.

Hibernate - findByProperty - how to use it with different types

I'm trying to write a search, using Hibernate that would run search on different types of variables. I have a model Movie, that has properties: title, director, genre, year. Title, director, genre are strings, year is an int.
In the jsp file I have a select that will choose the property by which I want to search, and then a text input where I enter the property value. So it can be for example: Title: Some title, Year: 2000, etc.
I pass those values to the controller. Now, I have such methods in my hibernate classes:
public Movie findByProperty(String searchCriteria, String criteriaValue) {
Criteria criteria = getCurrentSession().createCriteria(Movie.class);
Movie movie = (Movie) criteria.add(Restrictions.eq(searchCriteria, criteriaValue)).uniqueResult();
return movie;
}
and
public Movie findByProperty(String searchCriteria, String criteriaValue) {
jdbcMovieDao.openCurrentSession();
Movie entity = jdbcMovieDao.findByProperty(searchCriteria, criteriaValue);
jdbcMovieDao.closeCurrentSession();
return entity;
}
And in the controller:
public String findMovieBy(ModelMap modelMap, #RequestParam(required = true) String searchCriteria,
#RequestParam(required = true) String criteriaValue) {
Movie movie = movieService.findByProperty(searchCriteria, criteriaValue);
It works fine when I select String properties, but I also want to be able to select year as criterium. I could probably put some if here, but I don't want to do it - not unless there is a nicer way. Can anyone tell me how to do it 'properly'. Please, be gentle, I'm a beginner. :-)
I guess the problem is that year is a Number not String?
I think you could propably get it to work with Restriction.sqlRestriction and CAST so the "database"-side returns a String too... with something like that ... (assuming Oracle as DB)
Restrictions.sqlRestriction("CAST({alias}.propertyName as VARCHAR2(255)) = ?", criteraValue, Hibernate.STRING)
However you're going to do a lot of unessesary casting there so
I'd stick with the ifs ;)
Also, I'm not sure if CAST would cast varchar2 to varchar2 or if that would give an error.
Better solution would be using the Hibernate Metamodel (http://hibernate.org/orm/tooling/) to determine the property type but that's not gonna work without some if's I'm afraid
Restrictions.eq takes Object as the second argument. Just replace "String criteriaValue" by "Object criteriaValue" in findByProperty method, and you should be able to use both String and (autoboxed) int value in the search.
Of course the controller will have to pass int value for a year, not a String.
Your current implementation has other issue, it couples URL parameter searchCriteria with names of Movie class fields, used in the first argument of Restrictions.eq. The tight coupling between the URL and a model is not a good idea in general.
A malicious user could also use your URL in ways you don't expect, for example it's very easy to steal your whole movie database by subsequent queries with searchCriteria=id and subsequent natural numbers as searchValue.
You could make different controller methods for each type of search, which would make Spring convert the year to int automatically while calling the appropriate controller method. This would decouple your web interface and model too, for example you could change the URL syntax without major refactoring of the application.
Alternatively, you can make one controller method with four parameters and refactor the DAO to allow for search by more than one query key.
Whatever you're going to do, don't couple the URL with model field names, and don't use an URL parameter being either String or int depending on the context. It will never make a clean design internally, and it may hamper any future RESTful description of your web interface (for example using WADL), because you won't be able to specify a type of criteriaValue in URL in a consistent way.

How to select a list of objects as part of the constructor argument for a Hibernate group by query

I'm trying to figure out how to select a list of objects as part of a Hibernate group-by query. I know how to do it a harder way, but I'm curious if there is some special sugar syntax that achieves the same thing.
Basically, I have a query of this structure:
select com.myapp.domain.TagSummary(
tag.id, tag.term, tag.description, tag.synonyms, count(user)
)
from User user
join user.tags tag
I'd like to store the tag.synonyms as a List<Tag>. Is that possible, or do I need to query the cross product and do the separation manually after the query results come back?
Alternatively, what I really want in the end is a list of synonym terms separated by commas. So if a tag is spring and it has synonym terms spring-framework and spring-framework-3.1, it would be great to put into the constructor the string spring-framework, spring-framework-3.1. Is that possible?
EDIT: I have learned that I can use group_concat() to achieve the second half of the functionality, but it's only available in MySQL. Is there a way to make it available in hsqldb as well? In Spring 3.1, how do I add this function to Hibernate? I know I should call something on Configuration, but I don't know what bean to access it by.
for (Object[]> result : query.list()) {
Tag tag = (Tag ) result[3];
User user = (User) result[4];
}
You can get more information from this link
https://derrickpetzold.com/p/in-and-group-by-count-hibernate/

Categories

Resources