Skip to content

Commit 1eeb257

Browse files
authored
Handle INDEXEMPTY and INDEXMISSING in FT.INFO response (#356)
* issue #351 - handle INDEXEMPTY and INDEXMISSING in FT.INFO response properly * should work with enterprise and cluster but not with olders without INDEXMISSING support * test run against 7.3 and later
1 parent 8246616 commit 1eeb257

File tree

2 files changed

+59
-14
lines changed

2 files changed

+59
-14
lines changed

src/NRedisStack/Search/DataTypes/InfoResult.cs

+25-14
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
using StackExchange.Redis;
1+
using System.Reflection.Emit;
2+
using StackExchange.Redis;
23

34
namespace NRedisStack.Search.DataTypes;
45

56
public class InfoResult
67
{
78
private readonly Dictionary<string, RedisResult> _all = new();
8-
private static readonly string[] booleanAttributes = { "SORTABLE", "UNF", "NOSTEM", "NOINDEX", "CASESENSITIVE", "WITHSUFFIXTRIE" };
9+
private Dictionary<string, RedisResult>[] _attributes;
10+
private Dictionary<string, RedisResult> _indexOption;
11+
private Dictionary<string, RedisResult> _gcStats;
12+
private Dictionary<string, RedisResult> _cursorStats;
13+
14+
private static readonly string[] booleanAttributes = { "SORTABLE", "UNF", "NOSTEM", "NOINDEX", "CASESENSITIVE", "WITHSUFFIXTRIE", "INDEXEMPTY", "INDEXMISSING" };
915
public string IndexName => GetString("index_name")!;
10-
public Dictionary<string, RedisResult> IndexOption => GetRedisResultDictionary("index_options")!;
11-
public Dictionary<string, RedisResult>[] Attributes => GetRedisResultDictionaryArray("attributes")!;
16+
public Dictionary<string, RedisResult> IndexOption => _indexOption = _indexOption ?? GetRedisResultDictionary("index_options")!;
17+
public Dictionary<string, RedisResult>[] Attributes => _attributes = _attributes ?? GetAttributesAsDictionaryArray()!;
1218
public long NumDocs => GetLong("num_docs");
1319
public string MaxDocId => GetString("max_doc_id")!;
1420
public long NumTerms => GetLong("num_terms");
@@ -48,9 +54,9 @@ public class InfoResult
4854
public long NumberOfUses => GetLong("number_of_uses");
4955

5056

51-
public Dictionary<string, RedisResult> GcStats => GetRedisResultDictionary("gc_stats")!;
57+
public Dictionary<string, RedisResult> GcStats => _gcStats = _gcStats ?? GetRedisResultDictionary("gc_stats")!;
5258

53-
public Dictionary<string, RedisResult> CursorStats => GetRedisResultDictionary("cursor_stats")!;
59+
public Dictionary<string, RedisResult> CursorStats => _cursorStats = _cursorStats ?? GetRedisResultDictionary("cursor_stats")!;
5460

5561
public InfoResult(RedisResult result)
5662
{
@@ -94,24 +100,29 @@ private double GetDouble(string key)
94100
return result;
95101
}
96102

97-
private Dictionary<string, RedisResult>[]? GetRedisResultDictionaryArray(string key)
103+
private Dictionary<string, RedisResult>[]? GetAttributesAsDictionaryArray()
98104
{
99-
if (!_all.TryGetValue(key, out var value)) return default;
105+
if (!_all.TryGetValue("attributes", out var value)) return default;
100106
var values = (RedisResult[])value!;
101107
var result = new Dictionary<string, RedisResult>[values.Length];
102108
for (int i = 0; i < values.Length; i++)
103109
{
104-
var fv = (RedisResult[])values[i]!;
105110
var dict = new Dictionary<string, RedisResult>();
106-
for (int j = 0; j < fv.Length; j += 2)
111+
112+
IEnumerable<RedisResult> enumerable = (RedisResult[])values[i]!;
113+
IEnumerator<RedisResult> results = enumerable.GetEnumerator();
114+
while (results.MoveNext())
107115
{
108-
if (booleanAttributes.Contains((string)fv[j]!))
116+
string attribute = (string)results.Current;
117+
// if its boolean attributes add itself to the dictionary and continue
118+
if (booleanAttributes.Contains(attribute))
109119
{
110-
dict.Add((string)fv[j]!, fv[j--]);
120+
dict.Add(attribute, results.Current);
111121
}
112122
else
113-
{
114-
dict.Add((string)fv[j]!, fv[j + 1]);
123+
{//if its not a boolean attribute, add the next item as value to the dictionary
124+
results.MoveNext(); ;
125+
dict.Add(attribute, results.Current);
115126
}
116127
}
117128
result[i] = dict;

tests/NRedisStack.Tests/Search/SearchTests.cs

+34
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,40 @@ public void AlterAddSortable()
887887
Assert.Equal(4, info.CursorStats.Count);
888888
}
889889

890+
[SkipIfRedis(Comparison.LessThan, "7.3.0")]
891+
public void InfoWithIndexEmptyAndIndexMissing()
892+
{
893+
IDatabase db = redisFixture.Redis.GetDatabase();
894+
db.Execute("FLUSHALL");
895+
var ft = db.FT(2);
896+
var vectorAttrs = new Dictionary<string, object>()
897+
{
898+
["TYPE"] = "FLOAT32",
899+
["DIM"] = "2",
900+
["DISTANCE_METRIC"] = "L2",
901+
};
902+
903+
Schema sc = new Schema()
904+
.AddTextField("text1", 1.0, emptyIndex: true, missingIndex: true)
905+
.AddTagField("tag1", emptyIndex: true, missingIndex: true)
906+
.AddNumericField("numeric1", missingIndex: true)
907+
.AddGeoField("geo1", missingIndex: true)
908+
.AddGeoShapeField("geoshape1", Schema.GeoShapeField.CoordinateSystem.FLAT, missingIndex: true)
909+
.AddVectorField("vector1", Schema.VectorField.VectorAlgo.FLAT, vectorAttrs, missingIndex: true);
910+
Assert.True(ft.Create(index, FTCreateParams.CreateParams(), sc));
911+
912+
var info = ft.Info(index);
913+
var attributes = info.Attributes;
914+
foreach (var attribute in attributes)
915+
{
916+
Assert.True(attribute.ContainsKey("INDEXMISSING"));
917+
if (attribute["attribute"].ToString() == "text1" || attribute["attribute"].ToString() == "tag1")
918+
{
919+
Assert.True(attribute.ContainsKey("INDEXEMPTY"));
920+
}
921+
}
922+
}
923+
890924
[SkipIfRedis(Is.OSSCluster, Is.Enterprise)]
891925
public async Task AlterAddSortableAsync()
892926
{

0 commit comments

Comments
 (0)