Pageable sorting with specifications returns duplicated results - spring

I have a Service entity that has a field Jobs which has Job entity in a ManyToMany relationship.
Filtering, sorting and pagination works perfectly, but the only problem is sorting by Jobs.
When I send a request to /serviceslist?sort=jobs&sortDir=asc I get duplicate results for each Service that has multiple jobs connected to it. To be more specific, if a Service has 4 jobs, the service gets returned 4 times and so on.
I think I should use groupBy, but how would I implement groupBy in my service below?
Service:
public Page<com.bitweb.syda.data.entity.service.Service> getServicesList(ServiceListRequest request, Pageable pageable) {
Specification<com.bitweb.syda.data.entity.service.Service> spec = where(null);
if (request.getSearch() != null) spec = spec.and(search(request.getSearch()));
if (request.getName() != null) spec = spec.and(name(request.getName()));
if (request.getJobs() != null) spec = spec.and(hasJobs(request.getJobs()));
if (request.getNeedsPatrol() != null) spec = spec.and(needsPatrol(request.getNeedsPatrol()));
return serviceRepository.findAll(spec, pageable);
}
Controller:
#RequestMapping(path = "/serviceslist", method = RequestMethod.GET)
public Page<ServiceResponse> getServicesList(
#RequestParam(defaultValue = "0") Integer page,
#RequestParam(defaultValue = "10") Integer size,
#RequestParam(required = false) String search,
#RequestParam(required = false) String name,
#RequestParam(required = false) String jobs,
#RequestParam(required = false) Boolean needsPatrol,
#RequestParam(defaultValue = "createTime") String sort,
#RequestParam(defaultValue = "asc") String sortDir
) {
ServiceListRequest request = new ServiceListRequest(search, name, jobs, needsPatrol);
Sort.Direction direction;
if (sortDir.equals("asc")) {
direction = Sort.Direction.ASC;
} else {
direction = Sort.Direction.DESC;
}
return serviceService.getServicesList(request, of(page, size, direction, sort))
.map(ServiceResponse::new);
}
Here are some examples what I've tried already:
public Page<com.bitweb.syda.data.entity.service.Service> getServicesList(ServiceListRequest request, Pageable pageable) {
Specification<com.bitweb.syda.data.entity.service.Service> spec = (root, query, builder) -> {
//you can do any check here if you want with the join and check all the search parameters here if you want
//Join<Object, Object> jobs = root.join("jobs");
// also set query to get distinct values
query.distinct(true);
return null;
};
if (request.getSearch() != null) spec = spec.and(search(request.getSearch()));
if (request.getName() != null) spec = spec.and(name(request.getName()));
if (request.getJobs() != null) spec = spec.and(hasJobs(request.getJobs()));
if (request.getNeedsPatrol() != null) spec = spec.and(needsPatrol(request.getNeedsPatrol()));
return serviceRepository.findAll(spec, pageable);
}
After trying this everything works as expected, but sorting by jobs gives me this error:
PSQLException: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
Position: 652
I'm not very proficient with SQL and Criteria API and I don't know what I could do to fix this.
When using query.groupBy(root); instead of distinct, I get this error:
PSQLException: ERROR: column "job4_.id" must appear in the GROUP BY clause or be used in an aggregate function
Position: 780

Related

Spring Boot Pagination is not working with custom DTO list

I am stuck with integrating pagination in Spring boot project.
Service Impl class
#Override
public Page<OnlineBuyProductReportItemDTO> generateOnlineBuyProductReport(String fromDate, String toDate, int pageNum, String sortField, String sortDir) {
Pageable pageable = PageRequest.of(pageNum - 1, 10,
sortDir.equals("asc") ? Sort.by(sortField).ascending()
: Sort.by(sortField).descending()
);
List<PurchaseOrder> purchaseOrderList = purchaseOrderRepository.searchPurchaseOrdersForOnlineBuyProductReport(
DateUtil.convertToDate(fromDate), DateUtil.convertToDate(toDate));
OnlineBuyProductReportItemDTO item = null;
List<OnlineBuyProductReportItemDTO> itemList = new ArrayList<OnlineBuyProductReportItemDTO>();
if(purchaseOrderList != null && purchaseOrderList.size() > 0) {
for (PurchaseOrder purchaseOrder : purchaseOrderList) {
item = new OnlineBuyProductReportItemDTO();
item.setOrderNumber(purchaseOrder.getOrderNumber());
item.setCreatedDate(DateUtil.convertDatetoStringSwissDate(purchaseOrder.getCreationTime()));
Address address = addressRepository.getAddressById(purchaseOrder.getBillingAddressId());
item.setCustomerName(address.getFullName());
item.setContactNumber(address.getTelephone());
List<OrderCart> orderCartList = orderCartRepository.getOrderCartByPurchaseOrderId(purchaseOrder.getId());
List<String> buyProductNameList = new ArrayList<String>();
if(orderCartList != null && orderCartList.size() > 0) {
for (OrderCart orderCart : orderCartList) {
buyProductNameList.add(orderCart.getProductName());
}
}
item.setProductName(buyProductNameList);
item.setPrice(purchaseOrder.getTotalPrice());
item.setStatus(purchaseOrder.getDeliveryStatus().getName());
itemList.add(item);
}
}
return new PageImpl<OnlineBuyProductReportItemDTO>(itemList, pageable,itemList.size());
}
Here I have fetch data from 3 database tables and after data processing I have added to these data to DTO List.
But, when after integrated with the front end, pagination options are displayed but did't work as expected.
All the pages containing same data set and also sorting options are not working as expected.
It seems this pageable part is not applying to the data processing.
Pageable pageable = PageRequest.of(pageNum - 1, 10,
sortDir.equals("asc") ? Sort.by(sortField).ascending()
: Sort.by(sortField).descending()
);
Is there any way to do this

Why can't I compare two fields in a search predicate in Sitecore 7.5?

I am trying to build a search predicate in code that compares two fields in Sitecore and I am getting a strange error message. Basically I have two date fields on each content item - FirstPublishDate (the date that the content item was first published) and LastPublishDate (the last date that the content item was published). I would like to find all content items where the LastPublishDate falls within a certain date range AND where the LastPublishDate does not equal the FirstPublishDate. Using Linq here is my method for generating the predicate...
protected Expression<Func<T, Boolean>> getDateFacetPredicate<T>() where T : MySearchResultItem
{
var predicate = PredicateBuilder.True<T>();
foreach (var facet in myFacetCategories)
{
var dateTo = System.DateTime.Now;
var dateFrom = dateTo.AddDays(facet.Value*-1);
predicate = predicate.And(i => i.LastPublishDate.Between(dateFrom, dateTo, Inclusion.Both)).And(j => j.LastPublishDate != j.FirstPublishDate);
}
return predicate;
}
Then I use this predicate in my general site search code to perform the search as follows: the above predicate gets passed in to this method as the "additionalWhere" parameter.
public static SearchResults<T> GeneralSearch<T>(string searchText, ISearchIndex index, int currentPage = 0, int pageSize = 20, string language = "", IEnumerable<string> additionalFields = null,
Expression<Func<T, Boolean>> additionalWhere = null, Expression<Func<T, Boolean>> additionalFilter = null, IEnumerable<string> facets = null,
Expression<Func<T, Boolean>> facetFilter = null, string sortField = null, SortDirection sortDirection = SortDirection.Ascending) where T : SearchResultItem {
using (var context = index.CreateSearchContext()) {
var query = context.GetQueryable<T>();
if (!string.IsNullOrWhiteSpace(searchText)) {
var keywordPred = PredicateBuilder.True<T>();
// take into account escaping of special characters and working around Sitecore limitation with Contains and Equals methods
var isSpecialMatch = Regex.IsMatch(searchText, "[" + specialSOLRChars + "]");
if (isSpecialMatch) {
var wildcardText = string.Format("\"*{0}*\"", Regex.Replace(searchText, "([" + specialSOLRChars + "])", #"\$1"));
wildcardText = wildcardText.Replace(" ", "*");
keywordPred = keywordPred.Or(i => i.Content.MatchWildcard(wildcardText)).Or(i => i.Name.MatchWildcard(wildcardText));
}
else {
keywordPred = keywordPred.Or(i => i.Content.Contains(searchText)).Or(i => i.Name.Contains(searchText));
}
if (additionalFields != null && additionalFields.Any()) {
keywordPred = additionalFields.Aggregate(keywordPred, (current, field) => current.Or(i => i[field].Equals(searchText)));
}
//query = query.Where(i => (i.Content.Contains(searchText) || i.Name.Contains(searchText))); // more explicit call to check the content or item name for our term
query = query.Where(keywordPred);
}
if (language == string.Empty) {
language = Sitecore.Context.Language.ToString();
}
if (language != null) {
query = query.Filter(i => i.Language.Equals(language));
}
query = query.Page(currentPage, pageSize);
if (additionalWhere != null) {
query = query.Where(additionalWhere);
}
if (additionalFilter != null) {
query = query.Filter(additionalFilter);
}
query = query.ApplySecurityFilter();
FacetResults resultFacets = null;
if (facets != null && facets.Any()) {
resultFacets = facets.Aggregate(query, (current, fname) => current.FacetOn(i => i[fname])).GetFacets();
}
// calling this before applying facetFilter should allow us to get a total facet set
// instead of just those related to the current result set
// var resultFacets = query.GetFacets();
// apply after getting facets for more complete facet list
if (facetFilter != null) {
query = query.Where(facetFilter);
}
if (sortField != null)
{
if (sortDirection == SortDirection.Ascending)
{
query = query.OrderBy(x => x[sortField]);
}
else
{
query = query.OrderByDescending(x => x[sortField]);
}
}
var results = query.GetResults(); // this enumerates the actual results
return new SearchResults<T>(results.Hits, results.TotalSearchResults, resultFacets);
}
}
When I try this I get the following error message:
Server Error in '/' Application.
No constant node in query node of type: 'Sitecore.ContentSearch.Linq.Nodes.EqualNode'. Left: 'Sitecore.ContentSearch.Linq.Nodes.FieldNode'. Right: 'Sitecore.ContentSearch.Linq.Nodes.FieldNode'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NotSupportedException: No constant node in query node of type: 'Sitecore.ContentSearch.Linq.Nodes.EqualNode'. Left: 'Sitecore.ContentSearch.Linq.Nodes.FieldNode'. Right: 'Sitecore.ContentSearch.Linq.Nodes.FieldNode'.
Source Error:
Line 548: FacetResults resultFacets = null;
Line 549: if (facets != null && facets.Any()) {
Line 550: resultFacets = facets.Aggregate(query, (current, fname) => current.FacetOn(i => i[fname])).GetFacets();
Line 551: }
Line 552: // calling this before applying facetFilter should allow us to get a total facet set
From what I can understand about the error message it seems to not like that I am trying to compare two different fields to each other instead of comparing a field to a constant. The other odd thing is that the error seems to be pointing to a line of code that has to do with aggregating facets. I did a Google search and came up with absolutely nothing relating to this error. Any ideas?
Thanks,
Corey
I think what you are trying is not possible, and if you look at this that might indeed be the case. A solution that is given there is to put your logic in the index: create a ComputedField that checks your dates and puts a value in the index that you can search on (can be a simple boolean).
You will need to split your logic though - the query on the date range can still be done in the predicate (as it is relative to the current date) but the comparison of first and last should be done on index time instead of on query time.

how not to insert more than two sequential nextval value?

I'm using spring framework and oracle DB for the Web solution system.
The problem is when I call the web page related on oracle sequence,
sometimes more than two rows are inserted on the DB table.
That rows has not duplicated values but increased values from sequence.
Also I already checked the java code,
but I didn't use the loop or for sentences or call twice insert sentences.
Is that occurred often?
and how can I solve the problem?
Do I have to add the code for checking value or make the oracle trigger on the table?
This is the code.
public void insertDefaultLParameter(HttpServletRequest request, String workflowId) throws Exception{
String newLParaId = mapper.getNewLParaId();
HashMap<String, String> condition = new HashMap<String, String>();
condition.put("newLParaId", newLParaId);
condition.put("paraValue", "2013-05");
condition.put("workflowId", workflowId);
mapper.insertLParameter(condition);
mapper.insertLParameterMapping(condition);
}
"getNewLParaId()" called the sequence "MT_L_PARA_MAPPING_SEQ" like the below sql.
SELECT 'LPARA' || LPAD(MT_L_PARA_MAPPING_SEQ.nextval,10,0) FROM DUAL
After getting the value, the value is inserted into two tables through "insertLParameter" and "insertLParameterMapping" mapping id.
And the below is the code which call "insertDefaultLParameter" class.
See the bottom of the code.
You can find "rfmService.insertDefaultLParameter(request, praWfId);".
#RequestMapping(value="/insertWorkflowInfo", method=RequestMethod.POST, headers="Accept=application/json")
public ModelAndView insertWorkflowDetail(Locale locae, Model model, HttpServletRequest request) throws Exception{
HttpSession session = request.getSession(false);
User user = (User)session.getAttribute("user");
String userId = user.getUserId();
String workflowNm = "";
String alsPpsCd = "";
String workflowDesc = "";
String pid = "";
String plevel = "";
if(request.getParameter("workflowNm") != null || !request.getParameter("workflowNm").equals("")) workflowNm = request.getParameter("workflowNm");
if(request.getParameter("alsPpsCd") != null || !request.getParameter("alsPpsCd").equals("")) alsPpsCd = request.getParameter("alsPpsCd");
if(request.getParameter("workflowDesc") != null || !request.getParameter("workflowDesc").equals("")) workflowDesc = request.getParameter("workflowDesc");
if(request.getParameter("pid") != null || !request.getParameter("pid").equals("")) pid = request.getParameter("pid");
if(request.getParameter("plevel") != null || !request.getParameter("plevel").equals("")) plevel = request.getParameter("plevel");
//0.
//HashMap<String,String> treeMgtInfo = new HashMap<String,String>();
//treeMgtInfo.put("treeId",pid);
//
HashMap<String,String> input = new HashMap<String,String>();
//2.
String praWfId = wfService.selectPraWfid();
input.put("praWfId", praWfId);
input.put("wfNm",workflowNm);
input.put("wfDesc", SecurityUtil.removeXSS(workflowDesc));
input.put("alsPpsCd",alsPpsCd);
input.put("usrId",userId);
input.put("rgUsrId",userId);
// 1.
wfService.insertWorkflowDetail(input);
input.put("treeNm",workflowNm);
//
int treeLevCd = new Integer(plevel) +1 ;
input.put("treeLevCd",""+treeLevCd);
input.put("treeBjCd",Constant.TREE_BJ_CD_WORKFLOW);
input.put("treeCd",Constant.TREE_CD_WORKFLOW);
//
input.put("treeLrkRufId",praWfId);
input.put("upTreeId",pid);
//
int sceXrsSeqVl = treeService.selectSceXrsSeqId(input);
input.put("sceXrsSeqVl",""+sceXrsSeqVl);
String treeId = treeService.selectTreeIdInfo();
input.put("treeId",treeId);
// 2.
rfmService.insertDefaultLParameter(request, praWfId);
// 3.
treeService.insertTreeMgtInfoWithId(input);
ModelAndView modelAndView=new ModelAndView("defaultViews");
modelAndView.addObject("treeId",treeId);
modelAndView.addObject("praWfId",praWfId);
return modelAndView;
}
This is all of code related on the sequence.

HBase Aggregation

I'm having some trouble doing aggregation on a particular column in HBase.
This is the snippet of code I tried:
Configuration config = HBaseConfiguration.create();
AggregationClient aggregationClient = new AggregationClient(config);
Scan scan = new Scan();
scan.addColumn(Bytes.toBytes("drs"), Bytes.toBytes("count"));
ColumnInterpreter<Long, Long> ci = new LongColumnInterpreter();
Long sum = aggregationClient.sum(Bytes.toBytes("DEMO_CALCULATIONS"), ci , scan);
System.out.println(sum);
sum returns a value of null.
The aggregationClient API works fine if I do a rowcount.
I was trying to follow the directions in http://michaelmorello.blogspot.in/2012/01/row-count-hbase-aggregation-example.html
Could there be a problem with me using a LongColumnInterpreter when the 'count' field was an int? What am I missing in here?
You can only use long(8bytes) to do sum with default setting.
Cause in the code of AggregateImplementation's getSum method, it handle all the returned KeyValue as long.
List<KeyValue> results = new ArrayList<KeyValue>();
try {
boolean hasMoreRows = false;
do {
hasMoreRows = scanner.next(results);
for (KeyValue kv : results) {
temp = ci.getValue(colFamily, qualifier, kv);
if (temp != null)
sumVal = ci.add(sumVal, ci.castToReturnType(temp));
}
results.clear();
} while (hasMoreRows);
} finally {
scanner.close();
}
and in LongColumnInterpreter
public Long getValue(byte[] colFamily, byte[] colQualifier, KeyValue kv)
throws IOException {
if (kv == null || kv.getValueLength() != Bytes.SIZEOF_LONG)
return null;
return Bytes.toLong(kv.getBuffer(), kv.getValueOffset());
}

Jqgrid search results pagination with Spring 3.0

I have several jqgrids running and all are functioning fine. However, when I do a search, I am only displaying ten search results per page. Whenever there are more than ten results, clicking on page two has no effect on the grid. Here is one of my controller actions, pay particular attention to the if satatement where search is true....
EDIT
I think I may have found a clue as to what may be causing my issue. You see I have several subgrids under the main grid. In terms of my java code I have object-A which has a list of object-B
, thus A has a subgrid of B. The way i am building up the json string to feed to the grid is by iterating over the list of B contained in A. I did not write a query of some kind to say order by, and limit the results etc.
So i guess the real question should be how to build a finder on a collection so that the contents can be arranged and ordered as i wish?
Here is the action I am calling for one of my entities described as B above. Pay particular attention to where i said person.getContacts()
#RequestMapping(value = "contactjsondata/{pId}", method = RequestMethod.GET)
public #ResponseBody String contactjsondata(#PathVariable("pId") Long personId, Model uiModel, HttpServletRequest httpServletRequest) {
Person person = Person.findPerson(personId);
String column = "id";
if(httpServletRequest.getParameter("sidx") != null){
column = httpServletRequest.getParameter("sidx");
}
String orderType = "DESC";
if(httpServletRequest.getParameter("sord") != null){
orderType = httpServletRequest.getParameter("sord").toUpperCase();
}
int page = 1;
if(Integer.parseInt(httpServletRequest.getParameter("page")) >= 1){
page = Integer.parseInt(httpServletRequest.getParameter("page"));
}
int limitAmount = 10;
int limitStart = limitAmount*page - limitAmount;
List<Contact> contacts = new ArrayList<Contact>(person.getContacts());
double tally = Math.ceil(contacts.size()/10.0d);
int totalPages = (int)tally;
int records = contacts.size();
StringBuilder sb = new StringBuilder();
sb.append("{\"page\":\"").append(page).append("\", \"records\":\"").append(records).append("\", \"total\":\"").append(totalPages).append("\", \"rows\":[");
boolean first = true;
for (Contact c: contacts) {
sb.append(first ? "" : ",");
if (first) {
first = false;
}
sb.append(String.format("{\"id\":\"%s\", \"cell\":[\"%s\", \"%s\", \"%s\"]}",c.getId(), c.getId(), c.getContactType().getName() ,c.getContactValue()));
}
sb.append("]}");
return sb.toString();
}
To fix the issue with pagination you need to replace the following block of code
for (Contact c: contacts) {
sb.append(first ? "" : ",");
if (first) {
first = false;
}
sb.append(String.format("{\"id\":\"%s\", \"cell\":[\"%s\", \"%s\", \"%s\"]}",c.getId(), c.getId(), c.getContactType().getName() ,c.getContactValue()));
}
with:
for (int i=limitStart; i<Math.min(records, limitStart+limitAmount); i++){
Contact c = contacts[i];
sb.append(first ? "" : ",");
if (first) {
first = false;
}
sb.append(String.format("{\"id\":\"%s\", \"cell\":[\"%s\", \"%s\", \"%s\"]}",c.getId(), c.getId(), c.getContactType().getName() ,c.getContactValue()));
}
Another option is using loadonce:true to let the jqGrid handle pagination and sorting. In this case you don't need to make changes described above

Resources