In my web service application, the following model serves as contract for UI for statistical information where for a given date, I need to provide a summary of how many accepted
, rejected
etc:
@Data
public class StatusResponse {
private LocalDate processedDate;
private int accepted;
private int rejected;
private int failed;
private int other;
}
My database query returns results of the summaries as follows:
date status groupStatus count
2020-01-01 ACC Accepted 3
2020-01-22 RJCT Rejected 47
2020-01-22 NM Other 1
2020-01-23 NIY Failed 55
I have an enum Status
:
public enum Status{
ACCEPTED("Accepted"),
REJECTED("Rejected"),
OTHER("Other"),
FAILED("Failed");
private final String text;
Status (final String text) {
this.text = text;
}
}
In my repository class, I query and retrieve the status summaries into Guava MultiMap
object where the keys are date, and value is an EnumMap
of status and it's count:
public Multimap<LocalDate, EnumMap<Status, Integer>> statusSummary(final Request search) {
return this.namedParameterJdbcTemplate.query(this.QUERY,
new MapSqlParameterSource()
.addValue("processedDate", search.getProcessedDate()),
rs -> {
final Multimap<LocalDate, EnumMap<Status, Integer>> statusMap = ArrayListMultimap.create();
while (rs.next()) {
final EnumMap<Status, Integer> groupedStatusCount = new EnumMap<>(Status.class);
groupedStatusCount.put(fromValue(rs.getString("groupStatus")), rs.getInt("count"));
statusMap.put(rs.getDate("date").toLocalDate(), groupedStatusCount);
}
return statusMap;
}
);
}
So a key (the processedDate) can have the same status as value. For example, the map can contain:
key --> 2020-01-23
val1 --> OTHER 23
val2 --> ACCEPTED 2
val3 --> OTHER 4
My question and what I am struggling to complete:
How can I map the results of statusMap
to StatusResponse
and provide the summaries for each date. So StatusResponse
for the above is something like this:
{
"processedDate": [
2020,
1,
23
],
"accepted": 2,
"rejected": 0,
"failed": 0,
"other": 27,
"date": "2020-01-23T00:00:00"
}
I am using Java 8, and would like some help how can I achieve this with stream operation and using groupBy
in a functional style?
Additionally, I need to have further grouping where anything before today is grouped to today, and anything after today, grouped to today + 1.
Thank you
you can try the following approach. The summary can be stored in Map
and unwrapped at root level using JsonAnyGetter
annotation from Jackson. This will remove summary field from response and put inner values at root level.
@Data
@Builder
public class StatusResponse {
private LocalDate processedDate;
@JsonAnyGetter
private Map<Status, Integer> summary;
}
Now, let's convert MultiMap to the required format
List<StatusResponse> responseList = data.entries().stream()
.map(entry -> {
Map<Status, Integer> summary = entry.getValue().entries().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));
return StatusResponse.builder()
.processedDate(entry.getKey())
.summary(summary)
.build();
}).collect(Collectors.toList());
Now, we need to convert LocaleDate
to the proper format. we can take use jackson-datatype-jsr310
. To convert to format like 'yyyy-mm-dd', we can just register JavaTimeModule
to ObjectMapper.
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
The output would be something like this.
[{
"processedDate":"2020-01-23",
"FAILED":3,
"ACCEPTED":2
}]
Note: if database returns all status, it will include in response even if there values are zero. So, to have all status field in response, just make all of them are part of multimap.
You can further explore custom serializers to fine-tune date format. here