Skip to content

Add TS.NRANGE / TS.NREVRANGE support - #98

Merged
mumez merged 2 commits into
developfrom
feature/ts-support_v810_3
Sep 8, 2026
Merged

mumez merged 2 commits into
developfrom
feature/ts-support_v810_3

Conversation

@mumez

@mumez mumez commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds support for the Redis 8.10 TS.NRANGE and TS.NREVRANGE commands to RsRedisEndpoint.

Changes

  • RsRedisEndpoint.extension.st: adds tsNRangeBy:keys:... / tsNRevRangeBy:keys:... public methods (with using:/aggregationBy: overloads) sharing a single private executor.
  • RsTsAggregation.class.st: refactors asArray to extract a new aggregatorsString method.
  • New RsTsNAggregation.class.st: builds per-key AGGREGATION lists sharing one bucketDuration.
  • New RsTsNRangeRow.class.st: holds a parsed timestamp/values row.
  • New tests: RsTsNAggregationTest.class.st, RsTsNRangeTest.class.st, RsTsNRevRangeTest.class.st; adds a case to RsTsAggregationTest.class.st.
  • Adds doc/scripting-features/feature-ts-nrange-nrevrange.scripting.md (orchestration script used to generate this implementation).

Commits Included

5d097da Add TS.NRANGE / TS.NREVRANGE support

Files Changed

doc/scripting-features/feature-ts-nrange-nrevrange.scripting.md
src/RediStick-TimeSeries-Tests/RsTsAggregationTest.class.st
src/RediStick-TimeSeries-Tests/RsTsNAggregationTest.class.st
src/RediStick-TimeSeries-Tests/RsTsNRangeTest.class.st
src/RediStick-TimeSeries-Tests/RsTsNRevRangeTest.class.st
src/RediStick-TimeSeries/RsRedisEndpoint.extension.st
src/RediStick-TimeSeries/RsTsAggregation.class.st
src/RediStick-TimeSeries/RsTsNAggregation.class.st
src/RediStick-TimeSeries/RsTsNRangeRow.class.st

Implements TS.NRANGE and TS.NREVRANGE (Redis 8.10+), which query an
explicit list of keys and group results by timestamp rather than by
key. Adds RsTsNAggregation (per-key AGGREGATION lists sharing one
bucketDuration) and RsTsNRangeRow, and refactors RsTsAggregation to
expose aggregatorsString for reuse. Both commands share a single
private executor in RsRedisEndpoint, mirroring the existing
TS.MRANGE/TS.MREVRANGE pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
nAgg bucketDuration: 1000 ].

self assert: result notEmpty.
self assert: result first values size equals: 3

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: This is the crucible test for the novel AGGREGATION per-key contract, but it only asserts the value count (3), not that aggregators mapped to the right keys or produced the right numbers. Give the data (key1: 10,20 → avg 15; key2: 100,200 → min 100, max 200), it would pass even if the aggregator→key mapping or the shared bucketDuration were wrong. Suggest asserting exact values, e.g. self assertCollection: result first values equals: #(15 100 200). That locks the "first agg-list↔first key, second↔second key" ordering, which is the most error-prone part of the wire format.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f3a949e — now asserts exact values #(15 100 200) instead of just the count.


self assert: result second timestamp equals: 2000.
self assert: result second values size equals: 2.
self assert: result second values first equals: 30

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI / Optional: At timestamp 2000, key2 has no sample. Redis reports that as NaN; through the reused tsParseValue: it surfaces to callers as the string 'NaN' (the package convention of returning unparseable values as raw strings, same as single-key TS.RANGE). This test checks result second values first (key1=30) but not the missing slot, so the representation is not pinned. Consider asserting it (e.g. 'NaN') so consumers know what a missing value looks like rather than a Smalltalk Float.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f3a949e — the missing slot is actually a Smalltalk Float nan (not a string), since tsParseValue: parses the wire value via NumberParser. Now asserted with isNaN.

@@ -0,0 +1,47 @@
Class {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit / Consider: RsTsNAggregation is a surface-area-heavy class with no class comment, and the N prefix is only meaningful to someone who already knows it stands for the TS.NRANGE/TS.NREVRANGE family (note the asymmetry vs. RsTsRangeOptions, which is reused here rather than duplicated). A short comment documenting the contract would help a lot: one RsTsAggregation per key in key order, comma-joined multi-aggregator lists allowed per key, one shared bucketDuration, and that asArray deliberately bypasses the single-aggregator check (via aggregatorsString) because per-key multi-aggregators are legal here.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f3a949e — added a CRC-style class comment documenting the per-key AGGREGATION contract (one RsTsAggregation per key in order, shared bucketDuration, and why asArray uses aggregatorsString to bypass the single-aggregator check).

@mumez mumez left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: Approve — well-scoped, correctly-verified implementation; all findings below are Optional/FYI/Nit and non-blocking.

What I verified

  • Wire grammar (highest-leverage check): compared tsNRangeArgsFor: against the official TS.NRANGE doc. Order is exactly right: cmdName numkeys key... fromTimestamp toTimestamp, then LATEST/FILTER_BY_TS/FILTER_BY_VALUE/COUNT, then ALIGN/AGGREGATION <per-key lists> bucketDuration/BUCKETTIMESTAMP/EMPTY. Reusing RsTsRangeOptions unchanged is safe (its asArray emits exactly those 4 range tokens in grammar order), and injecting RsTsNAggregation into the unchanged RsTsAggregationOptions produces the correct AGGREGATION <list...> bucketDuration shape. The aggregatorsString refactor exists precisely so per-key comma-joined lists bypass the single-aggregator check — good design.
  • Reply parsing: rows are {timestamp. flatValuesArray}, ordered by key/aggregator input order; tsParseNRangeResult: mirrors the existing parse helpers. Missing values (NaN) surface as the raw string 'NaN' via the package's existing tsParseValue: convention — consistent, no special-casing added.
  • CI: green on Pharo 11/12/13 (smalltalkCI runs the TimeSeriesTests group).
  • Doc convention: the .scripting.md matches 13 existing siblings under doc/scripting-features/.
  • DRY/responsibility: the 8 forwarding shims (4× NRANGE, 4× NREVRANGE) and the single shared tsExecuteNRange: executor mirror the established tsMRange/tsMRevRange pattern; NREVRANGE reuses the same executor with only cmdName swapped — no duplicated command building or parsing.

Inline comments (3, all non-blocking)

  1. Optional — aggregation integration test (RsTsNRangeTest) only asserts value count, not that per-key aggregators mapped to the right keys or computed correctly; add exact value assertions (#(15 100 200)).
  2. FYI/Optional — missing-value slot (key2 absent at ts 2000) returns the string 'NaN'; not asserted or documented.
  3. Nit/Consider — RsTsNAggregation has no class comment; the N prefix is cryptic in isolation and asymmetric with the reused RsTsRangeOptions. A short doc note would help.

No Critical, Required, or security/performance issues found.
PR: #98

Pin exact aggregated values and the missing-value NaN representation
in RsTsNRangeTest instead of only checking counts, and add a CRC-style
class comment to RsTsNAggregation documenting its per-key AGGREGATION
contract.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mumez
mumez merged commit 79926c5 into develop Sep 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant