Skip to content

Commit

Permalink
Fix for 'toList' reducer results empty (#346)
Browse files Browse the repository at this point in the history
* issue #342- fix for 'toList' reducer results empty

* fix collection initializer
  • Loading branch information
atakavci authored Nov 7, 2024
1 parent 73e7a6d commit dd11910
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 19 deletions.
53 changes: 45 additions & 8 deletions src/NRedisStack/Search/AggregationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ namespace NRedisStack.Search;
public sealed class AggregationResult
{
public long TotalResults { get; }
private readonly Dictionary<string, RedisValue>[] _results;
private readonly Dictionary<string, object>[] _results;
private Dictionary<string, RedisValue>[] _resultsAsRedisValues;

public long CursorId { get; }


Expand All @@ -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<string, RedisValue>[arr.Length - 1];
_results = new Dictionary<string, object>[arr.Length - 1];
for (int i = 1; i < arr.Length; i++)
{
var raw = (RedisResult[])arr[i]!;
var cur = new Dictionary<string, RedisValue>();
var cur = new Dictionary<string, object>();
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;
Expand All @@ -52,17 +59,47 @@ private object ConvertMultiBulkToObject(IEnumerable<RedisResult> multiBulkArray)
{
return multiBulkArray.Select(item => item.Type == ResultType.MultiBulk
? ConvertMultiBulkToObject((RedisResult[])item!)
: item)
: (RedisValue)item)
.ToList();
}

public IReadOnlyList<Dictionary<string, RedisValue>> GetResults() => _results;
/// <summary>
/// Gets the results as a read-only list of dictionaries with string keys and RedisValue values.
/// </summary>
/// <remarks>
/// This method is deprecated and will be removed in future versions.
/// Please use <see cref="GetRow"/> instead.
/// </remarks>
[Obsolete("This method is deprecated and will be removed in future versions. Please use 'GetRow' instead.")]
public IReadOnlyList<Dictionary<string, RedisValue>> GetResults()
{
return getResultsAsRedisValues();
}

/// <summary>
/// Gets the aggregation result at the specified index.
/// </summary>
/// <param name="index">The zero-based index of the aggregation result to retrieve.</param>
/// <returns>
/// A dictionary containing the aggregation result as Redis values if the index is within bounds;
/// otherwise, <c>null</c>.
/// </returns>
[Obsolete("This method is deprecated and will be removed in future versions. Please use 'GetRow' instead.")]
public Dictionary<string, RedisValue>? 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<string, RedisValue>[] 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;
}
}
11 changes: 6 additions & 5 deletions src/NRedisStack/Search/Row.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ namespace NRedisStack.Search.Aggregation
{
public readonly struct Row
{
private readonly Dictionary<string, RedisValue> _fields;
private readonly Dictionary<string, object> _fields;

internal Row(Dictionary<string, RedisValue> fields)
internal Row(Dictionary<string, object> 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;
}
}
23 changes: 17 additions & 6 deletions tests/NRedisStack.Tests/Search/SearchTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<object> { "RediSearch", "RedisAI", "RedisJson" };
var actual = (List<object>)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"));
Expand All @@ -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<object>)res.Get("random");
Assert.Equal(2, actual.Count);
List<string> possibleValues = new List<string>() { "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<object>)res.Get("docs");
expected = new List<object> { "ai", "search", "json" };
Assert.True(!expected.Except(actual).Any() && expected.Count == actual.Count);
}


Expand Down

0 comments on commit dd11910

Please sign in to comment.