Skip to content

Commit 2481084

Browse files
authored
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 76465cc commit 2481084

File tree

3 files changed

+129
-11
lines changed

3 files changed

+129
-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

+81
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public IndexCreationTests(EndpointsFixture endpointsFixture) : base(endpointsFix
1616

1717
private static readonly string INDEXMISSING = "INDEXMISSING";
1818
private static readonly string INDEXEMPTY = "INDEXEMPTY";
19+
private static readonly string SORTABLE = "SORTABLE";
1920

2021
[Fact]
2122
public void TestMissingEmptyFieldCommandArgs()
@@ -202,4 +203,84 @@ public void TestCreateInt8VectorField(string endpointId)
202203
res = ft.Search("idx", q.AddParam("vec", vec2));
203204
Assert.Equal(2, res.TotalResults);
204205
}
206+
207+
[Fact]
208+
public void TestMissingSortableFieldCommandArgs()
209+
{
210+
string idx = "MISSING_EMPTY_SORTABLE_INDEX";
211+
Schema sc = new Schema()
212+
.AddTextField("text1", 1.0, missingIndex: true, emptyIndex: true, sortable: true)
213+
.AddTagField("tag1", missingIndex: true, emptyIndex: true, sortable: true)
214+
.AddNumericField("numeric1", missingIndex: true, sortable: true)
215+
.AddGeoField("geo1", missingIndex: true, sortable: true);
216+
217+
var ftCreateParams = FTCreateParams.CreateParams();
218+
219+
var cmd = SearchCommandBuilder.Create(idx, ftCreateParams, sc);
220+
var expectedArgs = new object[] { idx, "SCHEMA",
221+
"text1","TEXT",INDEXMISSING,INDEXEMPTY,SORTABLE,
222+
"tag1","TAG", INDEXMISSING,INDEXEMPTY,SORTABLE,
223+
"numeric1","NUMERIC", INDEXMISSING,SORTABLE,
224+
"geo1","GEO", INDEXMISSING, SORTABLE};
225+
Assert.Equal(expectedArgs, cmd.Args);
226+
}
227+
228+
[SkipIfRedis(Comparison.LessThan, "7.3.240")]
229+
[MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))]
230+
public void TestCombiningMissingEmptySortableFields(string endpointId)
231+
{
232+
string idx = "MISSING_EMPTY_SORTABLE_INDEX";
233+
IDatabase db = GetCleanDatabase(endpointId);
234+
var ft = db.FT(2);
235+
var vectorAttrs = new Dictionary<string, object>()
236+
{
237+
["TYPE"] = "FLOAT32",
238+
["DIM"] = "2",
239+
["DISTANCE_METRIC"] = "L2",
240+
};
241+
Schema sc = new Schema()
242+
.AddTextField("text1", 1.0, missingIndex: true, emptyIndex: true, sortable: true)
243+
.AddTagField("tag1", missingIndex: true, emptyIndex: true, sortable: true)
244+
.AddNumericField("numeric1", missingIndex: true, sortable: true)
245+
.AddGeoField("geo1", missingIndex: true, sortable: true)
246+
.AddGeoShapeField("geoshape1", Schema.GeoShapeField.CoordinateSystem.FLAT, missingIndex: true)
247+
.AddVectorField("vector1", Schema.VectorField.VectorAlgo.FLAT, vectorAttrs, missingIndex: true);
248+
249+
var ftCreateParams = FTCreateParams.CreateParams();
250+
Assert.True(ft.Create(idx, ftCreateParams, sc));
251+
252+
var sampleHash = new HashEntry[] { new("field1", "value1"), new("field2", "value2") };
253+
db.HashSet("hashWithMissingFields", sampleHash);
254+
255+
Polygon polygon = new GeometryFactory().CreatePolygon(new Coordinate[] { new Coordinate(1, 1), new Coordinate(10, 10), new Coordinate(100, 100), new Coordinate(1, 1), });
256+
257+
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") };
258+
db.HashSet("hashWithAllFields", hashWithAllFields);
259+
260+
var result = ft.Search(idx, new Query("ismissing(@text1)"));
261+
Assert.Equal(1, result.TotalResults);
262+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
263+
264+
result = ft.Search(idx, new Query("ismissing(@tag1)"));
265+
Assert.Equal(1, result.TotalResults);
266+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
267+
268+
result = ft.Search(idx, new Query("ismissing(@numeric1)"));
269+
Assert.Equal(1, result.TotalResults);
270+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
271+
272+
result = ft.Search(idx, new Query("ismissing(@geo1)"));
273+
Assert.Equal(1, result.TotalResults);
274+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
275+
276+
result = ft.Search(idx, new Query("ismissing(@geoshape1)"));
277+
Assert.Equal(1, result.TotalResults);
278+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
279+
280+
result = ft.Search(idx, new Query("ismissing(@vector1)"));
281+
Assert.Equal(1, result.TotalResults);
282+
Assert.Equal("hashWithMissingFields", result.Documents[0].Id);
283+
}
284+
285+
205286
}

tests/NRedisStack.Tests/Search/SearchTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2140,8 +2140,8 @@ public void TestFieldsCommandBuilder()
21402140
"PHONETIC",
21412141
"dm:en",
21422142
"WITHSUFFIXTRIE",
2143-
"SORTABLE",
21442143
"UNF",
2144+
"SORTABLE",
21452145
"num",
21462146
"NUMERIC",
21472147
"NOINDEX",
@@ -2157,8 +2157,8 @@ public void TestFieldsCommandBuilder()
21572157
"SEPARATOR",
21582158
";",
21592159
"CASESENSITIVE",
2160-
"SORTABLE",
21612160
"UNF",
2161+
"SORTABLE",
21622162
"vec",
21632163
"VECTOR",
21642164
"FLAT",

0 commit comments

Comments
 (0)