# TS.RANGE

```json metadata
{
  "schema_version": 2,
  "title": "TS.RANGE",
  "description": "Query a range in forward direction",
  "categories": ["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],
  "arguments": [{"name":"key","type":"key"},{"name":"fromTimestamp","type":"string"},{"name":"toTimestamp","type":"string"},{"name":"LATEST","optional":true,"since":"1.8.0","type":"string"},{"multiple":true,"name":"Timestamp","optional":true,"token":"FILTER_BY_TS","type":"integer"},{"arguments":[{"name":"FILTER_BY_VALUE","token":"FILTER_BY_VALUE","type":"pure-token"},{"name":"min","type":"double"},{"name":"max","type":"double"}],"name":"fbv","optional":true,"type":"block"},{"name":"count","optional":true,"token":"COUNT","type":"integer"},{"arguments":[{"name":"value","optional":true,"token":"ALIGN","type":"integer"},{"name":"aggregators","token":"AGGREGATION","type":"string"},{"name":"bucketDuration","type":"integer"},{"name":"buckettimestamp","optional":true,"since":"1.8.0","token":"BUCKETTIMESTAMP","type":"pure-token"},{"name":"empty","optional":true,"since":"1.8.0","token":"EMPTY","type":"pure-token"}],"name":"aggregation","optional":true,"type":"block"}],
  "syntax_fmt": "TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER_BY_TS Timestamp [Timestamp ...]] [FILTER_BY_VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION aggregators bucketDuration [BUCKETTIMESTAMP] [EMPTY]]",
  "complexity": "O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range",
  "group": "timeseries",
  "acl_categories": ["@timeseries","@read","@slow"],
  "since": "1.0.0",
  "tableOfContents": {"sections":[{"id":"required-arguments","title":"Required arguments"},{"id":"optional-arguments","title":"Optional arguments"},{"id":"complexity","title":"Complexity"},{"id":"examples","title":"Examples"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility"},{"id":"return-information","title":"Return information"},{"id":"see-also","title":"See also"},{"id":"related-topics","title":"Related topics"}]}

,
  "codeExamples": []
}
```


Query a range in forward direction. Starting from Redis 8.6, NaN values are included in raw measurement reports (queries without aggregation).

[Examples](#examples)

## Required arguments

<details open>
<summary><code>key</code></summary> 

is the key name for the time series.
</details>

<details open>
<summary><code>fromTimestamp</code></summary> 

is start timestamp for the range query (integer Unix timestamp in milliseconds) or `-` to denote the timestamp of the earliest sample in the time series.
</details>

<details open>
<summary><code>toTimestamp</code></summary> 

is end timestamp for the range query (integer Unix timestamp in milliseconds) or `+` to denote the timestamp of the latest sample in the time series.

<note><b>Note:</b>    When the time series is a compaction, the last compacted value may aggregate raw values with timestamp beyond `toTimestamp`. That is because `toTimestamp` only limits the timestamp of the compacted value, which is the start time of the raw bucket that was compacted.</note>
</details>

## Optional arguments

<details open>
<summary><code>LATEST</code> (since RedisTimeSeries 1.8)</summary>

is used when a time series is a compaction. With `LATEST`, TS.RANGE also reports the compacted value of the latest, possibly partial, bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.RANGE does not report the latest, possibly partial, bucket. When a time series is not a compaction, `LATEST` is ignored.
  
The data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest, possibly partial, bucket is also required. In such a case, use `LATEST`.
</details>

<details open>
<summary><code>FILTER_BY_TS ts...</code> (since RedisTimeSeries 1.6)</summary> 

filters samples by a list of specific timestamps. A sample passes the filter if its exact timestamp is specified and falls within `[fromTimestamp, toTimestamp]`.

When used together with `AGGREGATION`: samples are filtered before being aggregated.
</details>

<details open>
<summary><code>FILTER_BY_VALUE min max</code> (since RedisTimeSeries 1.6)</summary> 

filters samples by minimum and maximum values. `min` and `max` cannot be NaN values.

When used together with `AGGREGATION`: samples are filtered before being aggregated.
</details>

<details open>
<summary><code>COUNT count</code></summary> 

When used without `AGGREGATION`: limits the number of reported samples.

When used together with `AGGREGATION`: limits the number of reported buckets.
</details>

<details open>
<summary><code>ALIGN align</code> (since RedisTimeSeries 1.6)</summary> 

is a time bucket alignment control for `AGGREGATION`. It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined.

`align` values include:
   
 - `start` or `-`: The reference timestamp will be the query start interval time (`fromTimestamp`) which can't be `-`
 - `end` or `+`: The reference timestamp will be the query end interval time (`toTimestamp`) which can't be `+`
 - A specific timestamp: align the reference timestamp to a specific time
   
<note><b>Note:</b> When not provided, alignment is set to `0`.</note>
</details>

<details open>
<summary><code>AGGREGATION aggregators bucketDuration</code></summary> 

for each time series, aggregates samples into time buckets, where:

  - `aggregators` is one or more comma-separated aggregators from the following table:

    | aggregator   | Description                                                     |
    | ------------ | --------------------------------------------------------------- |
    | `avg`        | Arithmetic mean of all non-NaN values                           |
    | `sum`        | Sum of all non-NaN values                                       |
    | `min`        | Minimum non-NaN value                                           |
    | `max`        | Maximum non-NaN value                                           |
    | `range`      | Difference between the maximum and the minimum non-NaN values   |
    | `count`      | Number of non-NaN values                                        |
    | `countNaN`   | Number of NaN values (since Redis 8.6)                          |
    | `countAll`   | Number of values, including NaN and non-NaN (since Redis 8.6)   |
    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |
    | `last`       | The non-NaN value with the highest timestamp in the bucket      |
    | `std.p`      | Population standard deviation of the non-NaN values             |
    | `std.s`      | Sample standard deviation of the non-NaN values                 |
    | `var.p`      | Population variance of the non-NaN values                       |
    | `var.s`      | Sample variance of the non-NaN values                           |
    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8) |

  - `bucketDuration` is duration of each bucket, in milliseconds.

  Note: `aggregators` can either be a single aggregator or multiple aggregators separated by commas as shown below.
  No whitespace is allowed between aggregators.

  AGGREGATION min,avg,max
  
  Without `ALIGN`, bucket start times are multiples of `bucketDuration`.
  
  With `ALIGN align`, bucket start times are multiples of `bucketDuration` with remainder `align % bucketDuration`.
  
  The first bucket start time is less than or equal to `fromTimestamp`.
</details>

<details open>
<summary><code>[BUCKETTIMESTAMP bt]</code> (since RedisTimeSeries 1.8)</summary> 

controls how bucket timestamps are reported.

| `bt`             | Timestamp reported for each bucket                            |
| ---------------- | ------------------------------------------------------------- |
| `-` or `start`   | the bucket's start time (default)                             |
| `+` or `end`     | the bucket's end time                                         |
| `~` or `mid`     | the bucket's mid time (rounded down if not an integer)        |
</details>

<details open>
<summary><code>[EMPTY]</code> (since RedisTimeSeries 1.8)</summary> 

is a flag, which, when specified, reports aggregations also for empty buckets.

| aggregator           | Value reported for each empty bucket |
| -------------------- | ------------------------------------ |
| `sum`, `count`       | `0`                                  |
| `last`               | The value of the last sample before the bucket's start. `NaN` when no such sample. |
| `twa`                | Average value over the bucket's timeframe based on linear interpolation of the last sample before the bucket's start and the first sample after the bucket's end. `NaN` when no such samples. |
| `min`, `max`, `range`, `avg`, `first`, `std.p`, `std.s` | `NaN` |

Regardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for buckets that end before the earliest sample or begin after the latest sample in the time series.
</details>

## Complexity

TS.RANGE complexity can be improved in the future by using binary search to find the start of the range, which makes this `O(Log(n/m)+k*m)`.
But, because `m` is small, you can disregard it and look at the operation as `O(Log(n)+k)`.

## Examples

<details open><summary><b>Filter results by timestamp or sample value</b></summary>

Consider a metric where acceptable values are between -100 and 100, and the value 9999 is used as an indication of bad measurement.


127.0.0.1:6379> TS.CREATE temp:TLV LABELS type temp location TLV
OK
127.0.0.1:6379> TS.MADD temp:TLV 1000 30 temp:TLV 1010 35 temp:TLV 1020 9999 temp:TLV 1030 40
1) (integer) 1000
2) (integer) 1010
3) (integer) 1020
4) (integer) 1030


Now, retrieve all values except out-of-range values.


TS.RANGE temp:TLV - + FILTER_BY_VALUE -100 100
1) 1) (integer) 1000
   2) 30
2) 1) (integer) 1010
   2) 35
3) 1) (integer) 1030
   2) 40


Now, retrieve the minimum, average, and maximum values, while ignoring out-of-range values.


TS.RANGE temp:TLV - + FILTER_BY_VALUE -100 100 AGGREGATION min,avg,max 1000
1) 1) (integer) 1000
   2) 30
   3) 35
   4) 40

</details>

<details open><summary><b>Align aggregation buckets</b></summary>

To demonstrate alignment, let’s create a stock and add prices at nine different timestamps.


127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A
OK
127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120
1) (integer) 1000
2) (integer) 1010
3) (integer) 1020
127.0.0.1:6379> TS.MADD stock:A 2000 200 stock:A 2010 210 stock:A 2020 220
1) (integer) 2000
2) (integer) 2010
3) (integer) 2020
127.0.0.1:6379> TS.MADD stock:A 3000 300 stock:A 3010 310 stock:A 3020 320
1) (integer) 3000
2) (integer) 3010
3) (integer) 3020


Next, aggregate without using `ALIGN`, defaulting to alignment 0.


127.0.0.1:6379> TS.RANGE stock:A - + AGGREGATION min 20
1) 1) (integer) 1000
   2) 100
2) 1) (integer) 1020
   2) 120
3) 1) (integer) 2000
   2) 200
4) 1) (integer) 2020
   2) 220
5) 1) (integer) 3000
   2) 300
6) 1) (integer) 3020
   2) 320


And now set `ALIGN` to 10 to have a bucket start at time 10, and align all the buckets with a 20 milliseconds duration.


127.0.0.1:6379> TS.RANGE stock:A - + ALIGN 10 AGGREGATION min 20
1) 1) (integer) 990
   2) 100
2) 1) (integer) 1010
   2) 110
3) 1) (integer) 1990
   2) 200
4) 1) (integer) 2010
   2) 210
5) 1) (integer) 2990
   2) 300
6) 1) (integer) 3010
   2) 310


When the start timestamp for the range query is explicitly stated (not `-`), you can set `ALIGN` to that time by setting align to `-` or to `start`.


127.0.0.1:6379> TS.RANGE stock:A 5 + ALIGN - AGGREGATION min 20
1) 1) (integer) 985
   2) 100
2) 1) (integer) 1005
   2) 110
3) 1) (integer) 1985
   2) 200
4) 1) (integer) 2005
   2) 210
5) 1) (integer) 2985
   2) 300
6) 1) (integer) 3005
   2) 310


Similarly, when the end timestamp for the range query is explicitly stated, you can set `ALIGN` to that time by setting align to `+` or to `end`.
</details>

## Redis Software and Redis Cloud compatibility

| Redis<br />Software | Redis<br />Cloud | <span style="min-width: 9em; display: table-cell">Notes</span> |
|:----------------------|:-----------------|:------|
| <span title="Supported">&#x2705; Supported</span><br /> | <span title="Supported">&#x2705; Flexible & Annual</span><br /><span title="Supported">&#x2705; Free & Fixed</nobr></span> |  |

## Return information

**RESP2:**

One of the following:
* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of 
  * Without `AGGREGATION` or with a single aggregator:
    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) pairs representing (timestamp, value).
  * With multiple aggregators:
    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) tuples representing (timestamp, value...).
* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter value, wrong key type, key does not exist, etc.

**RESP3:**

One of the following:
* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of
  * Without `AGGREGATION` or with a single aggregator:
    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) pairs representing (timestamp, value).
  * With multiple aggregators:
    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) tuples representing (timestamp, value...).    
* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter value, wrong key type, key does not exist, etc.



## See also

[`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange)

## Related topics

[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)

