Java 8 groupBy and reduce not summing up values in the column - java-8

I am trying to use Java8 groupby and reduce feature to sum elements based on grouping but the code doesnt seem to be working for me.
Here is my data from the Dtolist that I am trying to map and extract in an excel sheet.
My requirement is that for every DTO object I want this kilometersNumber to be summed up based on groupingBy TruckId. So , the highlighted columns should be having summed up values for same TruckId(**Contract flotte)
This is my DTO:
'''public class CostsTruckWsDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String truckId;
private LocalDate period;
private Bigdecimal KilometersNumber;
private String periodStartDate;
Here is the logic::
''costsTruckWsDTO.setGroupBy(TruckId.toUpperCase());
Map<String, List> mapCostsTruck = costsTruck.stream()
.collect(Collectors.groupingBy(d -> d.getGroupBy().toUpperCase()));
for (Map.Entry<String, List> entry : mapCostsTruck.entrySet()) {
BigDecimal totalKm = entry.getValue().stream().map(x -> x.getKilometersNumber()).reduce(BigDecimal.ZERO,
BigDecimal::add);
}
}''
But what am getting is value for kilometere is not geeting added as per the screenshot.
Can anybody pls help me what I am missing here!!!
Highly appreciate

You can do the operation of grouping together with the operation of summing using two collectors:
Map<String, BigDecimal> result = trucks.stream()
.collect(Collectors.groupingBy(CostsTruckWsDTO::getTruckId,
Collectors.mapping(CostsTruckWsDTO::getKilometersNumber, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));

Related

How to search Mongo for element in a list of string using spring query

So, here's my mongo entity :
public class OfferEntity {
#Id
private String id;
public List<String> categories;
private Type type;
private String title;
}
I use spring org.springframework.data.mongodb.core.query.Criteria and org.springframework.data.mongodb.core.query.Query.
The goal is to find Offers by categories.
Something like :
Query query = new Query();
ArrayList<String> strings = new ArrayList<>();
strings.add("new");
strings.add("old");
strings.add("blue");
strings.add("stolen");
query.addCriteria(Criteria.where("categories").contains(strings);
or
query.addCriteria(Criteria.where("categories").contains("new","old","blue");
I know there's no such thing as "contains" method here, but you get the idea.
I've found some things about "elemMatch" but it does not seems to match my need, as it's meant to find a element in an array with a matching key/value pair.
Any thoughts ? I'm sure it's possible but I'm a bit lost here.
You need to use Criteria.in
Reference
query.addCriteria(Criteria.where("categories").in(Arrays.asList("new","old","blue"));

Spring Data MongoDB - how to set maxTimeMS for Aggregation pipeline query

I've noticed that org.springframework.data.mongodb.core.aggregation.AggregationOption class covers just a small subset of these options described within MongoDB Aggregation pipeline documentation: https://docs.mongodb.com/manual/reference/command/aggregate/#syntax
I need to set maxTimeMS option, but it is not available within org.springframework.data.mongodb.core.aggregation.AggregationOption:
public class AggregationOptions {
private static final String BATCH_SIZE = "batchSize";
private static final String CURSOR = "cursor";
private static final String EXPLAIN = "explain";
private static final String ALLOW_DISK_USE = "allowDiskUse";
private static final String COLLATION = "collation";
private static final String COMMENT = "comment";
...
However, the another class (of mongodb-driver) actually has such field maxTimeMS,
com.mongodb.AggregationOptions:
public class AggregationOptions {
private final Integer batchSize;
private final Boolean allowDiskUse;
private final OutputMode outputMode;
private final long maxTimeMS;
private final Boolean bypassDocumentValidation;
private final Collation collation;
...
Any idea/tricks how to set this maxTimeMS for Aggregation query using Spring Data MongoDB API? Or maybe do I need to build/write such aggregation by using native query?
Btw. I know that Spring Data MongoDB supports maxTimeMS for find operations, for example:
Query.query(mongoCriteria).with(pageable).maxTime(Duration.ofMinutes(4))
However I need to set aggregation query processing timeout on server side in order to prevent "never ending" queries that kill performance.
spring-data-mongodb:2.2.0.RELEASE
I was trying to set a time limit to my query and it took me some time to find the answer; so, I want to share my method with anyone who might be interested:
AggregationOptions aggregationOptions = new AggregationOptions.Builder().maxTime(Duration.ofSeconds(3)).build();
Aggregation aggregate = Aggregation.newAggregation(match).withOptions(aggregationOptions);

Spring Data JPA Projection with select distinct

I have a database table which holds Metadata for documents. My task now is to get a list with documenttypes. The documenttypes are not unique in the database table but of course I want them to be in my list. The sql is very simple:
SELECT DISTINCT groupname, group_displayorder
FROM t_doc_metadata
ORDER BY group_displayorder;
I have learned that I can use projections to get a subset of fields from my entity DocMetadata. I solved this as follows. My Entity:
#Entity
#Table(name="T_DOC_METADATA")
#Data
public class DocMetadata {
..............
#Column(nullable=false)
private String displayname;
#Column(nullable=false)
private Integer displayorder;
#Column(nullable=false)
private String groupname;
#Column(name="GROUP_DISPLAYORDER",
nullable=false)
private Integer groupDisplayorder;
#Column(name="METADATA_CHANGED_TS",
nullable=false,
columnDefinition="char")
private String metadataChangedTimestamp;
..........
}
My inteface for projection:
public interface GroupnameAndOrder {
String getGroupname();
Integer getGroupDisplayorder();
void setGroupname(String name);
void setGroupDisplayorder(int order);
}
Now I thought I'd be extraordinary clever by adding these lines to my repository:
#Query("select distinct d.groupname, d.groupDisplayorder from DocMetadata d order by d.groupDisplayorder")
public List<GroupnameAndOrder> findSortedGroupnames();
Sadly, when iterating over the result list and calling getGroupname() the result is null.
So I changed the lines in my repository according to the documentation:
public List<GroupnameAndOrder> findBy();
Now I get the groupnames but of course they are not unique now. So it doesn't solve my problem.
Is there any way to receive a ordered list with unique groupnames?
You are trying to be too clever. Instead just write the proper find method and return the GroupnameAndOrder. Spring Data JPA will then only retrieve what is needed for the projection.
Something like this should do the trick.
List<GroupnameAndOrder> findDistinctByOrderByGroupDisplayorder();

Spring entity dynamically calculated field with parameter from native query

I have a somewhat complex entity like following (Notice the super-class with many more fields):
public class Question extends Entry {
#OneToMany(orphanRemoval = true, mappedBy = "question")
#JsonManagedReference
private List<Answer> answers = new ArrayList<>();
private Long viewCount = 0L;
private Category category;
#OneToMany(mappedBy = "question", fetch = FetchType.LAZY,
cascade = CascadeType.ALL, orphanRemoval = true)
private List<QuestionTranslation> translations = new ArrayList<>();
#Transient
private double distance;
}
distance should be calculated from the DB when retrieving the result set from a native query.
E.g.
SELECT q.*, ST_Distance_Sphere(cast(q.location as geometry), ST_MakePoint(cast(?1 as double precision), cast(?2 as double precision))) as distance from question q
I cannot use #Formula to annotate my field distance since the query has to take parameters.
How can I map the field distance from the SQL query result to my entity field distance while leaving all the other mappings to be done by Hibernate?
Edit
Based on #gmotux suggestion I created a wrapper entity.
#Entity
#SqlResultSetMapping(
name="MappingQ",
entities={
#EntityResult(
entityClass = QuestionWithDistance.class,
fields={
#FieldResult(name="distance",column="distance"),
#FieldResult(name="question",column="question")})})
public class QuestionWithDistance{
#Id
#GeneratedValue
private String id;
#OneToOne
private Question question;
private double distance;
}
Query
Query query = entityManager.createNativeQuery("SELECT q.*, 222.22 as distance from question q", "MappingQ");
But it always fails with
org.postgresql.util.PSQLException: The column name id1_15_0_ was not found in this ResultSet.
Since you need extra parameters to calculate your field, you indeed cannot use #Formula, or even a getter to calculate the field.
Unfortunately for your case the only thing that comes to mind, assuming you are using an EntityManager based configuration for Hibernate, is leveraging its #PostLoad event listener, which you can use for calculating field values upon entity loading, like :
public class Question extends Entry {
#PostLoad
private void postLoad() {
this.distance = DistanceCalculator.calculateDistance(Double param1,Double param2);
//other calculations
}
}
That of-course is only a workaround and it means that you must have a static method somewhere execute native queries.
I would suggest detaching the "distance" notion from your Question entity, if possible in your requirements and calculate it, when required, with either a native SQL function call or a service method.

Sorting on #Transient column in Spring Data Rest via PagingAndSortingRepository

Our application uses PagingAndSortingRepository to serve our REST API. This works great, but we ran into a specific edge case that we can't seem to solve:
We have a alphanumeric field that has to be sortable (e.g. SOMETHING-123). One possible solution was to use something like a regex inside the database query's order by. This was ruled out, as we wanted to stay database independant. Thus we split up the column into two columns.
So before we had an Entity with 1 String field:
#Entity
public class TestingEntity {
#Id
#GeneratedValue
private long id;
private String alphanumeric
}
And now we have an Entity with 2 additional fields and the old field made #Transient which is filled at #PostLoad:
#Entity
public class Testing {
#Id
#GeneratedValue
private long id;
#Transient
public String alphanumeric;
#PostLoad
public void postLoad(){
this.alphanumeric = this.alphabetic + "-" + this.numeric;
}
public void setAlphanumeric(String alphanumeric) {
int index = alphanumeric.indexOf("-");
this.alphabetic = alphanumeric.substring(0, index);
this.numeric = Long.parseLong(alphanumeric.substring(index + 1));
}
#JsonIgnore
private String alphabetic;
#JsonIgnore
private Long numeric;
}
This is working great and the additional fields do not get exposed. However the sorting on the field "alphanumeric" does obviously not work anymore. The simplest solution would be to make this request:
localhost:8080/api/testingEntity?sort=alphanumeric,asc
and internally rewrite it to the working request:
localhost:8080/api/testingEntity?sort=alphabetic,asc&sort=numeric,asc
What is the best way to tackle this issue?

Resources