IgnoreCase Finder Not Working with Spring Data Rest and Neo4J - spring

I am unable to coax Spring Data Neo4J (with Spring Data Rest) to ignore case with a finder method. Here's an example repository:
#RepositoryRestResource
public interface WidgetRepository extends PagingAndSortingRepository<Widget, Long> {
Optional<Widget> findByNameIgnoreCase(String name);
}
This example will only find widgets by exact case even though I have the IgnoreCase keyword. I would appreciate advice on how to get a finder method to ignore case with Neo4J. Thanks!

The case specific keywords are not implemented in Spring Data Neo4j yet. But it is possible to use regex in a derived query method.
Define a regex finder method
Optional<Widget> findByNameMatchesRegex(String name);
and use it like this
widgetRepository.findByNameMatchesRegex("(?i)paul");
This is the only option right now within Neo4j to find strings by case-insensitive queries. https://neo4j.com/docs/developer-manual/current/cypher/clauses/where/#case-insensitive-regular-expressions
However this might be useful to support the ignoreCase keyword within Spring Data Neo4j as a convenient access function.

Related

GraphQL SPQR fetches all fields on the server side

I'm new to Spring Boot and I just started using graphql-spqr for Spring Boot since it allows for easy bootstrapping of Java projects.
However, as per my understanding, GraphQL basically allows the fetching of selected fields from the database. As per the examples, I've seen, this type of selection in the graphql-spqr library happens on the client side. Is there a way to do selection both client-side and server-side so as to speed up the queries?
I've looked into EntityGraph examples for GraphQL but they are mostly implemented for complex queries that involve JOINs. However, nothing exists for simple queries like findAll(), findById() etc.
I would like to use findAll() with the server fetching only the fields as requested by the client. How can I do that?
What was said in the comments is correct: GraphQL (and hence SPQR, as it's merely a tool to hook the schema up) does not know anything about SQL, databases, JOINs or anything else. It's a communication protocol, the rest is up to you.
As for your situation, you'd have to inject the subselection into the resolver and pass it down to SQL. In the simplest case, it can look like this (in pseudo code):
public List<Book> books(#GraphQLEnvironment Set<String> fields) {
//pass the requested field names further
return database.query("SELECT " + fields + " FROM book");
}
You can inject ResolutionEnvironment using the same annotation, in case you need the full context.

Implement complex search feature using Spring BOOT REST and Spring Data JPA using Criteria API

I need to implement complex search feature using Spring Boot REST and Spring Data JPA using Criteria API. I need to provide RPIs like below
/college?select=*&where=name:DemoCollege and location:LA and staff{firstName:foo, lastName:boo, workExp>10} and type in [1,2,3]
Collage object has name, location, type fields and staff list.
It has onetomany relationship with Staff so College can have one or many Staff members.
based on the uri I need to build query using criteria api.
I am finding it very complex to implement the toPredicate() method of org.springframework.data.jpa.domain.Specification. Is there any such example handing such complex search filters?
Thanks in advance.
I think the best choice, in your case, is the specification-arg-resolver lib that provide convenient way to build specification declaratively. For example, this code:
#RequestMapping("/customers")
public Object findByName(
#And({
#Spec(path="registrationDate", params="registeredBefore", spec=DateBefore.class),
#Spec(path="lastName", spec=Like.class)}) Specification<Customer> customerSpec) {
return customerRepo.findAll(customerSpec);
}
is corresponding to this request:
GET http://myhost/customers?registeredBefore=2015-01-18&lastName=Simpson
It supports the following specifications: Like, LikeIgnoreCase, Equal, EqualIgnoreCase, In, Null, NotNull, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, DateBetween, Join, Join fetch, and it allows you to combine them, composing complex expressions.
Another option is Querydsl and Web support Spring Data extensions. It also allows you to build a 'REST query language' but has fewer possibilities. You can read in my answer how to use it: https://stackoverflow.com/a/48596145

Spring Data MongoDB: Specifying a hint on a Spring Data Repository find method

I am implementing a Spring Data Repository and having my repository extend the MongoRepository. I am looking for a way to specify a hint on my findBy methods so I can be control. I have seen several times when a non-optimal index would be picked as the winning plan.
This is what my repository looks like right now:
public interface AccountRepository extends MongoRepository<Account, ObjectId> {
#Meta(maxExcecutionTime = 60000L, comment = "Comment" )
public List<Account> findByUserIdAndBrandId(Long userId, Long brandId);
}
I researched a bunch and found that the JPARepository from spring data supports the #QueryHint annotation but I do not believe that annotation is supported for MongoDb. Is there a similar annotation I can specify on top of my findBy method to specify the hint?
MongoTemplate allows to specify a hint, however, I have a ton of findBy methods and I would hate to add an implementation underneath just to specify a hint.

Java Spring, JPA - like expression with wildcard

I am struggling with creation of JPQL statement using Like expression in Java Spring application. My aim is to implement simple case insensitive search function.
My code, which should return a list of companies containing a keyWord in their name looks like this:
List<Company> companies = em.createQuery("select cmp from JI_COMPANIES as companies where UPPER(cmp.name) LIKE :keyWord", Company.class)
.setParameter("keyWord","%" + keyWord.toUpperCase() + "%").getResultList();
return companies;
However, this query only works when my keyWord matches the name of company (Like statement is not used).
When testing the function above, I see in my console message from Hibernate:
Hibernate: select ... /* all my parameters */ ... where company0_.CMPN_NAME=?
It seems that my query is translated to cmpn_name='name' instead of using like expression.
You can look about Hibernate batch processing best pratices and Stateless session.
It was my fault due to not understanding how Spring Data JPA library works.
I have tried to create a custom implementation of myCompanyRepository (myCompanyRepository extends JpaRepository, CompanyRepositoryCustom).
The code which I mentioned above was located in my CompanyRepositoryCustomImpl class which implements CompanyRepositoryCustom interface. However I used method name "findCompaniesByName(String name)". JPA automatically creates a query from this method name and my implementation is not used.
Here is the link for Spring Data JPA reference

How to handle a large set of data using Spring Data Repositories?

I have a large table that I'd like to access via a Spring Data Repository.
Currently, I'm trying to extend the PagingAndSortingRepository interface but it seems I can only define methods that return lists, eg.:
public interface MyRepository extends
PagingAndSortingRepository<MyEntity, Integer>
{
#Query(value="SELECT * ...")
List<MyEntity> myQuery(Pageable p);
}
On the other hand, the findAll() method that comes with PagingAndSortingRepository returns an Iterable (and I suppose that the data is not loaded into memory).
Is it possible to define custom queries that also return Iterable and/or don't load all the data into memory at once?
Are there any alternatives for handling large tables?
We have the classical consulting answer here: it depends. As the implementation of the method is store specific, we depend on the underlying store API. In case of JPA there's no chance to provide streaming access as ….getResultList() returns a List. Hence we also expose the List to the client as especially JPA developers might be used to working with lists. So for JPA the only option is using the pagination API.
For a store like Neo4j we support the streaming access as the repositories return Iterable on CRUD methods as well as on the execution of finder methods.
The implementation of findAll() simply loads the entire list of all entities into memory. Its Iterable return type doesn't imply that it implements some sort of database level cursor handling.
On the other hand your custom myQuery(Pageable) method will only load one page worth of entities, because the generated implementation honours its Pageable parameter. You can declare its return type either as Page or List. In the latter case you still receive the same (restricted) number of entities, but not the metadata that a Page would additionally carry.
So you basically did the right thing to avoid loading all entities into memory in your custom query.
Please review the related documentation here.
I think what you are looking for is Spring Data JPA Stream. It brings a significant performance boost to data fetching particularly in databases with millions of record. In your case you have several options which you can consider
Pull all data once in memory
Use pagination and read pages each time
Use something like Apache Spark
Streaming data using Spring Data JPA
In order to make Spring Data JPA Stream to work, we need to modify our MyRepository to return Stream<MyEntity> like this:
public interface MyRepository extends PagingAndSortingRepository<MyEntity, Integer> {
#QueryHints(value = {
#QueryHint(name = HINT_CACHEABLE, value = "false"),
#QueryHint(name = READ_ONLY, value = "true")
})
#Query(value="SELECT * ...")
Stream<MyEntity> myQuery();
}
In this example, we disable second level caching and hint Hibernate that the entities will be read only. If your requirement is different, make sure to change those settings accordingly for your requirements.

Resources