Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions redis/commands/timeseries/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def add(
self,
key: KeyT,
timestamp: Union[int, str],
value: Number,
value: Union[Number, str],
retention_msecs: Optional[int] = None,
uncompressed: Optional[bool] = False,
labels: Optional[Dict[str, str]] = None,
Expand Down Expand Up @@ -251,7 +251,7 @@ def add(

return self.execute_command(ADD_CMD, *params)

def madd(self, ktv_tuples: List[Tuple[KeyT, Union[int, str], Number]]):
def madd(self, ktv_tuples: List[Tuple[KeyT, Union[int, str], Union[Number, str]]]):
"""
Append new samples to one or more time series.

Expand Down Expand Up @@ -507,7 +507,7 @@ def createrule(
aggregation_type:
Aggregation type: One of the following:
[`avg`, `sum`, `min`, `max`, `range`, `count`, `first`, `last`, `std.p`,
`std.s`, `var.p`, `var.s`, `twa`]
`std.s`, `var.p`, `var.s`, `twa`, 'countNaN', 'countAll']
bucket_size_msec:
Duration of each bucket, in milliseconds.
align_timestamp:
Expand Down Expand Up @@ -593,7 +593,7 @@ def range(
aggregation_type:
Optional aggregation type. Can be one of [`avg`, `sum`, `min`, `max`,
`range`, `count`, `first`, `last`, `std.p`, `std.s`, `var.p`, `var.s`,
`twa`]
`twa`, 'countNaN', 'countAll']
bucket_size_msec:
Time bucket for aggregation in milliseconds.
filter_by_ts:
Expand Down Expand Up @@ -669,7 +669,7 @@ def revrange(
aggregation_type:
Optional aggregation type. Can be one of [`avg`, `sum`, `min`, `max`,
`range`, `count`, `first`, `last`, `std.p`, `std.s`, `var.p`, `var.s`,
`twa`]
`twa`, 'countNaN', 'countAll']
bucket_size_msec:
Time bucket for aggregation in milliseconds.
filter_by_ts:
Expand Down Expand Up @@ -783,7 +783,7 @@ def mrange(
aggregation_type:
Optional aggregation type. Can be one of [`avg`, `sum`, `min`, `max`,
`range`, `count`, `first`, `last`, `std.p`, `std.s`, `var.p`, `var.s`,
`twa`]
`twa`, 'countNaN', 'countAll']
bucket_size_msec:
Time bucket for aggregation in milliseconds.
with_labels:
Expand Down Expand Up @@ -877,7 +877,7 @@ def mrevrange(
aggregation_type:
Optional aggregation type. Can be one of [`avg`, `sum`, `min`, `max`,
`range`, `count`, `first`, `last`, `std.p`, `std.s`, `var.p`, `var.s`,
`twa`].
`twa`, 'countNaN', 'countAll'].
bucket_size_msec:
Time bucket for aggregation in milliseconds.
with_labels:
Expand Down
214 changes: 214 additions & 0 deletions tests/test_asyncio/test_timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -915,3 +915,217 @@ async def test_decrby_with_insertion_filters(decoded_r: redis.Redis):

data_points = await decoded_r.ts().range("time-series-1", "-", "+")
assert_resp_response(decoded_r, data_points, [(1000, -11.1)], [[1000, -11.1]])


@pytest.mark.redismod
@skip_if_server_version_lt("8.5.0")
async def test_range_with_count_nan_count_all_aggregators(decoded_r: redis.Redis):
await decoded_r.ts().create(
"temperature:2:32",
)

# Fill with values
assert await decoded_r.ts().add("temperature:2:32", 1000, "NaN") == 1000
assert await decoded_r.ts().add("temperature:2:32", 1003, 25) == 1003
assert await decoded_r.ts().add("temperature:2:32", 1005, "NaN") == 1005
assert await decoded_r.ts().add("temperature:2:32", 1006, "NaN") == 1006

# Ensure we count only NaN values
data_points = await decoded_r.ts().range(
"temperature:2:32",
1000,
1006,
aggregation_type="countNan",
bucket_size_msec=1000,
)
assert_resp_response(
decoded_r,
data_points,
[(1000, 3)],
[[1000, 3]],
)

# Ensure we count ALL values
data_points = await decoded_r.ts().range(
"temperature:2:32",
1000,
1006,
aggregation_type="countAll",
bucket_size_msec=1000,
)
assert_resp_response(
decoded_r,
data_points,
[(1000, 4)],
[[1000, 4]],
)


@pytest.mark.redismod
@skip_if_server_version_lt("8.5.0")
async def test_rev_range_with_count_nan_count_all_aggregators(decoded_r: redis.Redis):
await decoded_r.ts().create(
"temperature:2:32",
)

# Fill with values
assert await decoded_r.ts().add("temperature:2:32", 1000, "NaN") == 1000
assert await decoded_r.ts().add("temperature:2:32", 1003, 25) == 1003
assert await decoded_r.ts().add("temperature:2:32", 1005, "NaN") == 1005
assert await decoded_r.ts().add("temperature:2:32", 1006, "NaN") == 1006

# Ensure we count only NaN values
data_points = await decoded_r.ts().revrange(
"temperature:2:32",
1000,
1006,
aggregation_type="countNan",
bucket_size_msec=1000,
)
assert_resp_response(
decoded_r,
data_points,
[(1000, 3)],
[[1000, 3]],
)

# Ensure we count ALL values
data_points = await decoded_r.ts().revrange(
"temperature:2:32",
1000,
1006,
aggregation_type="countAll",
bucket_size_msec=1000,
)
assert_resp_response(
decoded_r,
data_points,
[(1000, 4)],
[[1000, 4]],
)


@pytest.mark.redismod
@skip_if_server_version_lt("8.5.0")
async def test_mrange_with_count_nan_count_all_aggregators(decoded_r: redis.Redis):
await decoded_r.ts().create(
"temperature:A",
labels={"type": "temperature", "name": "A"},
)
await decoded_r.ts().create(
"temperature:B",
labels={"type": "temperature", "name": "B"},
)

# Fill with values
assert await decoded_r.ts().madd(
[("temperature:A", 1000, "NaN"), ("temperature:A", 1001, 27)]
)
assert await decoded_r.ts().madd(
[("temperature:B", 1000, "NaN"), ("temperature:B", 1001, 28)]
)

# Ensure we count only NaN values
data_points = await decoded_r.ts().mrange(
1000,
1001,
aggregation_type="countNan",
bucket_size_msec=1000,
filters=["type=temperature"],
)
assert_resp_response(
decoded_r,
data_points,
[
{"temperature:A": [{}, [(1000, 1.0)]]},
{"temperature:B": [{}, [(1000, 1.0)]]},
],
{
"temperature:A": [{}, {"aggregators": ["countnan"]}, [[1000, 1.0]]],
"temperature:B": [{}, {"aggregators": ["countnan"]}, [[1000, 1.0]]],
},
)

# Ensure we count ALL values
data_points = await decoded_r.ts().mrange(
1000,
1001,
aggregation_type="countAll",
bucket_size_msec=1000,
filters=["type=temperature"],
)
assert_resp_response(
decoded_r,
data_points,
[
{"temperature:A": [{}, [(1000, 2.0)]]},
{"temperature:B": [{}, [(1000, 2.0)]]},
],
{
"temperature:A": [{}, {"aggregators": ["countall"]}, [[1000, 2.0]]],
"temperature:B": [{}, {"aggregators": ["countall"]}, [[1000, 2.0]]],
},
)


@pytest.mark.redismod
@skip_if_server_version_lt("8.5.0")
async def test_mrevrange_with_count_nan_count_all_aggregators(decoded_r: redis.Redis):
await decoded_r.ts().create(
"temperature:A",
labels={"type": "temperature", "name": "A"},
)
await decoded_r.ts().create(
"temperature:B",
labels={"type": "temperature", "name": "B"},
)

# Fill with values
assert await decoded_r.ts().madd(
[("temperature:A", 1000, "NaN"), ("temperature:A", 1001, 27)]
)
assert await decoded_r.ts().madd(
[("temperature:B", 1000, "NaN"), ("temperature:B", 1001, 28)]
)

# Ensure we count only NaN values
data_points = await decoded_r.ts().mrevrange(
1000,
1001,
aggregation_type="countNan",
bucket_size_msec=1000,
filters=["type=temperature"],
)
assert_resp_response(
decoded_r,
data_points,
[
{"temperature:A": [{}, [(1000, 1.0)]]},
{"temperature:B": [{}, [(1000, 1.0)]]},
],
{
"temperature:A": [{}, {"aggregators": ["countnan"]}, [[1000, 1.0]]],
"temperature:B": [{}, {"aggregators": ["countnan"]}, [[1000, 1.0]]],
},
)

# Ensure we count ALL values
data_points = await decoded_r.ts().mrevrange(
1000,
1001,
aggregation_type="countAll",
bucket_size_msec=1000,
filters=["type=temperature"],
)
assert_resp_response(
decoded_r,
data_points,
[
{"temperature:A": [{}, [(1000, 2.0)]]},
{"temperature:B": [{}, [(1000, 2.0)]]},
],
{
"temperature:A": [{}, {"aggregators": ["countall"]}, [[1000, 2.0]]],
"temperature:B": [{}, {"aggregators": ["countall"]}, [[1000, 2.0]]],
},
)
Loading