1

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

M06H
  • 1,675
  • 3
  • 36
  • 76

1 Answers1

1

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

lucid
  • 2,722
  • 1
  • 12
  • 24
  • per message contract, I cannot have `summary` tag in the response and it needs to include the individual values for `accepted`, `rejected` etc even if they are no elements returned by db. Can you update your answer to include all the field? – M06H Jan 23 '20 at 17:50
  • @M06H if you make database returns all status even though values are zero, it will be included. – lucid Jan 24 '20 at 01:11