From 0f3235bfcdbfdf34839b8924836efc9d8e3ba180 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Thu, 30 Jul 2026 07:45:13 +0100 Subject: [PATCH] Mark the COLLECT search API as [Experimental] (NRS003); SE.Redis 3.0.25 REDUCE COLLECT is still gated behind the server's search-enable-unstable-features flag, so its wire format can move under us. It shipped as stable public API in v1.7.0 by oversight; this re-flags it as experimental for 1.7.1. Follows the established NRS ritual (cf. bed9558 for NRS002): - Experiments.SearchCollect const, reusing id NRS003. That id briefly existed on marc/8_10 for server 8.10 but was removed before any release - confirmed absent from every v* tag - so it is free to reuse under the 'ids that reached users are never reused' rule, and gets no retired-id breadcrumb. - [Experimental] on CollectReducer and Reducers.Collect(). - [NRS003] prefix on all 10 COLLECT entries in PublicAPI.Shipped.txt. The analyzer requires the prefix on every member of an experimental type, not just the type. - docs/exp/NRS003.md, and NRS003 added to Directory.Build.props NoWarn. Also bumps StackExchange.Redis 3.0.17 -> 3.0.25. Verified from outside the repo (where NoWarn does not apply) that a consumer gets 'error NRS003' with the correct exp/NRS003 url, on both net8.0 (BCL attribute) and netstandard2.0 (the internal polyfill). Full 'dotnet build -t:Rebuild' clean. --- Directory.Build.props | 2 +- Directory.Packages.props | 2 +- docs/exp/NRS003.md | 33 +++++++++++++++++++ src/NRedisStack/Experiments.cs | 2 ++ .../PublicAPI/PublicAPI.Shipped.txt | 20 +++++------ src/NRedisStack/Search/CollectReducer.cs | 4 ++- src/NRedisStack/Search/Reducers.cs | 5 ++- 7 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 docs/exp/NRS003.md diff --git a/Directory.Build.props b/Directory.Build.props index 008d7f4e..13e3103d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -23,7 +23,7 @@ true true true - $(NoWarn);CS1591 + $(NoWarn);CS1591;NRS003 $([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::get_Windows()))) diff --git a/Directory.Packages.props b/Directory.Packages.props index b61ed977..31c1d6bc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,7 +12,7 @@ - + diff --git a/docs/exp/NRS003.md b/docs/exp/NRS003.md new file mode 100644 index 00000000..c9db3317 --- /dev/null +++ b/docs/exp/NRS003.md @@ -0,0 +1,33 @@ +`REDUCE COLLECT` is gated behind the server's *unstable features* flag; the feature and its API may be subject to change. + +*COLLECT* is an `FT.AGGREGATE` reducer that gathers per-document projections within a `GROUPBY` group and returns them +as an array of per-entry maps under the reducer alias, optionally sorted and bounded. + +The server does not expose it by default: it must be enabled explicitly, for example via + +``` +CONFIG SET search-enable-unstable-features yes +``` + +In a cluster the coordinator fans the query out to every shard, so the flag must be enabled on all nodes rather than +only the one a given connection happens to route to. Without it, the server replies with an error. + +The corresponding library feature must also be considered subject to change: + +1. Existing bindings may cease working correctly if the underlying server API changes. +2. Changes to the server API may require changes to the library API, manifesting in either/both of build-time + or run-time breaks. + +Because the server itself still classes this as an unstable feature, such changes are rather more likely here than for +a feature that has simply not been out for long. If you acknowledge this, you can suppress this warning by adding the +following to your `csproj` file: + +```xml +$(NoWarn);NRS003 +``` + +or more granularly / locally in C#: + +```c# +#pragma warning disable NRS003 +``` diff --git a/src/NRedisStack/Experiments.cs b/src/NRedisStack/Experiments.cs index eee7520f..0c5e266f 100644 --- a/src/NRedisStack/Experiments.cs +++ b/src/NRedisStack/Experiments.cs @@ -9,6 +9,8 @@ namespace NRedisStack // NRS002 - Redis 8.8 multi-aggregate time-series internal static class Experiments { + public const string SearchCollect = "NRS003"; + // {0} is substituted with the diagnostic id, e.g. NRS042 -> https://redis.github.io/NRedisStack/exp/NRS042 public const string UrlFormat = "https://redis.github.io/NRedisStack/exp/{0}"; } diff --git a/src/NRedisStack/PublicAPI/PublicAPI.Shipped.txt b/src/NRedisStack/PublicAPI/PublicAPI.Shipped.txt index 5085b70e..8f015a70 100644 --- a/src/NRedisStack/PublicAPI/PublicAPI.Shipped.txt +++ b/src/NRedisStack/PublicAPI/PublicAPI.Shipped.txt @@ -1670,13 +1670,13 @@ NRedisStack.ISearchCommandsAsync.AliasListAsync(string! index) -> System.Threadi NRedisStack.SearchCommands.AliasList(string! index) -> StackExchange.Redis.RedisResult![]! NRedisStack.SearchCommandsAsync.AliasListAsync(string! index) -> System.Threading.Tasks.Task! static NRedisStack.SearchCommandBuilder.AliasList(string! index) -> NRedisStack.RedisStackCommands.SerializedCommand! -NRedisStack.Search.Aggregation.CollectReducer -override NRedisStack.Search.Aggregation.CollectReducer.Name.get -> string! -NRedisStack.Search.Aggregation.CollectReducer.Fields(params string![]! fields) -> NRedisStack.Search.Aggregation.CollectReducer! -NRedisStack.Search.Aggregation.CollectReducer.FieldsAll() -> NRedisStack.Search.Aggregation.CollectReducer! -NRedisStack.Search.Aggregation.CollectReducer.SortBy(params NRedisStack.Search.Aggregation.SortedField![]! fields) -> NRedisStack.Search.Aggregation.CollectReducer! -NRedisStack.Search.Aggregation.CollectReducer.SortByAsc(string! field) -> NRedisStack.Search.Aggregation.CollectReducer! -NRedisStack.Search.Aggregation.CollectReducer.SortByDesc(string! field) -> NRedisStack.Search.Aggregation.CollectReducer! -NRedisStack.Search.Aggregation.CollectReducer.Limit(int count) -> NRedisStack.Search.Aggregation.CollectReducer! -NRedisStack.Search.Aggregation.CollectReducer.Limit(int offset, int count) -> NRedisStack.Search.Aggregation.CollectReducer! -static NRedisStack.Search.Aggregation.Reducers.Collect() -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer +[NRS003]override NRedisStack.Search.Aggregation.CollectReducer.Name.get -> string! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer.Fields(params string![]! fields) -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer.FieldsAll() -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer.SortBy(params NRedisStack.Search.Aggregation.SortedField![]! fields) -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer.SortByAsc(string! field) -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer.SortByDesc(string! field) -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer.Limit(int count) -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]NRedisStack.Search.Aggregation.CollectReducer.Limit(int offset, int count) -> NRedisStack.Search.Aggregation.CollectReducer! +[NRS003]static NRedisStack.Search.Aggregation.Reducers.Collect() -> NRedisStack.Search.Aggregation.CollectReducer! diff --git a/src/NRedisStack/Search/CollectReducer.cs b/src/NRedisStack/Search/CollectReducer.cs index 878697cc..08b9d1bb 100644 --- a/src/NRedisStack/Search/CollectReducer.cs +++ b/src/NRedisStack/Search/CollectReducer.cs @@ -1,4 +1,5 @@ -using NRedisStack.Search.Literals; +using System.Diagnostics.CodeAnalysis; +using NRedisStack.Search.Literals; namespace NRedisStack.Search.Aggregation; @@ -33,6 +34,7 @@ namespace NRedisStack.Search.Aggregation; /// /// /// +[Experimental(Experiments.SearchCollect, UrlFormat = Experiments.UrlFormat)] public sealed class CollectReducer : Reducer { private bool _allFields = false; diff --git a/src/NRedisStack/Search/Reducers.cs b/src/NRedisStack/Search/Reducers.cs index d8a20e23..b348d032 100644 --- a/src/NRedisStack/Search/Reducers.cs +++ b/src/NRedisStack/Search/Reducers.cs @@ -1,4 +1,6 @@ -namespace NRedisStack.Search.Aggregation; +using System.Diagnostics.CodeAnalysis; + +namespace NRedisStack.Search.Aggregation; public static class Reducers { @@ -92,6 +94,7 @@ protected override void AddOwnArgs(List args) /// /// /// + [Experimental(Experiments.SearchCollect, UrlFormat = Experiments.UrlFormat)] public static CollectReducer Collect() => new CollectReducer(); public static Reducer RandomSample(string field, int size) => new RandomSampleReducer(field, size);