title: Group by in Java 8 on multiple fields with aggregations
body: <p>I have a list of domain objects that relate to web access records. These domain objects can stretch into the thousands in number.</p>
  
  <p>I don't have the resources or requirement to store them in a database in raw format, so instead I want to precompute aggregations and put the aggregated data in a database.</p>
  
  <p>I need to aggregate the total bytes transferred in 5 minute windows, <em>like</em> the following SQL query</p>
  
  <pre><code>select 
    round(request_timestamp, '5') as window, --round timestamp to the nearest 5 minute
    cdn, 
    isp, 
    http_result_code, 
    transaction_time, 
    sum(bytes_transferred)
  from web_records
  group by 
      round(request_timestamp, '5'), 
      cdn, 
      isp, 
      http_result_code, 
      transaction_time
  </code></pre>
  
  <p>In Java 8 my first current stab looks like this, <strong>I am aware this solution is similar to this response</strong> in <a href="https://stackoverflow.com/questions/28342814/group-by-multiple-field-names-in-java-8">Group by multiple field names in java 8</a></p>
  
  <pre><code>Map&lt;Date, Map&lt;String, Map&lt;String, Map&lt;String, Map&lt;String, Integer&gt;&gt;&gt;&gt;&gt;&gt;&gt; aggregatedData =
  webRecords
      .stream()
      .collect(Collectors.groupingBy(WebRecord::getFiveMinuteWindow,
                 Collectors.groupingBy(WebRecord::getCdn,
                   Collectors.groupingBy(WebRecord::getIsp,
                     Collectors.groupingBy(WebRecord::getResultCode,
                         Collectors.groupingBy(WebRecord::getTxnTime,
                           Collectors.reducing(0,
                                               WebRecord::getReqBytes(),
                                               Integer::sum)))))));
  </code></pre>
  
  <p>This works, but it's ugly, all those nested maps are a nightmare! To "flatten" or "unroll" the map out into rows I have to do this</p>
  
  <pre><code>for (Date window : aggregatedData.keySet()) {
    for (String cdn : aggregatedData.get(window).keySet()) {
      for (String isp : aggregatedData.get(window).get(cdn).keySet()) {
        for (String resultCode : aggregatedData.get(window).get(cdn).get(isp).keySet()) {
          for (String txnTime : aggregatedData.get(window).get(cdn).get(isp).get(resultCode).keySet()) {
  
             Integer bytesTransferred = aggregatedData.get(window).get(cdn).get(distId).get(isp).get(resultCode).get(txnTime);
             AggregatedRow row = new AggregatedRow(window, cdn, distId...
  </code></pre>
  
  <p>As you can see this is pretty messy and difficult to maintain.</p>
  
  <p>Anyone have any ideas of a better way to do this? Any help would be greatly appreciated. </p>
  
  <p>I'm wondering if there is a nicer way to unroll the nested maps, or if there is a library that allows you to do a GROUP BY on a collection.</p>