effective way to loopoever three array lists to compare values - for-loop

I have two object of arraylist orderList, productList and one String arraylist customerIdList.I have ProductInfo POJO to be mapped with orderList and productList where cuustomerId should match.If I don't have order or productlist for given ProdId I should add standard Error and map to the ProductInfo Error.Here is what I am doing ...
public class ProductInfo {
private List<ProductDetails> products;
private List<Error> errors;
private String customerId;
}
public class ProductDetails {
private String customerId;
private Order order;
private Product product;
private List<Error> errors;
}
Sample result ...
{
"productInfo": {
"customer_id": "123",
"product_details": [
{
"customer_id": "123",
"order_details": null,
"product_details": {
"customer_id": "123"
"product_id" : "2343"
"product_name": "XYZ",
"product_type": "PQR"
...
},
"errors": [
"error_code":"6001",
"error_desc":"Failure in getting Order information from Order Service"
]
},
{
"order_details": {
"customer_id":"123"
"order_id": "3543454",
"order_date":"2016-10-12",
"order_status":"ordered"
},
"product_details": null,
"errors": [
"error_code":"6001",
"error_desc":"Failure in getting Product information from Product Service"
]
}
],
"system_errors":[]
}
}
Looping over ArrayList and Mapping
for(String customerId : customerIdList) {
for(Product product: productList) {
for(SOrder ordr: orderList) {
if(customerId.equals(product.getCustomerId()) && customerId.equals(Order.getCustomerId()) ) {
ModelMapper mapper = new ModelMapper();
Order order = mapper.map(ordr, Order.class));
productDetails.setOrder(order);
//mapping to ProductInfo
productDetailsList.add(productDetails);
}
}
}
}
I want to know if there is any better way of doing this and also I am using ModelMapper to map SOrder to Order POJO andother POJOs would like to know if there is any other efficient model mapper available.Thanks.

you can create maps from productList and orderList with customerId as key
Map<String, Product> productMap = productList.stream().collect(Collectors.toMap(p -> p.getCustomerId(), p -> p));
Map<String, Product> orderMap = orderList.stream().collect(Collectors.toMap(o -> o.getCustomerId(), o -> o));
then with just one loop you can check if there is product and order for that customer id
for(String customerId : customerIdList) {
if (productMap.containsKey(customerId) && orderMap.containsKey(customerId)) {
//do your mapping stuff here
}
}

Related

Elasticsearch query on array of composite objects along with date ranges

Hi I have a question on how to create an elastic search query for a nested composite object with date ranges and additional field parameters like so
[{
"name": "A",
"availability": [
{
"partial": true,
"dates": {
"gte": "2020-12-01",
"lte": "2020-12-02"
}
}
]
},
{
"name": "B",
"availability": [
{
"partial": true,
"dates": {
"gte": "2020-12-05",
"lte": "2020-12-06"
}
},
{
"partial": false,
"dates": {
"gte": "2020-12-08",
"lte": "2020-12-11"
}
}
]
}]
This is my entity data
#Document(indexName = "workers")
public class Worker {
#Id
private String id;
#Field(type = FieldType.Text)
private String name;
#Field(type = FieldType.Nested)
private List<Availability> availability;
}
public class Availability {
#Field(type = FieldType.Boolean)
private boolean partial;
#Field(type = FieldType.Date_Range, format = DateFormat.custom, pattern = "uuuu-MM-dd")
private Map<String, LocalDate> dates;
}
This is the search query that I have currently written, but the results come as empty
final BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.must(QueryBuilders.termQuery("availability.partial", query.isPartial()));
RangeQueryBuilder availability = QueryBuilders.rangeQuery("availability.dates")
.gte(query.getStartDate())
.lte(query.getEndDate());
queryBuilder.must(availability);
Pageable pageable = PageRequest.of(pageNumber, pageSize);
// #formatter:off
return new NativeSearchQueryBuilder()
.withPageable(pageable)
.withQuery(queryBuilder)
.build();
This is my query dto
public class WorkerQuery {
private boolean partial;
private LocalDate startDate;
private LocalDate endDate;
}
// Request data
{
"partial": true,
"startDate": "2020-12-01",
"endDate": "2020-12-02"
}
Great start!! You're just missing a nested query since availability is nested. The Java query needs to be like this:
final BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.must(QueryBuilders.termQuery("availability.partial", query.isPartial()));
RangeQueryBuilder availability = QueryBuilders.rangeQuery("availability.dates")
.gte(query.getStartDate())
.lte(query.getEndDate())
.relation("within");
queryBuilder.must(availability);
final NestedQueryBuilder nested = QueryBuilders.nestedQuery("availability", queryBuilder);
Pageable pageable = PageRequest.of(pageNumber, pageSize);
// #formatter:off
return new NativeSearchQueryBuilder()
.withPageable(pageable)
.withQuery(nested)
.build();

springboot mongodb crud update only changed fields

Hello i have springboot with mongodb (spring-boot-starter-data-mongodb)
My problem is if I send only one or only the fields I want to change so the other values are set to null. I found something on the internet like #DynamicUpdate but not working on mongodb can you help me with this problem. I'm a beginner, I don't know how to help and it's quite important for me, if you need more code or more information, I'll write in the comment. I hope I have described the problem sufficiently. :)
MY POJO:
#Data
#Getter
#Setter
#NoArgsConstructor
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
private boolean enabled;
private String note;
Repo
#Repository
public interface PersonRepository extends MongoRepository <Person, String> {
}
i have this call
#PutMapping("/{id}")
#ResponseBody
public void UpdatePerson (#PathVariable String id , #RequestBody Person person) {
personRepository.save(person);
}
#GetMapping(path = "/{id}")
public Person getPersonByid(#PathVariable String id ){
return personRepository.findById(id).orElseThrow(PersonNotFound::new);
}
sample:
get call before update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Rambo",
"lastName": "Norris",
"enabled": true,
"note": "hello this is my first note from you",
}
put call :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck"
}
get call after update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck",
"lastName": null,
"enabled": false,
"note": null,
}
what I would like
get call before update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Rambo",
"lastName": "Norris",
"enabled": true,
"note": "hello this is my first note from you",
}
put call :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck"
}
get call after update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck",
"lastName": "Norris",
"enabled": true,
"note": "hello this is my first note from you",
}
You are inserting a new collection instead of updating. First, you need to get the old value from mongodb, then you need to update the collection, then save to DB.
Use the below code in #putmapping.
#PutMapping("/{id}")
#ResponseBody
public void UpdatePerson (#PathVariable String id , #RequestBody Person person) {
Person personFromDB = personRepository.findById(person.getId());
personFromDB.setFirstName(person.getFirstName());
personRepository.save(personFromDB);
}
Try updating like this
#PutMapping("/{id}")
public ResponseEntity<Person> UpdatePerson (#PathVariable String id , #RequestBody
Person person) {
Optional<Person> personData = personRepository.findById(id);
if (personData.isPresent()) {
Person _tutorial = personData.get();
if(!StringUtils.isEmpty(person.getFirstName())) {
_tutorial.setFirstName(person.getFirstName());
}
if(!StringUtils.isEmpty(person.getLastName())) {
_tutorial.setLastName(person.getLastName());
}
if(!StringUtils.isEmpty(person.getNote())) {
_tutorial.setNote(person.getNote());
}
if(!StringUtils.isEmpty(tutorial.isEnabled())) {
_tutorial.setEnabled(tutorial.isEnabled());
}
return new ResponseEntity<>(repo.save(_tutorial), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

Spring Boot - Get Data from DB and store it in list and parse it to JSON using jackson

I'm trying to get data from multiple tables and put it in Array List of class, and then convert it to JSON Object.
But when i'm trying to parse it to json using Jackson Object Mapper all the lists are converted as below
Using ObjectMapper().writeValueAsString for deserialization from class objects to json
```{
"College": [
{
"institution": [
{
"instId": "T34",
"Country": "India",
"Code": "T33"
},
{
"instId": "T22",
"Country": "India",
"Code": "T22"
}
],
"Rating": [
{
"star": "4"
"comments": "good"
},
{
"star": "2"
"comments": "ok"
},
}
]
}```
But i want the result as below
{
"College": [
{
"institution": [
{
"instId": "T34",
"Country": "India",
"Code": "T33"
}
],
"Rating": [
{
"star": "4"
"comments": "good"
}
]
},
{
"institution": [
{
"instId": "T22",
"Country": "India",
"Code": "T22"
}
],
"Rating": [
{
"star": "2"
"comments": "ok"
}
]
}
]
}
The above is just an example.
Please help in getting the desired output.
Below are the class files used.
public class AllCollege{
List<College> college = new ArrayList<>();
public List<College> getCollege() {
return college;
}
public void setCollege(List<College> college) {
this.college = college;
}
}
public class College{
private List<Institution> institution = new ArrayList<>();
private List<Rating> rating = new ArrayList<>();
public List<Institution> getInstitution() {
return institution;
}
public void setInstitution(List<Institution> institution) {
this.institution = institution;
}
public List<Rating> getRating() {
return rating;
}
public void setRating(List<Rating> rating) {
this.rating = rating;
}
}
public class Institution {
private String instId;
private String country;
private String code;
public String getInstId() {
return instId;
}
public void setInstId(String instId) {
this.instId = instId;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
public class Rating {
private String star;
private String comments;
public String getStar() {
return star;
}
public void setStar(String star) {
this.star = star;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
}
Below is where the data from tables is set into ArrayList and then converted to json string.
session = sessionFactory.openSession();
String sql = "from institution";
Query<InstDto> query = session.createQuery(sql);
List<Institution> configdtoList =query.list();
College alc = new College();
alc.setInstitution(configdtoList);
.
.
.
similarly Rating table.
List<College> clist = new new ArrayList<>();
clist.add(alc);
AllCollege ac = new AllCollege();
ac.setCollege(clist);
String responseJson = new ObjectMapper().writeValueAsString(ac)
class structure as below it will help you to parse:
public class Sample {
#JsonProperty("College")
private List<College> college;
}
public class College {
private List<Institution> institution;
#JsonProperty("Rating")
private List<Rating> rating;
}
public class Rating {
private String comments;
private String star;
}
public class Institution {
#JsonProperty("Code")
private String code;
#JsonProperty("Country")
private String country;
private String instId;
}
I have created an HashMap contains the List<AllCollege> as value and then used json parser which worked as expected.

Nested Group with Spring MongoDB

I need to generate a result with the number of alerts of each level for each user.
A structure similar to the following:
{
"identitity": "59e3b9dc5a3254691f327b67",
"alerts": [
{
"level": "INFO",
"count": "3"
},
{
"level": "ERROR",
"count": "10"
}
]
}
The alert entitity has the following structure:
#Document(collection = AlertEntity.COLLECTION_NAME)
public class AlertEntity {
public final static String COLLECTION_NAME = "alerts";
#Id
private ObjectId id;
#Field
private AlertLevelEnum level = AlertLevelEnum.INFO;
#Field("title")
private String title;
#Field("payload")
private String payload;
#Field("create_at")
private Date createAt = new Date();
#Field("delivered_at")
private Date deliveredAt;
#Field("delivery_mode")
private AlertDeliveryModeEnum deliveryMode =
AlertDeliveryModeEnum.PUSH_NOTIFICATION;
#Field("parent")
#DBRef
private ParentEntity parent;
#Field("son")
#DBRef
private SonEntity son;
private Boolean delivered = Boolean.FALSE;
}
I have implemented the following method tried to project the result in a nested way. But the "Identity" field is always null and the "alerts" field is a empty collection.
#Override
public List<AlertsBySonDTO> getAlertsBySon(List<String> sonIds) {
TypedAggregation<AlertEntity> alertsAggregation =
Aggregation.newAggregation(AlertEntity.class,
Aggregation.group("son.id", "level").count().as("count"),
Aggregation.project().and("son.id").as("id")
.and("alerts").nested(
bind("level", "level").and("count")));
// Aggregation.match(Criteria.where("_id").in(sonIds)
AggregationResults<AlertsBySonDTO> results = mongoTemplate.
aggregate(alertsAggregation, AlertsBySonDTO.class);
List<AlertsBySonDTO> alertsBySonResultsList = results.getMappedResults();
return alertsBySonResultsList;
}
The result I get is the following:
{
"response_code_name": "ALERTS_BY_SON",
"response_status": "SUCCESS",
"response_http_status": "OK",
"response_info_url": "http://yourAppUrlToDocumentedApiCodes.com/api/support/710",
"response_data": [
{
"identity": null,
"alerts": []
},
{
"identity": null,
"alerts": []
}
],
"response_code": 710
}
The result DTO is as follows:
public final class AlertsBySonDTO implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty("identity")
private String id;
#JsonProperty("alerts")
private ArrayList<Map<String, String>> alerts;
public AlertsBySonDTO() {
super();
}
public AlertsBySonDTO(String id, ArrayList<Map<String, String>> alerts) {
super();
this.id = id;
this.alerts = alerts;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public ArrayList<Map<String, String>> getAlerts() {
return alerts;
}
public void setAlerts(ArrayList<Map<String, String>> alerts) {
this.alerts = alerts;
}
}
What needs to be done to project the result in a nested way?
Thanks in advance
In aggregation framework there is an $unwind operator which will basically transform your one element collection with nested array of two elements to two separate documents with one element from this array. So you'll get:
{
"identitity": "59e3b9dc5a3254691f327b67",
"alerts": {
"level": "INFO",
"count": "3"
}
}
{
"identitity": "59e3b9dc5a3254691f327b67",
"alerts": {
"level": "ERROR",
"count": "10"
}
}
And this is where you can start your group by with count. Should be working fine.

Adding more information to the HATEOAS response in Spring Boot Data Rest

I have the following REST controller.
#RepositoryRestController
#RequestMapping(value = "/booksCustom")
public class BooksController extends ResourceSupport {
#Autowired
public BooksService booksService;
#Autowired
private PagedResourcesAssembler<Books> booksAssembler;
#RequestMapping("/search")
public HttpEntity<PagedResources<Resource<Books>>> search(#RequestParam(value = "q", required = false) String query, #PageableDefault(page = 0, size = 20) Pageable pageable) {
pageable = new PageRequest(0, 20);
Page<Books> booksResult = BooksService.findBookText(query, pageable);
return new ResponseEntity<PagedResources<Resource<Books>>>(BooksAssembler.toResource(BooksResult), HttpStatus.OK);
}
My Page<Books> BooksResult = BooksService.findBookText(query, pageable); is backed by SolrCrudRepository. When it is run BookResult has several fields in it, the content field and several other fields, one being highlighted. Unfortunately the only thing I get back from the REST response is the data in the content field and the metadata information in the HATEOAS response (e.g. page information, links, etc.). What would be the proper way of adding the highlighted field to the response? I'm assuming I would need to modify the ResponseEntity, but unsure of the proper way.
Edit:
Model:
#SolrDocument(solrCoreName = "Books_Core")
public class Books {
#Field
private String id;
#Field
private String filename;
#Field("full_text")
private String fullText;
//Getters and setters omitted
...
}
When a search and the SolrRepository is called (e.g. BooksService.findBookText(query, pageable);) I get back these objects.
However, in my REST response I only see the "content". I would like to be able to add the "highlighted" object to the REST response. It just appears that HATEOAS is only sending the information in the "content" object (see below for the object).
{
"_embedded" : {
"solrBooks" : [ {
"filename" : "ABookName",
"fullText" : "ABook Text"
} ]
},
"_links" : {
"first" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
},
"self" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook"
},
"next" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
},
"last" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
}
},
"page" : {
"size" : 1,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
Just so you can get a full picture, this is the repository that is backing the BooksService. All the service does is call this SolrCrudRepository method.
public interface SolrBooksRepository extends SolrCrudRepository<Books, String> {
#Highlight(prefix = "<highlight>", postfix = "</highlight>", fragsize = 20, snipplets = 3)
HighlightPage<SolrTestDocuments> findBookText(#Param("fullText") String fullText, Pageable pageable);
}
Ok, here is how I did it:
I wrote mine HighlightPagedResources
public class HighlightPagedResources<R,T> extends PagedResources<R> {
private List<HighlightEntry<T>> phrases;
public HighlightPagedResources(Collection<R> content, PageMetadata metadata, List<HighlightEntry<T>> highlightPhrases, Link... links) {
super(content, metadata, links);
this.phrases = highlightPhrases;
}
#JsonProperty("highlighting")
public List<HighlightEntry<T>> getHighlightedPhrases() {
return phrases;
}
}
and HighlightPagedResourcesAssembler:
public class HighlightPagedResourcesAssembler<T> extends PagedResourcesAssembler<T> {
public HighlightPagedResourcesAssembler(HateoasPageableHandlerMethodArgumentResolver resolver, UriComponents baseUri) {
super(resolver, baseUri);
}
public <R extends ResourceSupport> HighlightPagedResources<R,T> toResource(HighlightPage<T> page, ResourceAssembler<T, R> assembler) {
final PagedResources<R> rs = super.toResource(page, assembler);
final Link[] links = new Link[rs.getLinks().size()];
return new HighlightPagedResources<R, T>(rs.getContent(), rs.getMetadata(), page.getHighlighted(), rs.getLinks().toArray(links));
}
}
I had to add to my spring RepositoryRestMvcConfiguration.java:
#Primary
#Bean
public HighlightPagedResourcesAssembler solrPagedResourcesAssembler() {
return new HighlightPagedResourcesAssembler<Object>(pageableResolver(), null);
}
In cotroller I had to change PagedResourcesAssembler for newly implemented one and also use new HighlightPagedResources in request method:
#Autowired
private HighlightPagedResourcesAssembler<Object> highlightPagedResourcesAssembler;
#RequestMapping(value = "/conversations/search", method = POST)
public HighlightPagedResources<PersistentEntityResource, Object> findAll(
#RequestBody ConversationSearch search,
#SortDefault(sort = FIELD_LATEST_SEGMENT_START_DATE_TIME, direction = DESC) Pageable pageable,
PersistentEntityResourceAssembler assembler) {
HighlightPage page = conversationRepository.findByConversationSearch(search, pageable);
return highlightPagedResourcesAssembler.toResource(page, assembler);
}
RESULT:
{
"_embedded": {
"conversations": [
..our stuff..
]
},
"_links": {
...as you know them...
},
"page": {
"size": 1,
"totalElements": 25,
"totalPages": 25,
"number": 0
},
"highlighting": [
{
"entity": {
"conversationId": "a2127d01-747e-4312-b230-01c63dacac5a",
...
},
"highlights": [
{
"field": {
"name": "textBody"
},
"snipplets": [
"Additional XXX License for YYY Servers DCL-2016-PO0422 \n  \n<em>hi</em> bodgan \n  \nwe urgently need the",
"Additional XXX License for YYY Servers DCL-2016-PO0422\n \n<em>hi</em> bodgan\n \nwe urgently need the permanent"
]
}
]
}
]
}
I was using Page<Books> instead of HighlightPage to create the response page. Page obviously doesn't contain content which was causing the highlighted portion to be truncated. I ended up creating a new page based off of HighlightPage and returning that as my result instead of Page.
#RepositoryRestController
#RequestMapping(value = "/booksCustom")
public class BooksController extends ResourceSupport {
#Autowired
public BooksService booksService;
#Autowired
private PagedResourcesAssembler<Books> booksAssembler;
#RequestMapping("/search")
public HttpEntity<PagedResources<Resource<HighlightPage>>> search(#RequestParam(value = "q", required = false) String query, #PageableDefault(page = 0, size = 20) Pageable pageable) {
HighlightPage solrBookResult = booksService.findBookText(query, pageable);
Page<Books> highlightedPages = new PageImpl(solrBookResult.getHighlighted(), pageable, solrBookResult.getTotalElements());
return new ResponseEntity<PagedResources<Resource<HighlightPage>>>(booksAssembler.toResource(highlightedPages), HttpStatus.OK);
}
Probably a better way of doing this, but I couldn't find anything that would do what I wanted it to do without having a change a ton of code. Hope this helps!

Resources