Back to Redis

TS.MRANGE

content/commands/ts.mrange.md

latest23.0 KB
Original Source

{{< note >}} This command's behavior varies in clustered Redis environments. See the [multi-key operations]({{< relref "/develop/using-commands/multi-key-operations" >}}) page for more information. {{< /note >}}

Query a range across multiple time series by filters in the forward direction. Starting from Redis 8.6, NaN values are included in raw measurement reports (queries without aggregation).

{{< note >}} This command will reply only if the current user has read access to all keys that match the filter. Otherwise, it will reply with "(error): current user doesn't have read permission to one or more keys that match the specified filter". {{< /note >}}

Examples

Required arguments

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

is the start timestamp for the range query (integer Unix timestamp in milliseconds) or - to denote the timestamp of the earliest sample among all the time series that passes FILTER filterExpr....

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

is the end timestamp for the range query (integer Unix timestamp in milliseconds) or + to denote the timestamp of the latest sample among all the time series that passes FILTER filterExpr....

</details> <details open> <summary><code>FILTER filterExpr...</code></summary>

filters time series based on their labels and label values. Each filter expression has one of the following syntaxes:

  • label!= - the time series has a label named label
  • label=value - the time series has a label named label with a value equal to value
  • label=(value1,value2,...) - the time series has a label named label with a value equal to one of the values in the list
  • label= - the time series does not have a label named label
  • label!=value - the time series does not have a label named label with a value equal to value
  • label!=(value1,value2,...) - the time series does not have a label named label with a value equal to any of the values in the list

<note><b>Notes:</b>

  • At least one filter expression with a syntax label=value or label=(value1,value2,...) is required.
  • Filter expressions are conjunctive. For example, the filter type=temperature room=study means that a time series is a temperature time series of a study room.
  • Whitespaces are unallowed in a filter expression except between quotes or double quotes in values - e.g., x="y y" or x='(y y,z z)'. </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.MRANGE 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.MRANGE 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 the 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>WITHLABELS</code></summary>

includes in the reply all label-value pairs representing metadata labels of the time series. If WITHLABELS or SELECTED_LABELS are not specified, by default, an empty list is reported as label-value pairs.

</details> <details open> <summary><code>SELECTED_LABELS label...</code> (since RedisTimeSeries 1.6)</summary>

returns a subset of the label-value pairs that represent metadata labels of the time series. Use when a large number of labels exists per series, but only the values of some of the labels are required. If WITHLABELS or SELECTED_LABELS are not specified, by default, an empty list is reported as label-value pairs.

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

When used without AGGREGATION: limits the number of reported samples per time series.

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.

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 aggregator bucketDuration</code></summary>

per time series, aggregates samples into time buckets, where:

  • aggregator takes one of the following aggregation types:

    aggregatorDescription
    avgArithmetic mean of all non-NaN values
    sumSum of all non-NaN values
    minMinimum non-NaN value
    maxMaximum non-NaN value
    rangeDifference between the maximum and the minimum non-NaN values
    countNumber of non-NaN values
    countNaNNumber of NaN values (since Redis 8.6)
    countAllNumber of values, including NaN and non-NaN (since Redis 8.6)
    firstThe non-NaN value with the lowest timestamp in the bucket
    lastThe non-NaN value with the highest timestamp in the bucket
    std.pPopulation standard deviation of the non-NaN values
    std.sSample standard deviation of the non-NaN values
    var.pPopulation variance of the non-NaN values
    var.sSample variance of the non-NaN values
    twaTime-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8)
  • bucketDuration is duration of each bucket, in milliseconds.

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.

btTimestamp reported for each bucket
- or startthe bucket's start time (default)
+ or endthe bucket's end time
~ or midthe 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.

aggregatorValue reported for each empty bucket
sum, count0
lastThe value of the last sample before the bucket's start. NaN when no such sample.
twaAverage 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.sNaN

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> <details open> <summary><code>GROUPBY label REDUCE reducer</code> (since RedisTimeSeries 1.6)</summary>

splits time series into groups, each group contains time series that share the same value for the provided label name, then aggregates results in each group.

When combined with AGGREGATION the GROUPBY/REDUCE is applied post aggregation stage.

  • label is label name. A group is created for all time series that share the same value for this label.

  • reducer is an aggregation type used to aggregate the results in each group.

    reducerDescription
    avgArithmetic mean of all non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)
    sumSum of all non-NaN values; NaN if no such values
    minMinimum non-NaN value; NaN if no such values
    maxMaximum non-NaN value; NaN if no such values
    rangeDifference between the maximum and the minimum non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)
    countNumber of non-NaN values (since RedisTimeSeries 1.8)
    countNaNNumber of NaN values (since Redis 8.6)
    countAllNumber of values, including NaN and non-NaN (since Redis 8.6)
    std.pPopulation standard deviation of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)
    std.sSample standard deviation of thel non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)
    var.pPopulation variance of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)
    var.sSample variance of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)

<note><b>Notes:</b>

  • The produced time series is named <label>=<value>
  • The produced time series contains two labels with these label array structures:
    • __reducer__, the reducer used (e.g., "count")
    • __source__, the list of time series keys used to compute the grouped series (e.g., "key1,key2,key3") </note>
</details>

<note><b>Note:</b> An MRANGE command cannot be part of a transaction when running on a Redis cluster.</note>

Redis Software and Redis Cloud compatibility

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

Return information

{{< multitabs id="ts-mrange-return-info" tab1="RESP2" tab2="RESP3" >}}

If GROUPBY label REDUCE reducer is not specified:

[Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): for each time series matching the specified filters, the following is reported:

  • [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}): The time series key name
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): label-value pairs ([Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}), [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}))
    • By default, an empty array is reported
    • If WITHLABELS is specified, all labels associated with this time series are reported
    • If SELECTED_LABELS label... is specified, the selected labels are reported (null value when no such label defined)
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): timestamp-value pairs ([Integer reply]({{< relref "/develop/reference/protocol-spec#integers" >}}), [Simple string reply]({{< relref "/develop/reference/protocol-spec#simple-strings" >}})) representing all samples/aggregations matching the range

If GROUPBY label REDUCE reducer is specified:

[Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): for each group of time series matching the specified filters, the following is reported:

  • [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}) with the format label=value where label is the GROUPBY label argument
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): label-value pairs ([Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}), [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}})):
    • By default, an empty array is reported
    • If WITHLABELS is specified, the GROUPBY label argument and value are reported
    • If SELECTED_LABELS label... is specified, the selected labels are reported (null value when no such label defined or label does not have the same value for all grouped time series)
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): either a single pair ([Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}), [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}})): the GROUPBY label argument and value, or empty array
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): a single pair ([Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}), [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}})): the string __reducer__ and the reducer argument
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): a single pair ([Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}), [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}})): the string __source__ and the time series key names separated by ","
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): timestamp-value pairs ([Integer reply]({{< relref "/develop/reference/protocol-spec#integers" >}}), [Simple string reply]({{< relref "/develop/reference/protocol-spec#simple-strings" >}})) representing all samples/aggregations matching the range

-tab-sep-

If GROUPBY label REDUCE reducer is not specified:

[Map reply]({{< relref "/develop/reference/protocol-spec#maps" >}}): for each time series matching the specified filters, the following is reported:

  • [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}): The time series key name
  • [Map reply]({{< relref "/develop/reference/protocol-spec#maps" >}}) or [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): label-value pairs
    • By default, an empty map is reported
    • If WITHLABELS is specified, all labels associated with this time series are reported as a map
    • If SELECTED_LABELS label... is specified, the selected labels are reported as a map (null value when no such label defined)
  • Additional metadata including aggregators information
  • [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}): timestamp-value pairs ([Integer reply]({{< relref "/develop/reference/protocol-spec#integers" >}}), [Double reply]({{< relref "/develop/reference/protocol-spec#doubles" >}})) representing all samples/aggregations matching the range

If GROUPBY label REDUCE reducer is specified:

Similar structure as RESP2 but with map-based organization for labels and metadata, and double values instead of string values.

{{< /multitabs >}}

Examples

<details open> <summary><b>Retrieve maximum stock price per timestamp</b></summary>

Create two stocks and add their prices at three different timestamps.

{{< highlight bash >}} 127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A OK 127.0.0.1:6379> TS.CREATE stock:B LABELS type stock name B 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:B 1000 120 stock:B 1010 110 stock:B 1020 100
  4. (integer) 1000
  5. (integer) 1010
  6. (integer) 1020 {{< / highlight >}}

You can now retrieve the maximum stock price per timestamp.

{{< highlight bash >}} 127.0.0.1:6379> TS.MRANGE - + WITHLABELS FILTER type=stock GROUPBY type REDUCE max

    1. "type=stock"
        1. "type"
        2. "stock"
        1. "reducer"
        2. "max"
        1. "source"
        2. "stock:A,stock:B"
        1. (integer) 1000
        2. 120
        1. (integer) 1010
        2. 110
        1. (integer) 1020
        2. 120 {{< / highlight >}}

The FILTER type=stock clause returns a single time series representing stock prices. The GROUPBY type REDUCE max clause splits the time series into groups with identical type values, and then, for each timestamp, aggregates all series that share the same type value using the max aggregator.

</details> <details open> <summary><b>Calculate average stock price and retrieve maximum average</b></summary>

Create two stocks and add their prices at nine different timestamps.

{{< highlight bash >}} 127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A OK 127.0.0.1:6379> TS.CREATE stock:B LABELS type stock name B 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:B 1000 120 stock:B 1010 110 stock:B 1020 100
  4. (integer) 1000
  5. (integer) 1010
  6. (integer) 1020 127.0.0.1:6379> TS.MADD stock:A 2000 200 stock:A 2010 210 stock:A 2020 220
  7. (integer) 2000
  8. (integer) 2010
  9. (integer) 2020 127.0.0.1:6379> TS.MADD stock:B 2000 220 stock:B 2010 210 stock:B 2020 200
  10. (integer) 2000
  11. (integer) 2010
  12. (integer) 2020 127.0.0.1:6379> TS.MADD stock:A 3000 300 stock:A 3010 310 stock:A 3020 320
  13. (integer) 3000
  14. (integer) 3010
  15. (integer) 3020 127.0.0.1:6379> TS.MADD stock:B 3000 320 stock:B 3010 310 stock:B 3020 300
  16. (integer) 3000
  17. (integer) 3010
  18. (integer) 3020 {{< / highlight >}}

Now, for each stock, calculate the average stock price per a 1000-millisecond timeframe, and then retrieve the stock with the maximum average for that timeframe.

{{< highlight bash >}} 127.0.0.1:6379> TS.MRANGE - + WITHLABELS AGGREGATION avg 1000 FILTER type=stock GROUPBY type REDUCE max

    1. "type=stock"
        1. "type"
        2. "stock"
        1. "reducer"
        2. "max"
        1. "source"
        2. "stock:A,stock:B"
        1. (integer) 1000
        2. 110
        1. (integer) 2000
        2. 210
        1. (integer) 3000
        2. 310 {{< / highlight >}}
</details> <details open> <summary><b>Group query results</b></summary>

Query all time series with the metric label equal to cpu, then group the time series by the value of their metric_name label value and for each group return the maximum value and the time series keys (source) with that value.

{{< highlight bash >}} 127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system (integer) 1548149180000 127.0.0.1:6379> TS.ADD ts1 1548149185000 45 (integer) 1548149185000 127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user (integer) 1548149180000 127.0.0.1:6379> TS.MRANGE - + WITHLABELS FILTER metric=cpu GROUPBY metric_name REDUCE max

    1. "metric_name=system"
        1. "metric_name"
        2. "system"
        1. "reducer"
        2. "max"
        1. "source"
        2. "ts1"
        1. (integer) 1548149180000
        2. 90
        1. (integer) 1548149185000
        2. 45
    1. "metric_name=user"
        1. "metric_name"
        2. "user"
        1. "reducer"
        2. "max"
        1. "source"
        2. "ts2"
        1. (integer) 1548149180000
        2. 99 {{< / highlight >}}
</details> <details open> <summary><b>Filter query by value</b></summary>

Query all time series with the metric label equal to cpu, then filter values larger or equal to 90.0 and smaller or equal to 100.0.

{{< highlight bash >}} 127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system (integer) 1548149180000 127.0.0.1:6379> TS.ADD ts1 1548149185000 45 (integer) 1548149185000 127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user (integer) 1548149180000 127.0.0.1:6379> TS.MRANGE - + FILTER_BY_VALUE 90 100 WITHLABELS FILTER metric=cpu

    1. "ts1"
        1. "metric"
        2. "cpu"
        1. "metric_name"
        2. "system"
        1. (integer) 1548149180000
        2. 90
    1. "ts2"
        1. "metric"
        2. "cpu"
        1. "metric_name"
        2. "user"
        1. (integer) 1548149180000
        2. 99 {{< / highlight >}}
</details> <details open> <summary><b>Query using a label</b></summary>

Query all time series with the metric label equal to cpu, but only return the team label.

{{< highlight bash >}} 127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system team NY (integer) 1548149180000 127.0.0.1:6379> TS.ADD ts1 1548149185000 45 (integer) 1548149185000 127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user team SF (integer) 1548149180000 127.0.0.1:6379> TS.MRANGE - + SELECTED_LABELS team FILTER metric=cpu

    1. "ts1"
        1. "team"
        2. "NY"
        1. (integer) 1548149180000
        2. 90
        1. (integer) 1548149185000
        2. 45
    1. "ts2"
        1. "team"
        2. "SF"
        1. (integer) 1548149180000
        2. 99 {{< / highlight >}}
</details>

See also

[TS.RANGE]({{< relref "commands/ts.range/" >}}) | [TS.MREVRANGE]({{< relref "commands/ts.mrevrange/" >}}) | [TS.REVRANGE]({{< relref "commands/ts.revrange/" >}})

[RedisTimeSeries]({{< relref "/develop/data-types/timeseries/" >}})