Skip to content

Commit fd5fe53

Browse files
authored
Backport of: Fix index creation issue when multiple options are used with fields (#405) (#413)
Fix index creation issue when multiple options are used with fields (#405) * - change order of field options in search&query - add missing xml doc for parameters * fix search tests * fix format
1 parent f591719 commit fd5fe53

File tree

3 files changed

+127
-11
lines changed

3 files changed

+127
-11
lines changed

src/NRedisStack/Search/Schema.cs

+46-9
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,10 @@ internal override void AddFieldTypeArgs(List<object> args)
9797
AddPhonetic(args);
9898
AddWeight(args);
9999
if (WithSuffixTrie) args.Add(SearchArgs.WITHSUFFIXTRIE);
100-
if (Sortable) args.Add(FieldOptions.SORTABLE);
101100
if (Unf) args.Add(SearchArgs.UNF);
102101
if (MissingIndex) args.Add(FieldOptions.INDEXMISSING);
103102
if (EmptyIndex) args.Add(FieldOptions.INDEXEMPTY);
104-
103+
if (Sortable) args.Add(FieldOptions.SORTABLE);
105104
}
106105

107106
private void AddWeight(List<object> args)
@@ -165,10 +164,10 @@ internal override void AddFieldTypeArgs(List<object> args)
165164
args.Add(Separator);
166165
}
167166
if (CaseSensitive) args.Add(SearchArgs.CASESENSITIVE);
168-
if (Sortable) args.Add(FieldOptions.SORTABLE);
169167
if (Unf) args.Add(SearchArgs.UNF);
170168
if (MissingIndex) args.Add(FieldOptions.INDEXMISSING);
171169
if (EmptyIndex) args.Add(FieldOptions.INDEXEMPTY);
170+
if (Sortable) args.Add(FieldOptions.SORTABLE);
172171
}
173172
}
174173

@@ -192,10 +191,9 @@ internal GeoField(string name, bool sortable = false, bool noIndex = false, bool
192191
internal override void AddFieldTypeArgs(List<object> args)
193192
{
194193
if (NoIndex) args.Add(SearchArgs.NOINDEX);
195-
if (Sortable) args.Add(FieldOptions.SORTABLE);
196194
if (MissingIndex) args.Add(FieldOptions.INDEXMISSING);
195+
if (Sortable) args.Add(FieldOptions.SORTABLE);
197196
}
198-
199197
}
200198

201199
public class GeoShapeField : Field
@@ -252,10 +250,9 @@ internal NumericField(string name, bool sortable = false, bool noIndex = false,
252250
internal override void AddFieldTypeArgs(List<object> args)
253251
{
254252
if (NoIndex) args.Add(SearchArgs.NOINDEX);
255-
if (Sortable) args.Add(FieldOptions.SORTABLE);
256253
if (MissingIndex) args.Add(FieldOptions.INDEXMISSING);
254+
if (Sortable) args.Add(FieldOptions.SORTABLE);
257255
}
258-
259256
}
260257

261258
public class VectorField : Field
@@ -322,6 +319,10 @@ public Schema AddField(Field field)
322319
/// <param name="unf">Set this to true to prevent the indexer from sorting on the normalized form.
323320
/// Normalied form is the field sent to lower case with all diaretics removed</param>
324321
/// <param name="withSuffixTrie">Keeps a suffix trie with all terms which match the suffix.</param>
322+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
323+
/// Note the difference between a field with an empty value and a document with a missing value.
324+
/// By default, missing values are not indexed.</param>
325+
/// <param name="emptyIndex"> allows you to index and search for empty strings. By default, empty strings are not indexed.</param>
325326
/// <returns>The <see cref="Schema"/> object.</returns>
326327
public Schema AddTextField(string name, double weight = 1.0, bool sortable = false, bool unf = false, bool noStem = false,
327328
string? phonetic = null, bool noIndex = false, bool withSuffixTrie = false, bool missingIndex = false, bool emptyIndex = false)
@@ -342,6 +343,10 @@ public Schema AddTextField(string name, double weight = 1.0, bool sortable = fal
342343
/// <param name="unf">Set this to true to prevent the indexer from sorting on the normalized form.
343344
/// Normalied form is the field sent to lower case with all diaretics removed</param>
344345
/// <param name="withSuffixTrie">Keeps a suffix trie with all terms which match the suffix.</param>
346+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
347+
/// Note the difference between a field with an empty value and a document with a missing value.
348+
/// By default, missing values are not indexed.</param>
349+
/// <param name="emptyIndex"> allows you to index and search for empty strings. By default, empty strings are not indexed.</param>
345350
/// <returns>The <see cref="Schema"/> object.</returns>
346351
public Schema AddTextField(FieldName name, double weight = 1.0, bool sortable = false, bool unf = false, bool noStem = false,
347352
string? phonetic = null, bool noIndex = false, bool withSuffixTrie = false, bool missingIndex = false, bool emptyIndex = false)
@@ -355,6 +360,9 @@ public Schema AddTextField(FieldName name, double weight = 1.0, bool sortable =
355360
/// </summary>
356361
/// <param name="name">The field's name.</param>
357362
/// <param name="system">The coordinate system to use.</param>
363+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
364+
/// Note the difference between a field with an empty value and a document with a missing value.
365+
/// By default, missing values are not indexed.</param>
358366
/// <returns>The <see cref="Schema"/> object.</returns>
359367
public Schema AddGeoShapeField(string name, CoordinateSystem system, bool missingIndex = false)
360368
{
@@ -367,6 +375,9 @@ public Schema AddGeoShapeField(string name, CoordinateSystem system, bool missin
367375
/// </summary>
368376
/// <param name="name">The field's name.</param>
369377
/// <param name="system">The coordinate system to use.</param>
378+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
379+
/// Note the difference between a field with an empty value and a document with a missing value.
380+
/// By default, missing values are not indexed.</param>
370381
/// <returns>The <see cref="Schema"/> object.</returns>
371382
public Schema AddGeoShapeField(FieldName name, CoordinateSystem system, bool missingIndex = false)
372383
{
@@ -380,6 +391,9 @@ public Schema AddGeoShapeField(FieldName name, CoordinateSystem system, bool mis
380391
/// <param name="name">The field's name.</param>
381392
/// <param name="sortable">If true, the text field can be sorted.</param>
382393
/// <param name="noIndex">Attributes can have the NOINDEX option, which means they will not be indexed.</param>
394+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
395+
/// Note the difference between a field with an empty value and a document with a missing value.
396+
/// By default, missing values are not indexed.</param>
383397
/// <returns>The <see cref="Schema"/> object.</returns>
384398
public Schema AddGeoField(FieldName name, bool sortable = false, bool noIndex = false, bool missingIndex = false)
385399
{
@@ -393,6 +407,9 @@ public Schema AddGeoField(FieldName name, bool sortable = false, bool noIndex =
393407
/// <param name="name">The field's name.</param>
394408
/// <param name="sortable">If true, the text field can be sorted.</param>
395409
/// <param name="noIndex">Attributes can have the NOINDEX option, which means they will not be indexed.</param>
410+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
411+
/// Note the difference between a field with an empty value and a document with a missing value.
412+
/// By default, missing values are not indexed.</param>
396413
/// <returns>The <see cref="Schema"/> object.</returns>
397414
public Schema AddGeoField(string name, bool sortable = false, bool noIndex = false, bool missingIndex = false)
398415
{
@@ -406,6 +423,9 @@ public Schema AddGeoField(string name, bool sortable = false, bool noIndex = fal
406423
/// <param name="name">The field's name.</param>
407424
/// <param name="sortable">If true, the text field can be sorted.</param>
408425
/// <param name="noIndex">Attributes can have the NOINDEX option, which means they will not be indexed.</param>
426+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
427+
/// Note the difference between a field with an empty value and a document with a missing value.
428+
/// By default, missing values are not indexed.</param>
409429
/// <returns>The <see cref="Schema"/> object.</returns>
410430
public Schema AddNumericField(FieldName name, bool sortable = false, bool noIndex = false, bool missingIndex = false)
411431
{
@@ -419,6 +439,9 @@ public Schema AddNumericField(FieldName name, bool sortable = false, bool noInde
419439
/// <param name="name">The field's name.</param>
420440
/// <param name="sortable">If true, the text field can be sorted.</param>
421441
/// <param name="noIndex">Attributes can have the NOINDEX option, which means they will not be indexed.</param>
442+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
443+
/// Note the difference between a field with an empty value and a document with a missing value.
444+
/// By default, missing values are not indexed.</param>
422445
/// <returns>The <see cref="Schema"/> object.</returns>
423446
public Schema AddNumericField(string name, bool sortable = false, bool noIndex = false, bool missingIndex = false)
424447
{
@@ -437,6 +460,10 @@ public Schema AddNumericField(string name, bool sortable = false, bool noIndex =
437460
/// <param name="caseSensitive">If true, Keeps the original letter cases of the tags.</param>
438461
/// Normalied form is the field sent to lower case with all diaretics removed</param>
439462
/// <param name="withSuffixTrie">Keeps a suffix trie with all terms which match the suffix.</param>
463+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
464+
/// Note the difference between a field with an empty value and a document with a missing value.
465+
/// By default, missing values are not indexed.</param>
466+
/// <param name="emptyIndex"> allows you to index and search for empty strings. By default, empty strings are not indexed.</param>
440467
/// <returns>The <see cref="Schema"/> object.</returns>
441468
public Schema AddTagField(FieldName name, bool sortable = false, bool unf = false,
442469
bool noIndex = false, string separator = ",",
@@ -457,6 +484,10 @@ public Schema AddTagField(FieldName name, bool sortable = false, bool unf = fals
457484
/// <param name="caseSensitive">If true, Keeps the original letter cases of the tags.</param>
458485
/// Normalied form is the field sent to lower case with all diaretics removed</param>
459486
/// <param name="withSuffixTrie">Keeps a suffix trie with all terms which match the suffix.</param>
487+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
488+
/// Note the difference between a field with an empty value and a document with a missing value.
489+
/// By default, missing values are not indexed.</param>
490+
/// <param name="emptyIndex"> allows you to index and search for empty strings. By default, empty strings are not indexed.</param>
460491
/// <returns>The <see cref="Schema"/> object.</returns>
461492
public Schema AddTagField(string name, bool sortable = false, bool unf = false,
462493
bool noIndex = false, string separator = ",",
@@ -471,7 +502,10 @@ public Schema AddTagField(string name, bool sortable = false, bool unf = false,
471502
/// </summary>
472503
/// <param name="name">The field's name.</param>
473504
/// <param name="algorithm">The vector similarity algorithm to use.</param>
474-
/// <param name="attribute">The algorithm attributes for the creation of the vector index.</param>
505+
/// <param name="attributes">The algorithm attributes for the creation of the vector index.</param>
506+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
507+
/// Note the difference between a field with an empty value and a document with a missing value.
508+
/// By default, missing values are not indexed.</param>
475509
/// <returns>The <see cref="Schema"/> object.</returns>
476510
public Schema AddVectorField(FieldName name, VectorAlgo algorithm, Dictionary<string, object>? attributes = null, bool missingIndex = false)
477511
{
@@ -484,7 +518,10 @@ public Schema AddVectorField(FieldName name, VectorAlgo algorithm, Dictionary<st
484518
/// </summary>
485519
/// <param name="name">The field's name.</param>
486520
/// <param name="algorithm">The vector similarity algorithm to use.</param>
487-
/// <param name="attribute">The algorithm attributes for the creation of the vector index.</param>
521+
/// <param name="attributes">The algorithm attributes for the creation of the vector index.</param>
522+
/// <param name="missingIndex"> search for missing values, that is, documents that do not contain a specific field.
523+
/// Note the difference between a field with an empty value and a document with a missing value.
524+
/// By default, missing values are not indexed.</param>
488525
/// <returns>The <see cref="Schema"/> object.</returns>
489526
public Schema AddVectorField(string name, VectorAlgo algorithm, Dictionary<string, object>? attributes = null, bool missingIndex = false)
490527
{

tests/NRedisStack.Tests/Search/IndexCreationTests.cs

+79
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class IndexCreationTests : AbstractNRedisStackTest, IDisposable
1111
private readonly string index = "MISSING_EMPTY_INDEX";
1212
private static readonly string INDEXMISSING = "INDEXMISSING";
1313
private static readonly string INDEXEMPTY = "INDEXEMPTY";
14+
private static readonly string SORTABLE = "SORTABLE";
1415

1516
public IndexCreationTests(RedisFixture redisFixture) : base(redisFixture) { }
1617

@@ -163,4 +164,82 @@ public void TestCreateFloat16VectorField()
163164
res = ft.Search("idx", q.AddParam("vec", vec2ToBytes));
164165
Assert.Equal(2, res.TotalResults);
165166
}
167+
168+
[Fact]
169+
public void TestMissingSortableFieldCommandArgs()
170+
{
171+
string idx = "MISSING_EMPTY_SORTABLE_INDEX";
172+
Schema sc = new Schema()
173+
.AddTextField("text1", 1.0, missingIndex: true, emptyIndex: true, sortable: true)
174+
.AddTagField("tag1", missingIndex: true, emptyIndex: true, sortable: true)
175+
.AddNumericField("numeric1", missingIndex: true, sortable: true)
176+
.AddGeoField("geo1", missingIndex: true, sortable: true);
177+
178+
var ftCreateParams = FTCreateParams.CreateParams();
179+
180+
var cmd = SearchCommandBuilder.Create(idx, ftCreateParams, sc);
181+
var expectedArgs = new object[] { idx, "SCHEMA",
182+
"text1","TEXT",INDEXMISSING,INDEXEMPTY,SORTABLE,
183+
"tag1","TAG", INDEXMISSING,INDEXEMPTY,SORTABLE,
184+
"numeric1","NUMERIC", INDEXMISSING,SORTABLE,
185+
"geo1","GEO", INDEXMISSING, SORTABLE};
186+
Assert.Equal(expectedArgs, cmd.Args);
187+
}
188+
189+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.3.240")]
190+
public void TestCombiningMissingEmptySortableFields()
191+
{
192+
string idx = "MISSING_EMPTY_SORTABLE_INDEX";
193+
IDatabase db = redisFixture.Redis.GetDatabase();
194+
var ft = db.FT(2);
195+
var vectorAttrs = new Dictionary<string, object>()
196+
{
197+
["TYPE"] = "FLOAT32",
198+
["DIM"] = "2",
199+
["DISTANCE_METRIC"] = "L2",
200+
};
201+
Schema sc = new Schema()
202+
.AddTextField("text1", 1.0, missingIndex: true, emptyIndex: true, sortable: true)
203+
.AddTagField("tag1", missingIndex: true, emptyIndex: true, sortable: true)
204+
.AddNumericField("numeric1", missingIndex: true, sortable: true)
205+
.AddGeoField("geo1", missingIndex: true, sortable: true)
206+
.AddGeoShapeField("geoshape1", Schema.GeoShapeField.CoordinateSystem.FLAT, missingIndex: true)
207+
.AddVectorField("vector1", Schema.VectorField.VectorAlgo.FLAT, vectorAttrs, missingIndex: true);
208+
209+
var ftCreateParams = FTCreateParams.CreateParams();
210+
Assert.True(ft.Create(idx, ftCreateParams, sc));
211+
212+
var sampleHash = new HashEntry[] { new("field1", "value1"), new("field2", "value2") };
213+
db.HashSet("hashWithMissingFields", sampleHash);
214+
215+
Polygon polygon = new GeometryFactory().CreatePolygon(new Coordinate[] { new Coordinate(1, 1), new Coordinate(10, 10), new Coordinate(100, 100), new Coordinate(1, 1), });
216+
217+
var hashWithAllFields = new HashEntry[] { new("text1", "value1"), new("tag1", "value2"), new("numeric1", "3.141"), new("geo1", "-0.441,51.458"), new("geoshape1", polygon.ToString()), new("vector1", "aaaaaaaa") };
218+
db.HashSet("hashWithAllFields", hashWithAllFields);
219+
220+
var result = ft.Search(idx, new Query("ismissing(@text1)"));
221+
Assert.Equal(1, result.TotalResults);
222+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
223+
224+
result = ft.Search(idx, new Query("ismissing(@tag1)"));
225+
Assert.Equal(1, result.TotalResults);
226+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
227+
228+
result = ft.Search(idx, new Query("ismissing(@numeric1)"));
229+
Assert.Equal(1, result.TotalResults);
230+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
231+
232+
result = ft.Search(idx, new Query("ismissing(@geo1)"));
233+
Assert.Equal(1, result.TotalResults);
234+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
235+
236+
result = ft.Search(idx, new Query("ismissing(@geoshape1)"));
237+
Assert.Equal(1, result.TotalResults);
238+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
239+
240+
result = ft.Search(idx, new Query("ismissing(@vector1)"));
241+
Assert.Equal(1, result.TotalResults);
242+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
243+
}
244+
166245
}

tests/NRedisStack.Tests/Search/SearchTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2129,8 +2129,8 @@ public void TestFieldsCommandBuilder()
21292129
"PHONETIC",
21302130
"dm:en",
21312131
"WITHSUFFIXTRIE",
2132-
"SORTABLE",
21332132
"UNF",
2133+
"SORTABLE",
21342134
"num",
21352135
"NUMERIC",
21362136
"NOINDEX",
@@ -2146,8 +2146,8 @@ public void TestFieldsCommandBuilder()
21462146
"SEPARATOR",
21472147
";",
21482148
"CASESENSITIVE",
2149-
"SORTABLE",
21502149
"UNF",
2150+
"SORTABLE",
21512151
"vec",
21522152
"VECTOR",
21532153
"FLAT",

0 commit comments

Comments
 (0)