Spring Boot Pagination - java

I'm facing the problem for pagination data. Page data is getting calculated using the outer array dXPRecommendationResponses where I want it should be calculated through the nested array recommendations. I've mentioned the response data in result section.
I tried to change the page data calculation but it is getting calculated using the the data which we are passing in PageImpl() constructor.
I've used this approach to paginate the data which we are getting from third party API.
But it is getting calculated using the dxpRecommendationslist.
#This is the code through I need to generate the response
List<DXPRecommendationResponse> dxpRecommendationslist = new ArrayList<>();
List<DXPActivity> dxpActivities = getThirdResponse(pageable, correlationId,
dxpRecommendationslist, reservationGuestId, reservationId, nbxRecommendationRequest);
return new PageImpl<>(dxpRecommendationslist, pageable, dxpActivities.size());
#This method is used to get data from third party
public List<DXPActivity> getThirdResponse(final Pageable pageable, final String correlationId,
List<DXPRecommendationResponse> list, String reservationGuestId, String reservationId,
NBXRecommendationRequest nbxRecommendationRequest) {
List<DXPActivity> dxpActivities = new ArrayList<>();
NBXRecommendationResponse nbxRecommendationResponse = vVNBXRecommendationService
.getCalendarRecommendation(nbxRecommendationRequest, correlationId);
if (nbxRecommendationResponse != null) {
DXPRecommendationResponse dxpRecommendationResponse = new DXPRecommendationResponse();
dxpRecommendationResponse.setReservationGuestId(reservationGuestId);
dxpRecommendationResponse.setReservationNumber(reservationId);
dxpRecommendationResponse.setRecommendationType("CalendarType");
dxpRecommendationResponse.setDateTime(new Date());
populateActivities(dxpActivities, nbxRecommendationResponse);
List<DXPActivity> filteredList;
if (!CollectionUtils.isEmpty(dxpActivities)
&& dxpActivities.size() >= (pageable.getPageSize() * pageable.getPageNumber())) {
filteredList = Lists.partition(dxpActivities, pageable.getPageSize()).get(pageable.getPageNumber());
} else {
filteredList = new ArrayList<DXPActivity>();
}
dxpRecommendationResponse.setRecommendations(filteredList);
list.add(dxpRecommendationResponse);
}
return dxpActivities;
}
#This method is used to populate data
private void populateActivities(List<DXPActivity> dxpActivities,
NBXRecommendationResponse nbxRecommendationResponse) {
for (Activity activity :nbxRecommendationResponse.getCalendarRecommendation().getRecommendations().getActivities()) {
DXPActivity dxpActivity = new DXPActivity();
orikaMapper.map(activity, dxpActivity);
dxpActivities.add(dxpActivity);
}
}
The response data:
{
"_embedded": {
"dXPRecommendationResponses": [
{
"recommendationType": "CalendarType",
"reservationGuestId": "525dab66-1492-4908-a3bf-b5de558368e5",
"reservationNumber": "3a39f9ad-7e34-4bdb-91eb-b907fd6986c7",
"dateTime": "2019-08-19T14:38:18.413",
"recommendations": [
{
"productCode": "BIKE2006111000",
"activityName": "Bimini Bike Tour",
"recommendationId": "1565948843492_410387839_BIKE2006111000_cal",
"categoryCode": "DARING",
"activityStartTime": "2020-06-11T08:30:00.000",
"activityEndTime": "2020-06-11T11:30:00.000",
"activityDescription": "Bimini Bike Tour",
"sequence": 34.0,
"packageId": 103806,
"sourceId": "BIMINI BIKE",
"levelOfActivity": "EASY"
},
{
"productCode": "CUL2006110900",
"activityName": "Bimini Culinary Tour",
"recommendationId":"156594884349,
"categoryCode": "CULTURED",
"activityStartTime": "2020-06-11T07:30:00.000",
"activityEndTime": "2020-06-11T12:30:00.000",
"activityDescription": "Bimini Culinary Tour",
"sequence": 29.0,
"packageId": 103940,
"sourceId": "BIMINI CUL",
"levelOfActivity": "MODERATE"
}
]
}
]
},
"page": {
"size": 10,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}

The problem here is implementation of PageImpl constructor and how the resulting total is being computed here. Below is code taken from the constructor:
this.total = pageable.toOptional().filter(it -> !content.isEmpty())//
.filter(it -> it.getOffset() + it.getPageSize() > total)//
.map(it -> it.getOffset() + content.size())//
.orElse(total);
The important line here is second one, as described in doc comment, it is insurance against inconsistensies. It checks if you are on the last page and if so, it will use third line to compute resulting total. Therefore totally omitting total given by you. If you want to rewrite this behavior you should implement your Page which uses only given total.
BUT, I don't think it is semantically correct. You are mixing together things which probably should not be mixed (page object of one type, but the actual pagination done on another). The list given as pageable always has one element and you are paging subelements which does not seem right, is there even need to paginate the subelements?
One of the possible resolutions would be to make pageable only subelements by specifying Page<DXPActivity> recommendations member directly inside DXPRecommendationResponse and returning non-pageable DXPRecommendationResponse. But again, it seems a bit off. It really depens on what you are trying to build here and what is the logic behind that.

Related

How to create a deep insert structure using Olingo Client?

How to POST the following JSON structure using Apache OLINGO client? What is the best way to build up this structure? Are there any examples?
{
"itemNumber": "ITEM1"
"lines": [
{
componentNumber": "COMPONENT1"
},
{
componentNumber": "COMPONENT2"
}
]
}
The following Java example using the Olingo Client works for me to post the following JSON structure:
{
"itemNumber": "ITEM1"
"lines": [
{
componentNumber": "COMPONENT1"
},
{
componentNumber": "COMPONENT2"
}
]
}
Java code using OLINGO CLient
public void deepInsertExample(){
//Initiate the ODATA client
ODataClient client = ODataClientFactory.getClient();
client.getConfiguration();
getClient().getObjectFactory();
//Initiate the Client Object Factory
ClientObjectFactory factory = getClient().getObjectFactory();
//Create Line Item 1
ClientEntity lineItem1 = factory.newEntity(new FullQualifiedName("ODATA.LineItem"));
lineItem1.getProperties()
.add(factory.newPrimitiveProperty("componentNumber", factory.newPrimitiveValueBuilder().buildString("COMPONENT2")));
//Create Line Item 2
ClientEntity lineItem2 = factory.newEntity(new FullQualifiedName("ODATA.LineItem"));
lineItem2.getProperties()
.add(factory.newPrimitiveProperty("componentNumber", factory.newPrimitiveValueBuilder().buildString("COMPONENT1")));
//Initiate the entity set
ClientEntitySet entitySet = factory.newEntitySet();
//Add Line Item 1 and Line Item 2 to the Enity
entitySet.getEntities().add(lineItem1);
entitySet.getEntities().add(lineItem2);
//Create the Lines LInk
ClientLink linesLink = factory.newDeepInsertEntitySet("Lines", entitySet);
ClientComplexValue complexValueCreate = factory.newComplexValue("Lines");
complexValueCreate.getNavigationLinks().add(linesLink);
//Create the Item object
ClientEntity item = factory.newEntity(new FullQualifiedName("ODATA.Item"));
item.getProperties()
.add(factory.newPrimitiveProperty("itemNumber", factory.newPrimitiveValueBuilder().buildString("ITEM1")));
//Add the Lines(Entity Set) link to Item Object
item.addLink(linesLink);
//Post the Item
URI absoluteUri = client.newURIBuilder("URL").build();
ODataEntityCreateRequest<ClientEntity> request = client.getCUDRequestFactory()
.getEntityCreateRequest(absoluteUri, item);
request.setAccept("application/json;odata.metadata=minimal");
request.execute();
}
You will have to specify the the NavigationPropertyName in the deep part. So for your sample payload it would look like
{
"itemNumber": "ITEM1",
"lines": {
"componentNumber":"COMPONENT1",
"componentNumber":"COMPONENT2",
}
}
You can refer to this post in SO to get details about n level nesting
The above answer asuumes that your NavigationProperty is named lines, you can substitute it with the right name by looking at service/$metadata
The answer assumes that you are trying to do deep inserts in a odata2 service, for OData4 the concept remains the same but syntax might vary a bit. Please refer to the payload descripted in documentation in case of OData 4

Access the DTO elements

I am stuck with an issue and not sure how to proceed with it.
I have a DTO something like this :-
ORDER DTO:-
[
{
"ordNo": "77456",
"patients" :[ :- THIS DTO IS SHOTPATIENTDTO
{
"patientFirstName" : "Test",
"patientLastName" : "Dummy",
"patientDob" : "2018-01-04",
"patientUr" : "12345",
"patientId" : "1",
"batches" :[ //:------SHOTTEMPBACTH DTO
{
"batchId":"37", :- for each of the ID, I will set the BATCHDTO
"treatmentDateTime": "2017-12-06 17:55:50",
"status":"Submitted"
},
{
"batchId":"38",
"treatmentDateTime": "2017-12-06 17:55:50",
"Status":"On Hold"
}]
},
{
"patientFirstName" : "Second",
"patientLastName" : "Dummy",
"patientDob" : "2018-01-04",
"patientUr" : "542",
"patientId" : "1",
"batches" :[
{
"batchId":"39",
"treatmentDateTime": "2017-12-06 17:55:50",
"status":"Submitted"
},
{
"batchId":"40",
"treatmentDateTime": "2017-12-06 17:55:50",
"Status":"On Hold"
}]
}]
}]
I have an enitity which is ORDER having one to many relationship with BATCH TABLE.
Order which has a list of Batches(retrieve the batch using the batchId and set the object using the ORDER DTO) and there is another entity as SHOT_BATCH which conists of Batch + some additional attributes like patient details.
So while constructing my shotbatch entity for saving,
I am doing like this
[1]
for ( each of the order, get me the batches)
{
create an object of SHOTBATCH and set the properties
}
DAO.save(SHOTBATCH)
My question, I would like to set the patient name for each of the batches. How shall I iterate in order to get that ? In the above example, I would be
getting 4 batches(37-40) in the above loop[1]. so how can I determine which patient belongs to which batch so that I can set it in the SHOTBATCH.
Appreciate your help.
Please let me know if I got something wrong about your question. I understand that you want to build a collection and batch save it rather that calling DAO.save for every batchShot object. I would approach it in this way:
Lets say that we have the BatchShot object:
class ShotBatch {
private List<Shot> shots;
private String patientName;
public ShotBatch(final String patientName, final Shot[] shots) {
this.shots = Arrays.asList(shots);
this.patientName = patientName;
}
// getters setters
}
Then the create and save code would be:
List<ShotBatch> patientBatches = new ArrayList<>();
for (final Order order: orders) {
for (final Patient patient: order.getPatients()) {
// Assuming that patien has method getFullName that returns a String
// and getShots that returns an array of Shots
patientBatches.add(new ShotBatch(patient.getFullName(), patient.getShots()));
}
}
ShotBatchDao.saveAll(patientBatches);

Pulling JSON data from URL in Android 2017

Before I get started, could I just point out I've spent the last 5 hours going in circles here. I've tried what seems like every StackOverflow method out there to pull what seems like a simple int from an API. This seems like it should be way easier, but isn't.
What I'm trying to achieve is pull the JSON info from this URL:
http://api.apixu.com/v1/current.json?key=ENTERKEYHERE&q=leeds
And then gather the category "temp_c", which should be something along the lines of "7.0". Below is an example of how a call looks:
{
"location": {
"name": "Leeds",
"region": "West Yorkshire",
"country": "United Kingdom",
"lat": 53.81,
"lon": -1.54,
"tz_id": "Europe/London",
"localtime_epoch": 1509670055,
"localtime": "2017-11-03 0:47"
},
"current": {
"last_updated_epoch": 1509669008,
"last_updated": "2017-11-03 00:30",
"temp_c": 7,
"temp_f": 44.6,
"is_day": 0,
"condition": {
"text": "Partly cloudy",
"icon": "//cdn.apixu.com/weather/64x64/night/116.png",
"code": 1003
},
"wind_mph": 4.3,
"wind_kph": 6.8,
"wind_degree": 150,
"wind_dir": "SSE",
"pressure_mb": 1015,
"pressure_in": 30.4,
"precip_mm": 0,
"precip_in": 0,
"humidity": 93,
"cloud": 75,
"feelslike_c": 5.8,
"feelslike_f": 42.4,
"vis_km": 10,
"vis_miles": 6
}
}
I've successfully gotten the data to pull into an app during random points in the night, however have been unable to pull the specific piece of data at all. Currently, my app won't pull it at all and I'm not sure what I did before.
I'm truly sorry if this is a question deemed "duplicate", but I can assure you that for someone trying to learn the language, none of the answers fully explain how to do this using the newer API's (Some of the answers seem to no longer work in API 23+)
I'm not looking for a detailed app with loads of methods, just a simple way of pulling the JSON data and selecting a specific category. I'd be extremely grateful if you could explain how to do this as I plan on adapting the code in the future for other sources. Thank you!
You should use an AsyncTask or AsyncTaskLoader so the UI-Thread doesn't lag during loading. Search for this term or use the UI thread, but then it will lag during the load. Eitherways, call this function, with url being your url:
public static String getResponse(URL url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = urlConnection.getInputStream();
Scanner scanner = new Scanner(in);
scanner.useDelimiter("\\A");
boolean hasInput = scanner.hasNext();
if (hasInput) {
return scanner.next();
} else {
return null;
}
} finally {
urlConnection.disconnect();
}
The url can be gotten by calling
url = new URL(yourAddressAsAString)
The upper functoin should return a string. Convert it into a JSON Object
JSONObject obj = null;
try {
obj = new JSONObject(yourString);
} catch (Throwable t) { }
Then call
String tempc = String.valueOf(obj.getJSONObject("current").getJSONObject("temp_c"))
To get the Object in question. Good luck!
First step take your whole Json as a String lets say String json_string and here is what you have to do (Read the comments in code for further information):
String json_string="your json string here as a normal String json object";
try {
JSONObject jsonObjectRoot=new JSONObject(json_string);
JSONObject locationObject=jsonObjectRoot.getJSONObject("location");
String name=locationObject.getString("name"); //This will return leeds
String region=locationObject.getString("region"); //This will return West YorkShire!
}catch (JSONException json_except){
//You have an exception so check if your items really exist with those names!
json_except.printStackTrace();
}
So from the above example using your own data you need to know is:
All Json code should be in a try block and catch Json exception.
To get a json object from a string just a constructor itself like new JSONObject(string);
To get any inner json object (json object within json object) we use the method outer_object.getJSONObject("string_name_of_inner_object");and continue deeper until you get your object.
To get a string within an object we use String string=json_object.getString("the_string_name_as_it_appears_in_the_object");
To get a Json Array inside an object you use JSONArray jsonArray=your_json_object.getJSONArray("array_name_as_it_appears_in_json");
I hope this will help you!

Cloudant With Lucene Search Fails To Sort As Expected

I am pretty new to Cloudant but have developed in SQL on DB2 for some time. I am running into an issue where I *think I am using the Lucene query engine and Cloudant indexes to return results from my query. The query gets all the results I want however, they are not sorted correctly. I am wanting to sort the results alphabetically based on the "officialName" field. Because we are only returning the first 21 out of n results (and then we have a js handler to call more results via paging) we cannot sort in the java side but must do so via Cloudant. Our application is running Java and executed using IBM's Bluemix and WebSphere Liberty Profile. I have packaged the cloudant-client-2.8.0.jar and cloudant-HTTP-2.8.0.jar files to access the Cloudant database. We have many queries that are working so the connection itself is fine.
Here is the code that builds the Cloudant Client search object:
Search search = getCloudantDbForOurApp().search("bySearchPP-ddoc/bySearchPP-indx").includeDocs(true);
SearchResult<DeliverableDetails> result = search.sort(getSortJsonString(searchString)).querySearchResult(getSearchQuery(searchString), DeliverableDetails.class);
Here is the method getSortJsonString. It should be noted that the search string is typically NOT null. I should also note that leaving in or taking out the -score attribute does effect the search but never achieves alpha sorted results.
private String getSortJsonString(String searchString) {
String sortJson;
if (searchString != null && !searchString.isEmpty()) {
sortJson = "[\"-<score>\",\"officialName<string>\"]";
} else {
sortJson = "\"officialName<string>\"";
}
return sortJson;
}
Here is the getSearchQuery method's relevant code for reference:
...
query += "(";
query += "officialName:" + searchString + "^3";
query += " OR " + "deliverableName:" + searchString + "^3";
query += " OR " + "alias:" + searchString + "^3";
query += " OR " + "contact:" + searchString;
query += ")";
....
// The query will look like below, where<search_string> is some user inputted value
// (officialName:<search_string>*^3 OR deliverableName:<search_string>*^3 OR alias:<search_string>*^3 OR contact:<search_string>*)
I have setup a design doc and index using the Cloudant dashboard as follows:
{
"_id": "_design/bySearchPP-ddoc",
"_rev": "4-a91fc4ddeccc998c58adb487a121c168",
"views": {},
"language": "javascript",
"indexes": {
"bySearchPP-indx": {
"analyzer": {
"name": "perfield",
"default": "standard",
"fields": {
"alias": "simple",
"contact": "simple",
"deploymentTarget": "keyword",
"businessUnit": "keyword",
"division": "keyword",
"officialName": "simple",
"deliverableName": "simple",
"pid": "keyword"
}
},
"index": "function(doc) {
if (doc.docType === \"Page\") {
index(\"officialName\", doc.officialName, {\"store\":true, \"boost\":4.0});
index(\"deliverableName\", doc.deliverableName, {\"store\":true, \"boost\":3.0});
if (doc.aliases) {
for (var i in doc.aliases) {
index(\"alias\", doc.aliases[i], {\"store\":true, \"boost\":2.0});
}
}
if (doc.allContacts) {
for (var j in doc.allContacts) {
index(\"contact\", doc.allContacts[j], {\"store\":true, \"boost\":0.5});
}
}
index(\"deploymentTarget\", doc.deploymentTarget, {\"store\":true});
index(\"businessUnit\", doc.businessUnit, {\"store\":true});
index(\"division\", doc.division, {\"store\":true});
index(\"pid\", doc.pid.toLowerCase(), {\"store\":true});
}
}"
}
}
}
I am not sure if the sort is working and just not working how I want it to or if I have misconfigured something. Either way, any help would be greatly appreciated. -Doug
Solved my own issue w/ help from comments above. Apparently everything was setup correctly but once I debug per #markwatsonatx I could see the field I wanted wasn't being returned. Did some digging online and apparently for sort the field must be both indexed and NOT tokenized. Thus I checked my index and noticed that the filed was being analyzed by the Simple analyzer. Changed it to the Keyword and the sort works as expected. Hoep this helps someone.

Mongo DB Aggregate Query returns in Batches

I have the following code, :
CommandResult cr = db.doEval("db." + collectionName + ".aggregate("
+ query + ")");
Command result is giving in batches, where I need to get in single value.
Batch Result:{ "serverUsed" : "/servername" , "retval" : { **"_firstBatch**" : [ { "visitor_localdate" : 1367260200} , { "visitor_localdate"
Expected Result:
{ "serverUsed" : "/servername" , "retval" : { "**result**" : [ { "visitor_localdate" : 1367260200} , { "visitor_localdate"
The Mongo DB we are using is 2.6.4 with 64 bit.
Can any one help with this?. I am guessing there is some Configuration issue.
Your doing this all wrong. You don't need to jump through hoops like this just to get a dynamic collection name. Just use this syntax instead:
var collectionName = "collection";
var cursor = db[collectionName].aggregate( pipeline )
Where pipeline also is just the array of pipeline stage documents, ie:
var pipeline = [{ "$match": { } }, { "$group": { "_id": "$field" } }];
At any rate the .aggregate() method returns a cursor, you can iterate the results with standard methods:
while ( cursor.hasNext() ) {
var doc = cursor.next();
// do something with doc
}
But you are actually doing this in Java and not JavaScript, so from the base driver with a connection on object db you just do this:
DBObject match = new BasicDBObject("$match", new BasicDBObject());
DBObject group = new BasicDBObject("$group", new BasicDBObject());
List pipeline = new ArrayList();
pipeline.add(match);
pipeline.add(group);
AggregationOutput output = db.getCollection("collectionName").aggregate(pipeline);
The pipeline is basically a list interface of DBObject information where you construct the BSON documents representing the operations required.
The result here is of AggregationOutput, but cursor like results are obtainable by additionally supplying AggregationOptions as an additional option to pipeline
There was something related to bacth added in mongodb 2.6, more details here: http://docs.mongodb.org/manual/reference/method/db.collection.aggregate/#example-aggregate-method-initial-batch-size
From the link
db.orders.aggregate(
[
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 2 }
],
{
cursor: { batchSize: 0 }
}
)
You might be having a cursor batch in your aggregate query
The answer from Neil Lunn is not wrong but I want to add that the result you were expecting is a result for mongodb versions earlier than v2.6.
Before v2.6, the aggregate function returned just one document containing a result field, which holds an array of documents returned by the pipeline, and an ok field, which holds the value 1, indicating success.
However, from mongodb v2.6 on, the aggregate function returns a cursor (if $out option was not used).
See examples in mongodb v2.6 documentation and compare how it worked before v2.6 (i.e. in v2.4):

Categories