From 194f9f6307ea064442f1a5bff0be451271a7606e Mon Sep 17 00:00:00 2001 From: atakavci Date: Thu, 31 Oct 2024 14:08:01 +0300 Subject: [PATCH 1/2] issue #342- fix for 'toList' reducer results empty --- src/NRedisStack/Search/AggregationResult.cs | 53 ++++++++++++++++--- src/NRedisStack/Search/Row.cs | 11 ++-- tests/NRedisStack.Tests/Search/SearchTests.cs | 23 +++++--- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/NRedisStack/Search/AggregationResult.cs b/src/NRedisStack/Search/AggregationResult.cs index f3410c29..22f861a6 100644 --- a/src/NRedisStack/Search/AggregationResult.cs +++ b/src/NRedisStack/Search/AggregationResult.cs @@ -6,7 +6,9 @@ namespace NRedisStack.Search; public sealed class AggregationResult { public long TotalResults { get; } - private readonly Dictionary[] _results; + private readonly Dictionary[] _results; + private Dictionary[] _resultsAsRedisValues; + public long CursorId { get; } @@ -18,18 +20,23 @@ internal AggregationResult(RedisResult result, long cursorId = -1) // // the first element is always the number of results // TotalResults = (long)arr[0]; - _results = new Dictionary[arr.Length - 1]; + _results = new Dictionary[arr.Length - 1]; for (int i = 1; i < arr.Length; i++) { var raw = (RedisResult[])arr[i]!; - var cur = new Dictionary(); + var cur = new Dictionary(); for (int j = 0; j < raw.Length;) { var key = (string)raw[j++]!; var val = raw[j++]; if (val.Type == ResultType.MultiBulk) - continue; // TODO: handle multi-bulk (maybe change to object?) - cur.Add(key, (RedisValue)val); + { + cur.Add(key, ConvertMultiBulkToObject((RedisResult[])val!)); + } + else + { + cur.Add(key, (RedisValue)val); + } } _results[i - 1] = cur; @@ -52,17 +59,47 @@ private object ConvertMultiBulkToObject(IEnumerable multiBulkArray) { return multiBulkArray.Select(item => item.Type == ResultType.MultiBulk ? ConvertMultiBulkToObject((RedisResult[])item!) - : item) + : (RedisValue)item) .ToList(); } - public IReadOnlyList> GetResults() => _results; + /// + /// Gets the results as a read-only list of dictionaries with string keys and RedisValue values. + /// + /// + /// This method is deprecated and will be removed in future versions. + /// Please use instead. + /// + [Obsolete("This method is deprecated and will be removed in future versions. Please use 'GetRow' instead.")] + public IReadOnlyList> GetResults() + { + return getResultsAsRedisValues(); + } + /// + /// Gets the aggregation result at the specified index. + /// + /// The zero-based index of the aggregation result to retrieve. + /// + /// A dictionary containing the aggregation result as Redis values if the index is within bounds; + /// otherwise, null. + /// + [Obsolete("This method is deprecated and will be removed in future versions. Please use 'GetRow' instead.")] public Dictionary? this[int index] - => index >= _results.Length ? null : _results[index]; + => index >= getResultsAsRedisValues().Length ? null : getResultsAsRedisValues()[index]; public Row GetRow(int index) { return index >= _results.Length ? default : new Row(_results[index]); } + + private Dictionary[] getResultsAsRedisValues() + { + if (_resultsAsRedisValues == null) + _resultsAsRedisValues = _results.Select(dict => dict.ToDictionary( + kvp => kvp.Key, + kvp => kvp.Value is RedisValue value ? value : RedisValue.Null + )).ToArray(); + return _resultsAsRedisValues; + } } \ No newline at end of file diff --git a/src/NRedisStack/Search/Row.cs b/src/NRedisStack/Search/Row.cs index 0caac94e..07153f85 100644 --- a/src/NRedisStack/Search/Row.cs +++ b/src/NRedisStack/Search/Row.cs @@ -4,18 +4,19 @@ namespace NRedisStack.Search.Aggregation { public readonly struct Row { - private readonly Dictionary _fields; + private readonly Dictionary _fields; - internal Row(Dictionary fields) + internal Row(Dictionary fields) { _fields = fields; } public bool ContainsKey(string key) => _fields.ContainsKey(key); - public RedisValue this[string key] => _fields.TryGetValue(key, out var result) ? result : RedisValue.Null; + public RedisValue this[string key] => _fields.TryGetValue(key, out var result) ? (result is RedisValue ? (RedisValue)result : RedisValue.Null) : RedisValue.Null; + public object Get(string key) => _fields.TryGetValue(key, out var result) ? result : RedisValue.Null; public string? GetString(string key) => _fields.TryGetValue(key, out var result) ? result.ToString() : default; - public long GetLong(string key) => _fields.TryGetValue(key, out var result) ? (long)result : default; - public double GetDouble(string key) => _fields.TryGetValue(key, out var result) ? (double)result : default; + public long GetLong(string key) => _fields.TryGetValue(key, out var result) ? (long)(RedisValue)result : default; + public double GetDouble(string key) => _fields.TryGetValue(key, out var result) ? (double)(RedisValue)result : default; } } \ No newline at end of file diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index b39bcb09..00cb589a 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -1256,7 +1256,9 @@ public void TestAggregationGroupBy() res = rawRes.GetRow(0); Assert.Equal("redis", res["parent"]); // TODO: complete this assert after handling multi bulk reply - //Assert.Equal((RedisValue[])res["__generated_aliastolisttitle"], { "RediSearch", "RedisAI", "RedisJson"}); + var expected = new List { "RediSearch", "RedisAI", "RedisJson" }; + var actual = (List)res.Get("__generated_aliastolisttitle"); + Assert.True(!expected.Except(actual).Any() && expected.Count == actual.Count); req = new AggregationRequest("redis").GroupBy( "@parent", Reducers.FirstValue("@title").As("first")); @@ -1269,11 +1271,20 @@ public void TestAggregationGroupBy() res = ft.Aggregate("idx", req).GetRow(0); Assert.Equal("redis", res["parent"]); // TODO: complete this assert after handling multi bulk reply - // Assert.Equal(res[2], "random"); - // Assert.Equal(len(res[3]), 2); - // Assert.Equal(res[3][0] in ["RediSearch", "RedisAI", "RedisJson"]); - // req = new AggregationRequest("redis").GroupBy("@parent", redu - + actual = (List)res.Get("random"); + Assert.Equal(2, actual.Count); + List possibleValues = ["RediSearch", "RedisAI", "RedisJson"]; + Assert.Contains(actual[0].ToString(), possibleValues); + Assert.Contains(actual[1].ToString(), possibleValues); + + req = new AggregationRequest("redis") + .Load(new FieldName("__key")) + .GroupBy("@parent", Reducers.ToList("__key").As("docs")); + + res = db.FT().Aggregate("idx", req).GetRow(0); + actual = (List)res.Get("docs"); + expected = new List { "ai", "search", "json" }; + Assert.True(!expected.Except(actual).Any() && expected.Count == actual.Count); } From c195475838f8a5e7db8f635c8cba8d860794149b Mon Sep 17 00:00:00 2001 From: atakavci Date: Thu, 31 Oct 2024 14:18:25 +0300 Subject: [PATCH 2/2] fix collection initializer --- tests/NRedisStack.Tests/Search/SearchTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 00cb589a..2ed10466 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -1273,7 +1273,7 @@ public void TestAggregationGroupBy() // TODO: complete this assert after handling multi bulk reply actual = (List)res.Get("random"); Assert.Equal(2, actual.Count); - List possibleValues = ["RediSearch", "RedisAI", "RedisJson"]; + List possibleValues = new List() { "RediSearch", "RedisAI", "RedisJson" }; Assert.Contains(actual[0].ToString(), possibleValues); Assert.Contains(actual[1].ToString(), possibleValues);