/** * Adds sum, sumOfSquares, mean, stddev, and percentiles to the given NamedList * * @param res NamedList to add the type specific statistics too */ @Override protected void addTypeSpecificStats(NamedList<Object> res) { if (statsField.includeInResponse(Stat.sum)) { res.add("sum", sum); } if (statsField.includeInResponse(Stat.sumOfSquares)) { res.add("sumOfSquares", sumOfSquares); } if (statsField.includeInResponse(Stat.mean)) { res.add("mean", sum / count); } if (statsField.includeInResponse(Stat.stddev)) { res.add("stddev", getStandardDeviation()); } if (statsField.includeInResponse(Stat.percentiles)) { if (statsField.getIsShard()) { // as of current t-digest version, smallByteSize() internally does a full conversion in // order to determine what the size is (can't be precomputed?) .. so rather then // serialize to a ByteBuffer twice, allocate the max possible size buffer, // serialize once, and then copy only the byte[] subset that we need, and free up the buffer ByteBuffer buf = ByteBuffer.allocate(tdigest.byteSize()); // upper bound tdigest.asSmallBytes(buf); res.add("percentiles", Arrays.copyOf(buf.array(), buf.position())); } else { NamedList<Object> percentileNameList = new NamedList<Object>(); for (Double percentile : statsField.getPercentilesList()) { // Empty document set case if (tdigest.size() == 0) { percentileNameList.add(percentile.toString(), null); } else { Double cutoff = tdigest.quantile(percentile / 100); percentileNameList.add(percentile.toString(), cutoff); } } res.add("percentiles", percentileNameList); } } }