# Class AggregateQuery

java.lang.Object
com.codename1.health.AggregateQuery

public final class AggregateQuery extends Object

Describes a bucketed summary read against `HealthStore`.

    AggregateQuery q = new AggregateQuery()
            .addType(HealthDataType.STEPS)
            .addMetric(AggregateMetric.TOTAL)
            .setTimeRange(HealthTimeRange.calendarDays(7, ZoneId.systemDefault()))
            .setBucket(HealthInterval.calendarDays(1, ZoneId.systemDefault()));

##### Overlapping sources are counted twice, on every platform

When a phone and a watch both record steps for the same walk, the store holds two overlapping sets of samples, and a total over them counts the walk twice.

This includes iOS. HealthKit's statistics engine does de-duplicate overlapping sources -- but no port uses it in this release. Every metric here is computed by shared code from raw samples read back through an ordinary query, so that the bucket arithmetic has one implementation rather than one per platform that can drift. iOS therefore double-counts exactly as Android does. A port that grows a native aggregate path would change that, and this note with it.

This API does not paper over the overlap with a heuristic de-duplicator -- guessing which of two overlapping sources is authoritative is exactly the kind of silent wrongness health data cannot afford. Use `addSource(String)` to pin the query to the source you trust, and tell the user which device a figure came from.

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `AggregateQuery()`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `AggregateQuery`
  `addMetric(AggregateMetric metric)`
  Adds a metric to compute.
  `AggregateQuery`
  `addSource(String bundleId)`
  `AggregateQuery`
  `addType(HealthDataType type)`
  Adds a type to summarize.
  `HealthInterval`
  `getBucket()`
  The bucket width, or null for one bucket over the whole range.
  `List<AggregateMetric>`
  `getMetrics()`
  The metrics this query computes.
  `List<String>`
  `getSources()`
  The source filter, empty when unrestricted.
  `HealthTimeRange`
  `getTimeRange()`
  The span this query summarizes.
  `List<HealthDataType>`
  `getTypes()`
  The types this query summarizes.
  `HealthUnit`
  `getUnit()`
  The requested unit, or null for canonical units.
  `boolean`
  `isDeduplicateSources()`
  Whether this query asked for source de-duplication.
  `static boolean`
  `isMeaningful(HealthDataType type, AggregateMetric metric)`
  Whether `metric` says anything true about `type`.
  `AggregateQuery`
  `setBucket(HealthInterval bucket)`
  Splits the range into buckets of this width.
  `AggregateQuery`
  `setDeduplicateSources(boolean deduplicate)`
  Restricts the summary to one writing app -- see the double-counting warning on this class.
  `AggregateQuery`
  `setTimeRange(HealthTimeRange timeRange)`
  The span to summarize.
  `AggregateQuery`
  `setUnit(HealthUnit unit)`
  Returns aggregated values in `unit` rather than each type's canonical unit.
  `void`
  `validate()`
  Validates the query and throws if it cannot be run.

  ### Methods inherited from class Object

  `clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### AggregateQuery

    public AggregateQuery()

- ## Method Details

  - ### addType

    public AggregateQuery addType(HealthDataType type)
    Adds a type to summarize. At least one is required.

  - ### getTypes

    public List<HealthDataType> getTypes()
    The types this query summarizes.

  - ### addMetric

    public AggregateQuery addMetric(AggregateMetric metric)
    Adds a metric to compute. At least one is required.

  - ### getMetrics

    public List<AggregateMetric> getMetrics()
    The metrics this query computes.

  - ### setDeduplicateSources

    public AggregateQuery setDeduplicateSources(boolean deduplicate)

    Restricts the summary to one writing app -- see the double-counting warning on this class. Asks the platform to de-duplicate overlapping sources.

    Off by default, because the shared rollup cannot do it and most stores therefore cannot honour it. Check `HealthStore.isSourceDeduplicationSupported()` first: asking a store that cannot fails the aggregate with `HealthError.NOT_SUPPORTED` rather than silently returning the double-counted answer.

    Where it is honoured -- HealthKit, whose statistics engine does this natively -- a walk recorded by both a phone and a watch is counted once instead of twice.

  - ### isDeduplicateSources

    public boolean isDeduplicateSources()
    Whether this query asked for source de-duplication.

  - ### addSource

    public AggregateQuery addSource(String bundleId)

  - ### getSources

    public List<String> getSources()
    The source filter, empty when unrestricted.

  - ### setTimeRange

    public AggregateQuery setTimeRange(HealthTimeRange timeRange)
    The span to summarize. Required.

  - ### getTimeRange

    public HealthTimeRange getTimeRange()
    The span this query summarizes.

  - ### setBucket

    public AggregateQuery setBucket(HealthInterval bucket)

    Splits the range into buckets of this width. Leave unset for a single bucket covering the whole range.

    Prefer `HealthInterval.calendarDays(int,java.time.ZoneId)` over a fixed 24-hour width whenever the buckets are labelled with dates in your UI -- see `HealthInterval`.

  - ### getBucket

    public HealthInterval getBucket()
    The bucket width, or null for one bucket over the whole range.

  - ### setUnit

    public AggregateQuery setUnit(HealthUnit unit)
    Returns aggregated values in `unit` rather than each type's canonical unit.

  - ### getUnit

    public HealthUnit getUnit()
    The requested unit, or null for canonical units.

  - ### validate

    public void validate() throws HealthException

    Validates the query and throws if it cannot be run.

    ###### Throws

    - `HealthException`: `HealthError.INVALID_ARGUMENT` for a missing type, metric or range, or for a metric that is meaningless for the requested type; `HealthError.UNIT_MISMATCH` for an incompatible unit.
    Throws:
      `HealthException`

  - ### isMeaningful

    public static boolean isMeaningful(HealthDataType type, AggregateMetric metric)

    Whether `metric` says anything true about `type`.

    `AggregateMetric.COUNT` and `AggregateMetric.DURATION` apply to everything. Summing a discrete series -- the total of every body mass ever recorded -- and averaging a cumulative one -- the mean of arbitrarily-chunked step totals -- are both meaningless, so they are rejected rather than answered.