get 400K data from tables in Spring MVC within some sec - java

I'm using Spring MVC and I need to get more than 400k datas by my table where i also using JOIN in mysql query.
What I actually have is a Controller that returns which contains a List. I call the Controller using AJAX.
The problem with my solution is that I'm not able to get the data of List with in seconds it take more than 5 minutes to load on JSP page.
In page Jquery..
$(document).ready(function ajaxPost() {
$.ajax({
type: "GET",
data: page,
url: "allListAjax",
success: function(list) {
//here i get responce list and page which takes 5 minutes
}//success
}//ajax
}//ready
In Controller..
#RequestMapping(value="/allListAjax")
public #ResponseBody IVRRouteReportWrapper dashoardAjax(Model model, #RequestParam(required = false) Integer page) {
IVRRouteReportWrapper wrappObj= new IVRRouteReportWrapper();
List<IVRRouteReport> list = ivrRouteServiceInterface.getAllIVRRouteReport(page);
wrappObj.setIVRouteReportList(list);
wrappObj.setPage(page);
return wrappObj;
}
here, IVRRouteReportWrapper is a Domain model which contains setters and getters of List and page.
In Service Implementation...
public List<IVRRouteReport> getAllIVRRouteReport(Integer page) {
return ivrRouteDAOInterface.getAllIVRRouteReport(page);
}
In Dao Implementation...
public List<IVRRouteReport> getAllIVRRouteReport(Integer page) {
if(page==null) {
page = 0;
}else {
page = page*200;
}
String strqry= "SELECT c.caller_id_number as caller_id_number, c.destination_number as destination_number,"
+" c.created_time as created_time, vbDtmf.digit as dtmf FROM VoiceBroadcastDTMF vbDtmf "
+"LEFT JOIN cdr c ON vbDtmf.uuid=c.orig_id ORDER BY c.created_time DESC";
Query query = getSession().createSQLQuery(strqry)
.addScalar("caller_id_number", new StringType())
.addScalar("destination_number", new StringType())
.addScalar("created_time", new StringType())
.addScalar("dtmf", new StringType())
.setResultTransformer(Transformers.aliasToBean(IVRRouteReport.class))
.setFirstResult(page)
.setMaxResults(200);
List<IVRRouteReport> ivrRouteReportList =(List<IVRRouteReport>) query.getResultList();
getSession().flush();
return ivrRouteReportList;
}
Is there any way to return this List Fast on jsp page ? Thanks in advance.

Index the "created_time" column and also omit not required fields by specifying only the required fields in the query as below
String strqry= "SELECT c.caller_id_number as caller_id_number, c.destination_number as destination_number,"
+" c.created_time as created_time, vbDtmf.digit as dtmf FROM VoiceBroadcastDTMF vbDtmf "
+"LEFT JOIN (SELECT caller_id_number , destination_number , created_time FROM cdr) as c ON vbDtmf.uuid=c.orig_id ORDER BY c.created_time DESC";

Go for pagination.
Refer spring-mvc-pagination

Related

Neo4j OGM session.query() does not return my custom projection

I am facing a strange issue while migrating my old Neo4j code to the new OGM 3.2.38.
When I run the following query in my browser
MATCH (a:Attribute)--(c:Class {name: 'Dog'})--(b:Attribute) RETURN a.name as nom1,c.name as nom2,b.name as nom3
I have results :
╒════════╤══════╤════════╕
│"nom1" │"nom2"│"nom3" │
╞════════╪══════╪════════╡
│"gender"│"Dog" │"age" │
├────────┼──────┼────────┤
│"age" │"Dog" │"gender"│
└────────┴──────┴────────┘
When I use the session.query() with Result type as follows:
String query = "MATCH (a:Attribute)--(c:Class {name: 'Dog'})--(b:Attribute) RETURN a.name as nom1,c.name as nom2,b.name as nom3";
Result result = session.query(query, new HashMap<>());
I have the following results [{nom3=age, nom2=Dog, nom1=gender}, {nom3=gender, nom2=Dog, nom1=age}].
Then, I want to use a custom DTO to get theses results directly mapped into a Java object as follows:
public class TestResult {
String nom1;
String nom2;
String nom3;
}
String query = "MATCH (a:Attribute)--(c:Class {name: 'Dog'})--(b:Attribute) RETURN a.name as nom1,c.name as nom2,b.name as nom3";
Iterable<TestResult> testResultList = session.query(TestResult.class, query, new HashMap<>());
for(TestResult testResult : testResultList){
System.out.println("-------------------------------");
System.out.println(testResult.toString());
}
This session.query() call with the TestResult object does not return anything, the Iterable is empty. Do you have an idea how where the problem is?
When debugging the session.query() call, I see that the database returns the results, but that somehow they are not mapped to my TestResult objects.

Is there a spring for graphql alternative for netflix's #DGSDataloader and DGSData

Trying to migrate a Netflix DGS GraphQL and Neo4J project to now use Spring for GraphQL and Neo4J instead. Hit a roadblock when I wanted to avoid the N+1 problem.
Yes, there is an alternative to avoid the N+1 problem in Spring for GraphQL. It's the #BatchMapping annotation:
Suppose you have the following schema:
type Query {
artistas: [Artista]
}
type Artista {
id: ID
apellido: String
estilo: String
obras:[Obra]
}
type Obra{
artistaId: ID
titulo: String
imagen: String
}
And the following #QueryMapping:
#QueryMapping
Flux<Artista> artistas(){
return Flux.fromIterable(allArtistas);
}
Our Artista DTO contains a List of Obra we may sometimes want, so it can cause us the N+1 problem:
record Artista (Long id, String apellido, String estilo, List<Obra> obras){}
record Obra (Long artistaId, String titulo, String imagen){}
So if you add an additional mapping method annotated with #BatchMapping, you tell the GraphQL engine to fetch that data using a DataLoader under the hood and keeping it at hand for each DB roundtrip, for example.
#BatchMapping(typeName = "Artista")
Mono<Map<Artista, List<Obra>>> obras(List<Artista> artistas){
var artistasIds = artistas.stream()
.map(Artista::id)
.toList();
var todasLasObras = obtenerObras(artistasIds);
return todasLasObras.collectList()
.map(obras -> {
Map<Long, List<Obra>> obrasDeCadaArtistaId = obras.stream()
.collect(Collectors.groupingBy(Obra::artistaId));
return artistas.stream()
.collect(Collectors.toMap(
unArtista -> unArtista, //K, el Artista
unArtista -> obrasDeCadaArtistaId.get(Long.parseLong(unArtista.id().toString())))); //V, la lista de obras
});
}
private Flux<Obra> obtenerObras(List<Long> artistasIds) {
// ...your service-specific way of getting all the Obras from each artistaId...
}
If you throw some logs here and there you can check it only fetches the Obras once.
Hope it helps!

Spring data aggregation query elasticsearch

I am trying to make the below elasticsearch query to work with spring data. The intent is to return unique results for the field "serviceName". Just like a SELECT DISTINCT serviceName FROM table would do comparing to a SQL database.
{
"aggregations": {
"serviceNames": {
"terms": {
"field": "serviceName"
}
}
},
"size":0
}
I configured the field as a keyword and it made the query work perfectly in the index_name/_search api as per the response snippet below:
"aggregations": {
"serviceNames": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "service1",
"doc_count": 20
},
{
"key": "service2",
"doc_count": 8
},
{
"key": "service3",
"doc_count": 8
}
]
}
}
My problem is the same query doesn't work in Spring data when I try to run with a StringQuery I get the error below. I am guessing it uses a different api to run queries.
Cannot execute jest action , response code : 400 , error : {"root_cause":[{"type":"parsing_exception","reason":"no [query] registered for [aggregations]","line":2,"col":19}],"type":"parsing_exception","reason":"no [query] registered for [aggregations]","line":2,"col":19} , message : null
I have tried using the SearchQuery type to achieve the same results, no duplicates and no object loading, but I had no luck. The below sinnipet shows how I tried doing it.
final TermsAggregationBuilder aggregation = AggregationBuilders
.terms("serviceName")
.field("serviceName")
.size(1);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withIndices("index_name")
.withQuery(matchAllQuery())
.addAggregation(aggregation)
.withSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.withSourceFilter(new FetchSourceFilter(new String[] {"serviceName"}, new String[] {""}))
.withPageable(PageRequest.of(0, 10000))
.build();
Would someone know how to achieve no object loading and object property distinct aggregation on spring data?
I tried many things without success to print queries on spring data, but I could not, maybe because I am using the com.github.vanroy.springdata.jest.JestElasticsearchTemplate implementation.
I got the query parts with the below:
logger.info("query:" + searchQuery.getQuery());
logger.info("agregations:" + searchQuery.getAggregations());
logger.info("filter:" + searchQuery.getFilter());
logger.info("search type:" + searchQuery.getSearchType());
It prints:
query:{"match_all":{"boost":1.0}}
agregations:[{"serviceName":{"terms":{"field":"serviceName","size":1,"min_doc_count":1,"shard_min_doc_count":0,"show_term_doc_count_error":false,"order":[{"_count":"desc"},{"_key":"asc"}]}}}]
filter:null
search type:DFS_QUERY_THEN_FETCH
I figured out, maybe can help someone. The aggregation don't come with the query results, but in a result for it self and is not mapped to any object. The Objects results that comes apparently are samples of the query elasticsearch did to run your aggregation (not sure, maybe).
I ended up by creating a method which can do a simulation of what would be on the SQL SELECT DISTINCT your_column FROM your_table, but I think this will work only on keyword fields, they have a limitation of 256 characters if I am not wrong. I explained some lines in comments.
Thanks #Val since I was only able to figure it out when debugged into Jest code and check the generated request and raw response.
public List<String> getDistinctField(String fieldName) {
List<String> result = new ArrayList<>();
try {
final String distinctAggregationName = "distinct_field"; //name the aggregation
final TermsAggregationBuilder aggregation = AggregationBuilders
.terms(distinctAggregationName)
.field(fieldName)
.size(10000);//limits the number of aggregation list, mine can be huge, adjust yours
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withIndices("your_index")//maybe can be omitted
.addAggregation(aggregation)
.withSourceFilter(new FetchSourceFilter(new String[] { fieldName }, new String[] { "" }))//filter it to retrieve only the field we ar interested, probably we can take this out.
.withPageable(PageRequest.of(0, 1))//can't be zero, and I don't want to load 10 results every time it runs, will always return one object since I found no "size":0 in query builder
.build();
//had to use the JestResultsExtractor because com.github.vanroy.springdata.jest.JestElasticsearchTemplate don't have an implementation for ResultsExtractor, if you use Spring defaults, you can probably use it.
final JestResultsExtractor<SearchResult> extractor = new JestResultsExtractor<SearchResult>() {
#Override
public SearchResult extract(SearchResult searchResult) {
return searchResult;
}
};
final SearchResult searchResult = ((JestElasticsearchTemplate) elasticsearchOperations).query(searchQuery,
extractor);
final MetricAggregation aggregations = searchResult.getAggregations();
final TermsAggregation termsAggregation = aggregations.getTermsAggregation(distinctAggregationName);//this is where your aggregation results are, in "buckets".
result = termsAggregation.getBuckets().parallelStream().map(TermsAggregation.Entry::getKey)
.collect(Collectors.toList());
} catch (Exception e) {
// threat your error here.
e.printStackTrace();
}
return result;
}

How to get the next page on spring pagination

I have a resource in my service with pagination and I would like to know how could I process the next request to get the second page.
Here is the java resource:
#GetMapping(value = "/partner/codes")
public Page<String> getCodes(#PageableDefault(size = 5) Pageable pageable) {
final List<String> userIds = service.getIds();
int start = pageable.getOffset();
int end = (start + pageable.getPageSize()) > userIds.size() ? userIds.size() : (start + pageable.getPageSize());
return new PageImpl<String>(userIds.subList(start, end), pageable, userIds.size());
}
And the response the response with 5 results:
{
"content":[
"4a136aa6-00d4-44f0-bb48-d192fd8bc010",
"bebebaf2-b881-4733-8a65-1ecf80b5192e",
"1a0f9d07-1393-48a8-8883-37d87681e84b",
"d2580fdc-db6c-4fa3-89d4-2b52898a20bf",
"2c90e683-4ed4-45a4-b70b-614a3339670b"
],
"last":false,
"totalPages":3,
"totalElements":57,
"size":20,
"number":0,
"sort":null,
"numberOfElements":20,
"first":true
}
I'm sorry, as there was nothing explicit in the documentation, I hadn't noticed that just passing the parameters in the query string.
?page=2&size=20
And the client should create the rule using the response message.

Using dynamic query for spring mvc pagination

I am trying to implement pagination in spring mvc.
In my case, I have a Review entity which consists of reviewStatus and the date it was submitted on.
I am trying to use JpaRepository, but I am not sure how to support the following search query using it.
SELECT * from review WHERE review.reviewStatus = 2 AND
( review.submittedOn BETWEEN '2015-09-08' AND '2015-09-09' )
I can see(refer http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html) that we can define methods for particular queries like findbyname.
However, in my case, if the user doesn't provide status then the above query simply becomes:
SELECT * from review WHERE ( review.submittedOn BETWEEN
'2015-09-08' AND '2015-09-09' )
and if there is no submitted date provided, it becomes
SELECT * from review WHERE review.reviewStatus = 2
In short, the query depends upon the values entered by the end-user and I wish to use the same parameters to implement pagination.
Please guide and/or share links to implement same.
Thanks in advance.
Have a look at http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/PagingAndSortingRepository.html
You can pass a Pageable object into the findAll method
http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Pageable.html
As per http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-seven-pagination/
public List<Person> search(String searchTerm, int pageIndex) {
LOGGER.debug("Searching persons with search term: " + searchTerm);
//Passes the specification created by PersonSpecifications class and the page specification to the repository.
Page requestedPage = personRepository.findAll(lastNameIsLike(searchTerm), constructPageSpecification(pageIndex));
return requestedPage.getContent();
}
/**
* Returns a new object which specifies the the wanted result page.
* #param pageIndex The index of the wanted result page
* #return
*/
private Pageable constructPageSpecification(int pageIndex) {
Pageable pageSpecification = new PageRequest(pageIndex, NUMBER_OF_PERSONS_PER_PAGE, sortByLastNameAsc());
return pageSpecification;
}
have a look this for spring mvc paging: How to implement pagination in Spring MVC 3
As for you dynamic query, you may use a Stringbuilder to build your query based on user input. Somewhere along the lines of:
pulic YourDomainObject searchDynamically(String reviewStatus, String date){
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM review WHERE ");
if(null != reviewStatus && null != date){
sql.append(" reviewStatus = ? and date = ? ");
}elseif(null != reviewStatus){
sql.append(" reviewStatus = ? ");
}elseif(null != date){
sql.append(" date = ? ");
}
}

Categories

Resources