Is it possible to be able to accept many matrix parameters in my rest URL without having to declare them in my code? - rest

I am developing a Restful WS which does the simple job of querying a DB and bringing back some data. The table that is querying has around 20 columns.
I want to be able to filter the my returned records by using the matrix parameters in the WHERE clause of my SQL statements.
For Example:
Lets say that we have the table People with the columns id, firstname, lastname
I want the URL http://localhost:808/myservice/people;firstname=nick
to bring me back all the people with firstname equals Nick (select * from people where firsname='Nick').
First of all, is this the correct practice to do that?
Second, in my tablet that I have 20 columns I must create a method in my Java code that will contain all the possible matrix parameters (see below) or there is a better way to do this?
public Response getPeople(#MatrixParam("id") String id,
#MatrixParam("firstname") String firstname,
#MatrixParam("lastname") String lastname,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,) {
}
Thanks in advance

First of all do not create your query by concatenating strings:
String q = "select * from where firstName = " + firstName //BAD!
You are asking for troubles like SQL injection attacks. If you use JDBC, use query parameters.
Since probably you want to use GET request, you can stick to your approach, use query parameters instead (#QueryParam). You might also consider the following approach:
http://localhost:808/myservice/people?filter=firstname:nick,lastName:smith
and method:
public Response getPeople(#QueryParam("filter") String filter) {
//if filter is not null, tokenize filter string by ',' then by ':'
//to get needed parameters
}

You should use #BeanParam in order to map the MatrixParam to an object.
This way you can keep the resource pretty simple, but still have the possibility to add more matrix parameters. Also, adding matrix params doesn't involve changing the resource at all. The #BeanParam works also with #PathParam and #QueryParam.
Example:
Consider this:
http://localhost:8081/myservice/people;firstname=nick,lastName=smith/?offset=3&limit=4
and then the resource:
#GET
public Response get(#BeanParam Filter filter, #BeanParam Paging paging) {
return Response.ok("some results").build();
}
and the Filter class looks like:
public class Filter {
public Filter(#MatrixParam("firstname") String firstname, #MatrixParam("lastname") String lastname) {}
}
and the Paging class:
public class Paging {
public Paging(#QueryParam("offset") int offset, #QueryParam("limit") int limit) { }
}
You can also use more filters, like Filter1, Filter2 etc in order to keep it more modular.
Using the matrix parameters the biggest advantage is caching. It makes even more sense if you have more than one level in you API, like ../animals;size=medium/mamals;fur=white/?limit=3&offset=4, because the query params would apply otherwise to all collections.

Related

MongoDB query for equal search with $regex

I have an entity
class Data {
string name;
string city;
string street;
string phone;
string email;
}
An api has been written to find Data by each param. This is search api so if a param is provided, it will be used if not then everything has to be queried for that param.
#Query("{'name': ?0,'city': ?1,'street': ?2, 'phone': ?3,'email': ?4}")
Page<IcePack> findDataSearchParams(String name,
String city,
String street,
String phone,
String email);
This only works when all the params are sent in the request. It wont work if any of the params are not sent because it will look for null value in the DB for that param.
I want to query everything for that param if it is not requested like the way it is done in SQL. I tired to use $regex with empty string when something is not sent but regex works like a like search but I want to do equal search
anyway to do this
Filtering out parts of the query depending on the input value is not directly supported. Nevertheless it can be done using #Query the $and and operator and a bit of SpEL.
interface Repo extends CrudRepository<IcePack,...> {
#Query("""
{ $and : [
?#{T(com.example.Repo.QueryUtil).ifPresent([0], 'name')},
?#{T(com.example.Repo.QueryUtil).ifPresent([1], 'city')},
...
]}
""")
Page<IcePack> findDataSearchParams(String name, String city, ...)
class QueryUtil {
public static Document ifPresent(Object value, String property) {
if(value == null) {
return new Document("$expr", true); // always true
}
return new Document(property, value); // eq match
}
}
// ...
}
Instead of addressing the target function via the T(...) Type expression writing an EvaluationContextExtension (see: json spel for details) allows to get rid of repeating the type name over and over again.

Select nested property from MongoDB repository with Spring Boot

I like the parsing of method names in the MongoRepository so I don't have to write queries. But I was wondering if there is a way to use this pattern to only select a certain (nested) field.
My Document looks like:
#Document(collection = "elements")
public class ElementEntity {
#Id
private String id;
private String type;
private MetaData metaData;
private String json;
}
public class MetaData {
private String title;
private String description;
private final List<String> keywords = new ArrayList<>();
}
I can search ElementEntities by keyword with this:
List<ElementEntity> findByMetaDataKeywords(String keyword);
I now want to get a list of possible keywords, but I don't find any documentation on how or if it is even possible with the method name pattern. I was hoping something like this might work, but it doesn't:
List<String> getDistinctMetaDataKeywordsAsc();
Is there a way to achieve this with just an interface method, or do I need to write a (SQL?) query?
EDIT based on a comment in one of the answers:
Assuming I have two documents in my elements collection:
has the keywords: "disclaimer" and "legal"
has the keywords: "footnote" and "legal"
I want to have a method that returns me a List with the three distinct keywords in alphabetic order: "disclaimer", "footnote", "legal"
try this
List<ElementEntity> findByMetaData_Keywords(List<String> keywords);

I am trying to use dynamic order by but the list retrieved is not ordered

public List<Series> findSeries(int period, String fieldname, int num) {
TypedQuery<Series> query = em.createQuery(
"select s from Series s where s.period = ?1 order by ?2",
Series.class);
query.setParameter(1, period);
query.setParameter(2, fieldname);
query.setMaxResults(num);
return query.getResultList();
}
This is the method I am using. I think order by isn't even getting executed, it doesn't give any error even when I pass incorrect fieldname.
When it comes to dynamic limit and ordering, its best to use PagingAndSortingRepository so now my Repository extends this repository. I can simply use JPA criteria query as below.
If u want to learn more about JPA criteria query i found this very helpful http://docs.spring.io/spring-data/data-jpa/docs/1.0.x/reference/html/#jpa.query-methods.query-creation
#Repository
public interface SeriesRepository extends PagingAndSortingRepository<Series,Long>{
List<Series> findByPeriod(int period, Pageable pageable);
}
And then when I call this method from my dao i can just instantiate PageRequest which is one of the implementation of Pageable. I can add limit and sorting order to this instance.
public List<Series> getSeriesByFilter(int period, String fieldname, int num) {
Sort sort = new Sort(Sort.Direction.ASC, fieldname);
Pageable pageable = new PageRequest(0, num, sort);
return seriesRepo.findByPeriod(period, pageable);
}
You cannot pass variables as column name in order by.
There is a work around which may help you achieve what you are trying.
public List<Series> findSeries(int period, String fieldname, int num) {
String query = "select s from Series s where s.period = "+period+" order by "+fieldname;
return entityManager.createQuery(query).getResultList();
}
Check this question Hibernate Named Query Order By parameter
There are ways to pass column name in order by in ASP, however I am not able to find anything in Spring or JPA.
"Order By" using a parameter for the column name
http://databases.aspfaq.com/database/how-do-i-use-a-variable-in-an-order-by-clause.html

named native query and mapping columns to field of entity that doesn't exist in table

I have a named native query and I am trying to map it to the return results of the named native query. There is a field that I want to add to my entity that doesn't exist in the table, but it will exist in the return result of the query. I guess this would be the same with a stored proc...
How do you map the return results of a stored proc in JPA?...
How do you even call a stored proc?
here is an example query of what I would like to do...
select d.list_id as LIST_ID, 0 as Parent_ID, d.description from EPCD13.distribution_list d
The Result will be mapped to this entity...
public class DistributionList implements Serializable {
#Id
#Column(name="LIST_ID")
private long listId;
private String description;
private String owner;
private String flag;
#Column(name="PARENT_ID", nullable = true)
private long parentID;
}
parent ID is not in any table in my database. I will also need to use this entity again for other calls, that have nothing to do with this call, and that will not need this parent_id? Is there anything in the JPA standard that will help me out?
If results from database are not required for further manipulation, just for preview, you can consider using database view or result classes constructor expression.
If entities retrieved from database are required for further manipulation, you can make use of multiple select expression and transient fields.
Replace #Column annotation with #Transient annotation over parentID.
After retrieving multiple columns from database, iterate over results and manually set parentID.

Passing values in apex

This is a relatively straight forward question but one that has me stumped. In apex I am creating a new list of sObjects (see below):
public static sobjectPartnerSoapSforceCom.sObject_x[] retrieve(String fieldList, String sObjectType, String[] ids, String username, String password)
When I try to create a new sobjectPartnerSoapSforceCom.sObject_x I cant figure out how to pass the required parameters to the retrieve(...).
For example one of my attempts:
ListsObjectList = new List ('id', 'Contact', contactSobjectId , 'blah', 'blah') ;
throws an error with "expecting right parenthesis, found ','.
How can I pass the required parameters to perform the retrieve statement?
Any help appreciated.
The first parameter to retrieve is a String, so your field list should probably be:
String fieldList = 'id, Contact, contactSobjectId , blah, blah';
Then you make an array of Strings for the contact IDs you want:
List<String> ids = new List<String> { 'contactId1', 'contactId2' };
Then make the retrieve call:
soapBinding.retrieve(fieldList, 'Contact', ids, 'username#domain.com', 'thepassword');