LDAP template search by multiple attributes - spring

Trying to search for users details by using userid,emailid,firstname,lastname,GUID,etc...many more values that need to be added in future
The search should be performed using all the attributes which are not null.
Found this piece of code online *
String filter = "(&(sn=YourName)(mail=*))";
*
Is there any other predefined template or such to do the search, more optimal way without directly specifying values to be Null or using if else statements for each and every attribute? All values must be passed to the method and those not null must be used for search using LDAP. Anything? Please help.

You can effectively use the Filters at run time to specify what to use for search and what not depending on some rules or your NULL validations on attributes. Pls find sample code which fetches person name using filters in ldapTemplate :-
public static final String BASE_DN = "dc=xxx,dc=yyy";
private LdapTemplate ldapTemplate ;
public List getPersonNames() {
String cn = "phil more";
String sn = "more";
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new EqualsFilter("sn", sn));
filter.and(new WhitespaceWildcardsFilter("cn", cn));
return ldapTemplate.search(
BASE_DN,
filter.encode(),
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("cn").get();
}
});
}
As name suggests the AndFilters joins all individual filters used in lookup like EqualFilter which checks for equality of attributes while WhitespaceWildcardsFilter to perform wildcard search. So here like we got cn = phil more, it in turn uses *phil*more* for search.

Related

MapStruct Spring Page to custom object conversion includes check

I am using MapStruct to convert a Page object to a custom object of my application. I am using this mapping in order to convert the content field of the Page object to a list of custom objects found in my data model:
#Mapping(target = "journeys", source = "content")
While this works OK and does convert the elements when content is present, this does not work correctly in case of no Page content. Taking a look at the code seems to show that the following check is added in the generated mapper class:
if ( page.hasContent() ) {
List<JourneyDateViewResponseDto> list = page.getContent();
journeyDateViewPageResponseDto.setJourneys( new ArrayList<JourneyDateViewResponseDto>( list ) );
}
When this is added the mapping action of the inner objects is omitted, meaning that I end up with a null list. I am not really sure as to why and how this check is added but I would like to find a way of disabling it and simply end up with an empty list of elements. Is there a way this can be done using MapStruct?
MapStruct has the concept of presence checkers (methods that have the pattern hasXXX). This is used to decide if a source property needs to be mapped.
In case you want to have a default value in your object I would suggest making sure that your object is instantiated with an empty collection or provide an #ObjectFactory for your object in which you are going to set the empty collection.
e.g.
Default value in class
public class JourneyDateViewPageResponseDto {
protected List<JourneyDateViewResponseDto> journeys = new ArrayList<>();
//...
}
Using #ObjectFactory
#Mapper
public interface MyMapper {
JourneyDateViewPageResponseDto map(Page< JourneyDateViewResponseDto> page);
#ObjectFactory
default JourneyDateViewPageResponseDto createDto() {
JourneyDateViewPageResponseDto dto = new JourneyDateViewPageResponseDto();
dto.setJourneys(new ArrayList<>());
return dto;
}
}
#Mapping(target = "journeys", source = "content", defaultExpression = "java(java.util.List.of())")

Don't know how to treat that as a predicate

I'm trying to run a custom query in my repository, but I'm getting a InvalidDataAccessResourceUsageException. "Don't know how to treat that as a predicate String("n.id = '1234'")".
public void myMethod() {
myRepository.queryUsingCustomFilters("n.id = '1234'");
}
public interface MyRepository() extends Neo4jRepository<MyObject, String> {
#Query("MATCH (n) WHERE {filter} RETURN n")
List<MyObject> queryUsingCustomFilters(#Param("filter") String filter);
}
I have a simple example for now, but the string I'm passing in the future could be a little bit more complicated, such as "n.id = '1234' AND (n.name = 'one name' OR n.name = 'another name')"
I don't believe you can pass entire clauses/predicates/queries as a #Param.
If you want to build queries at run time, you might want to look at composing it using the lower level Neo4j OGM filters (see https://neo4j.com/docs/ogm-manual/current/reference/#reference:filters)
So in the case you describe above, you could simply add Filters as required and chain them together to build your WHERE clause

Sorting a custom JPA query with pageable

So, I've already done this using the standard Spring Data JPA interface which extends PagingAndSortingRepository in order to achieve pagination and sorting for a REST API. The thing is, now I want to achieve the very same thing but now using just vanilla JPA and so far so good I managed to get my API to paginate but the sorting doesn't work at all. Every time I try to set the parameter (from a pageable object using pageable.getSort()) it ends with a query error (either if I just send a string as parameter like "name" or just send the sort object, it shows errors).
Here's some code:
My repo implementation:
#Override
public List<Project> findByAll(Pageable pageable) {
Query query = em.createQuery("SELECT project FROM Project project ORDER BY :sort");
query.setParameter("sort", pageable.getSort());
query.setMaxResults(pageable.getPageSize());
query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
return query.getResultList();
}
My service:
#Override
public Page<Project> findAll(Pageable pageable) {
objects = Lists.newArrayList(repository.findByAll(pageable));
PageImpl<Project> pages= new PageImpl<Project>(objects, pageable, repository.count());
return pages;
}
To be clear, I'm filling the Pageable object via URI and from the console I can say it's actually getting the data, so I assume the problem is with the repo.
Edit: This is the error I get when I replace the setParameter("sort", ...) for a hardcoded string aka query.setParameter("sort", "name"):
java.lang.NumberFormatException: For input string: "name"
And I think this method should stand for strings as well. If I use query.setParameter("sort", pageable.getSort()), the error is the same.
The order by cannot be set as a query parameter. Also, the Pageable.getSort().toString() likely won't return a string suitable for use in an order by clause as it will result in a String that represents the Order as property: ORDER, note the colon.
Here are some modifications that will work, assuming Java 8...
String order = StringUtils.collectionToCommaDelimitedString(
StreamSupport.stream(sort.spliterator(), false)
.map(o -> o.getProperty() + " " + o.getDirection())
.collect(Collectors.toList()));
Query query = em.createQuery(
String.format("SELECT project FROM Project project ORDER BY %s", order));

How to query data via Spring data JPA with user defined offset and limit (Range)

Is it possible to fetch data in user defined ranges [int starting record -int last record]?
In my case user will define in query String in which range he wants to fetch data.
I have tried something like this
Pageable pageable = new PageRequest(0, 10);
Page<Project> list = projectRepository.findAll(spec, pageable);
Where spec is my defined specification but unfortunately this do not help.
May be I am doing something wrong here.
I have seen other spring jpa provided methods but nothing are of much help.
user can enter something like this localhost:8080/Section/employee? range{"columnName":name,"from":6,"to":20}
So this says to fetch employee data and it will fetch the first 15 records (sorted by columnName ) does not matter as of now.
If you can suggest me something better that would be great.if you think I have not provided enough information please let me know, I will provide required information.
Update :I do not want to use native or Create query statements (until I don't have any other option).
May be something like this:
Pageable pageable = new PageRequest(0, 10);
Page<Project> list = projectRepository.findAll(spec, new pageable(int startIndex,int endIndex){
// here my logic.
});
If you have better options, you can suggest me that as well.
Thanks.
Your approach didn't work, because new PageRequest(0, 10); doens't do what you think. As stated in docs, the input arguments are page and size, not limit and offset.
As far as I know (and somebody correct me if I'm wrong), there is no "out of the box" support for what you need in default SrpingData repositories. But you can create custom implementation of Pagable, that will take limit/offset parameters. Here is basic example - Spring data Pageable and LIMIT/OFFSET
We can do this with Pagination and by setting the database table column name, value & row counts as below:
#Transactional(readOnly=true)
public List<String> queryEmployeeDetails(String columnName,String columnData, int startRecord, int endRecord) {
Query query = sessionFactory.getCurrentSession().createQuery(" from Employee emp where emp.col= :"+columnName);
query.setParameter(columnName, columnData);
query.setFirstResult(startRecord);
query.setMaxResults(endRecord);
List<String> list = (List<String>)query.list();
return list;
}
If I am understanding your problem correctly, you want your repository to allow user to
Provide criteria for query (through Specification)
Provide column to sort
Provide the range of result to retrieve.
If my understanding is correctly, then:
In order to achieve 1., you can make use of JpaSpecificationExecutor from Spring Data JPA, which allow you to pass in Specificiation for query.
Both 2 and 3 is achievable in JpaSpecificationExecutor by use of Pagable. Pageable allow you to provide the starting index, number of record, and sorting columns for your query. You will need to implement your range-based Pageable. PageRequest is a good reference on what you can implement (or you can extend it I believe).
So i got this working as one of the answer suggested ,i implemented my own Pageable and overrided getPagesize(),getOffset(),getSort() thats it.(In my case i did not need more)
public Range(int startIndex, int endIndex, String sortBy) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.sortBy = sortBy;
}
#Override
public int getPageSize() {
if (endIndex == 0)
return 0;
return endIndex - startIndex;
}
#Override
public int getOffset() {
// TODO Auto-generated method stub
return startIndex;
}
#Override
public Sort getSort() {
// TODO Auto-generated method stub
if (sortBy != null && !sortBy.equalsIgnoreCase(""))
return new Sort(Direction.ASC, sortBy);
else
return new Sort(Direction.ASC, "id");
}
where startIndex ,endIndex are starting and last index of record.
to access it :
repository.findAll(spec,new Range(0,20,"id");
There is no offset parameter you can simply pass. However there is a very simple solution for this:
int pageNumber = Math.floor(offset / limit) + ( offset % limit );
PageRequest pReq = PageRequest.of(pageNumber, limit);
The client just have to keep track on the offset instead of page number. By this I mean your controller would receive the offset instead of the page number.
Hope this helps!

how to create query parser to parse query param in spring REST

My query parameter is like this:
q=name:abc+age:20+roleid:(23|45)|audeince:(23|24).Here + is for AND | is for OR
I have to accept this query param as it is into my spring controller and have to make query to solar to fetch the data.

#Controller
#RequestMapping("/user")
public class BooksController {
#RequestMapping(value="/details", method=RequestMethod.GET)
public ResponseEntity<?> getUser(final HttpServletRequest request) {
String params = requestParams.get("q")[0];
//passing this string to make query in apache solar
}
}
I need to write a parser to split the param value to make a solar query.how to write a query parser to split the above url to make solar query satisfying OR AND condition.name:abc+age:20+roleid:(23|45)|audeince:(23|24) means create a solar query where name=abc and age=20 and roleid in (23,24) or audience in (23,24) .This way user sends query.
Eg:firstName:(abc|bcd)+lastName:abc+emailId:abc+dsID:abc|countryCd:US+audienceId:(123+678)
first using regex convert like this
firstName:(abc|bcd)+ -----------segment1
lastName:abc+.............segment2
emailId:abc+.............segment3
dsID:abc|--------------segment4
countryCd:US+----------segment5
audienceId:(123+678)------segment 6;
like many segments may come in the url
i have a class called
class Queryobj{
private String field;
private List value;
private String internalOperator;
private String externalOperator;
}
firstName:(abc|bcd)+ again using regex map Like this
field=firstName
value={abc,bcd}
internalOperator=|
externalOperator=+
like second segment emailId:abc+
field=emailId
value=abc
internalOperator=null
externalOperator=+
same for other segments also .if there are n segments we have n objects.
After that add each object to Linked List.if internalOperator or externalOperator is null then leave it as null .How can I achieve that
You can use this regex pattern to get "key:value operator" segment
Pattern keyValuePattern = Pattern.compile("[\\w]+:([\\w#.]+|\\([\\w|+#.]+\\))[+|]?");

Resources