Skip to content

Commit 88e9e36

Browse files
gkorlandNick Craverjorodamgravell
authored
add support for Index Definition (#1544)
* add support for Index Definition * remove nullable * keep binary compatibility add another constructor * Mark AddHash as deprecated * Add basic test * fix build * fix build * Update ClientTest.cs * Update RediSearchTestBase.cs * fix type enum ToString() * remove AddHash test, command is not supported in Search 2.0 * set tests to run in serial * revert * add AssemblyInfo.cs * fix according to code review * fix build * ForceReplication during failovers to reduce errors in clustered scenarios * NRediSearch: flush every test Fundamentally, JRediSearch is doing this, and bases test assumptions on it. To get back to a decent/matching state, we need to do the same. * ...and disable parallelization... * Match RediSearch 2.0 assumptions See https://github.com/RediSearch/JRediSearch/blob/f1d7e2ed64a18e99b31b3b7fe48bb972c00e3999/src/test/java/io/redisearch/client/ClientTest.java#L403-L408 * Split builds out * Woops * Fix bad merge * do not perform suggestion range validation if the value came from the server Co-authored-by: Nick Craver <[email protected]> Co-authored-by: Jordan Rodak <[email protected]> Co-authored-by: mgravell <[email protected]>
1 parent 8856862 commit 88e9e36

File tree

4 files changed

+127
-12
lines changed

4 files changed

+127
-12
lines changed

src/NRediSearch/Client.cs

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,104 @@ public enum IndexOptions
5050
KeepTermFrequencies = 16
5151
}
5252

53+
public sealed class IndexDefinition
54+
{
55+
public enum IndexType
56+
{
57+
/// <summary>
58+
/// Used to indicates that the index should follow the keys of type Hash changes
59+
/// </summary>
60+
Hash
61+
}
62+
63+
internal readonly IndexType _type = IndexType.Hash;
64+
internal readonly bool _async;
65+
internal readonly string[] _prefixes;
66+
internal readonly string _filter;
67+
internal readonly string _languageField;
68+
internal readonly string _language;
69+
internal readonly string _scoreFiled;
70+
internal readonly double _score;
71+
internal readonly string _payloadField;
72+
73+
public IndexDefinition(bool async = false, string[] prefixes = null,
74+
string filter = null, string languageField = null, string language = null,
75+
string scoreFiled = null, double score = 1.0, string payloadField = null)
76+
{
77+
_async = async;
78+
_prefixes = prefixes;
79+
_filter = filter;
80+
_languageField = languageField;
81+
_language = language;
82+
_scoreFiled = scoreFiled;
83+
_score = score;
84+
_payloadField = payloadField;
85+
}
86+
87+
internal void SerializeRedisArgs(List<object> args)
88+
{
89+
args.Add("ON".Literal());
90+
args.Add(_type.ToString("g"));
91+
if (_async)
92+
{
93+
args.Add("ASYNC".Literal());
94+
}
95+
if (_prefixes?.Length > 0)
96+
{
97+
args.Add("PREFIX".Literal());
98+
args.Add(_prefixes.Length.ToString());
99+
args.AddRange(_prefixes);
100+
}
101+
if (_filter != null)
102+
{
103+
args.Add("FILTER".Literal());
104+
args.Add(_filter);
105+
}
106+
if (_languageField != null) {
107+
args.Add("LANGUAGE_FIELD".Literal());
108+
args.Add(_languageField);
109+
}
110+
if (_language != null) {
111+
args.Add("LANGUAGE".Literal());
112+
args.Add(_language);
113+
}
114+
if (_scoreFiled != null) {
115+
args.Add("SCORE_FIELD".Literal());
116+
args.Add(_scoreFiled);
117+
}
118+
if (_score != 1.0) {
119+
args.Add("SCORE".Literal());
120+
args.Add(_score.ToString());
121+
}
122+
if (_payloadField != null) {
123+
args.Add("PAYLOAD_FIELD".Literal());
124+
args.Add(_payloadField);
125+
}
126+
}
127+
128+
}
129+
53130
public sealed class ConfiguredIndexOptions
54131
{
55132
// This news up a enum which results in the 0 equivalent.
56133
// It's not used in the library and I'm guessing this isn't intentional.
57134
public static IndexOptions Default => new IndexOptions();
58135

59136
private IndexOptions _options;
137+
private IndexDefinition _definition;
60138
private string[] _stopwords;
139+
61140
public ConfiguredIndexOptions(IndexOptions options = IndexOptions.Default)
62141
{
63142
_options = options;
64143
}
65144

145+
public ConfiguredIndexOptions(IndexDefinition definition, IndexOptions options = IndexOptions.Default)
146+
: this(options)
147+
{
148+
_definition = definition;
149+
}
150+
66151
/// <summary>
67152
/// Set a custom stopword list.
68153
/// </summary>
@@ -84,17 +169,18 @@ public ConfiguredIndexOptions SetNoStopwords()
84169

85170
internal void SerializeRedisArgs(List<object> args)
86171
{
87-
SerializeRedisArgs(_options, args);
88-
if (_stopwords != null && _stopwords.Length != 0)
172+
SerializeRedisArgs(_options, args, _definition);
173+
if (_stopwords?.Length > 0)
89174
{
90175
args.Add("STOPWORDS".Literal());
91176
args.Add(_stopwords.Length.Boxed());
92177
args.AddRange(_stopwords);
93178
}
94179
}
95180

96-
internal static void SerializeRedisArgs(IndexOptions options, List<object> args)
181+
internal static void SerializeRedisArgs(IndexOptions options, List<object> args, IndexDefinition definition)
97182
{
183+
definition?.SerializeRedisArgs(args);
98184
if ((options & IndexOptions.UseTermOffsets) == 0)
99185
{
100186
args.Add("NOOFFSETS".Literal());
@@ -453,20 +539,24 @@ public Task<bool> ReplaceDocumentAsync(string docId, Dictionary<string, RedisVal
453539

454540
/// <summary>
455541
/// Index a document already in redis as a HASH key.
542+
/// [Deprecated] Use IDatabase.HashSet instead.
456543
/// </summary>
457544
/// <param name="docId">the id of the document in redis. This must match an existing, unindexed HASH key</param>
458545
/// <param name="score">the document's index score, between 0 and 1</param>
459546
/// <param name="replace">if set, and the document already exists, we reindex and update it</param>
460547
/// <returns>true on success</returns>
548+
[Obsolete("Use IDatabase.HashSet instead.")]
461549
public bool AddHash(string docId, double score, bool replace) => AddHash((RedisKey)docId, score, replace);
462550

463551
/// <summary>
464552
/// Index a document already in redis as a HASH key.
553+
/// [Deprecated] Use IDatabase.HashSet instead.
465554
/// </summary>
466555
/// <param name="docId">the id of the document in redis. This must match an existing, unindexed HASH key</param>
467556
/// <param name="score">the document's index score, between 0 and 1</param>
468557
/// <param name="replace">if set, and the document already exists, we reindex and update it</param>
469558
/// <returns>true on success</returns>
559+
[Obsolete("Use IDatabase.HashSet instead.")]
470560
public bool AddHash(RedisKey docId, double score, bool replace)
471561
{
472562
var args = new List<object> { _boxedIndexName, docId, score };
@@ -479,20 +569,24 @@ public bool AddHash(RedisKey docId, double score, bool replace)
479569

480570
/// <summary>
481571
/// Index a document already in redis as a HASH key.
572+
/// [Deprecated] Use IDatabase.HashSet instead.
482573
/// </summary>
483574
/// <param name="docId">the id of the document in redis. This must match an existing, unindexed HASH key</param>
484575
/// <param name="score">the document's index score, between 0 and 1</param>
485576
/// <param name="replace">if set, and the document already exists, we reindex and update it</param>
486577
/// <returns>true on success</returns>
578+
[Obsolete("Use IDatabase.HashSet instead.")]
487579
public Task<bool> AddHashAsync(string docId, double score, bool replace) => AddHashAsync((RedisKey)docId, score, replace);
488580

489581
/// <summary>
490582
/// Index a document already in redis as a HASH key.
583+
/// [Deprecated] Use IDatabase.HashSet instead.
491584
/// </summary>
492585
/// <param name="docId">the id of the document in redis. This must match an existing, unindexed HASH key</param>
493586
/// <param name="score">the document's index score, between 0 and 1</param>
494587
/// <param name="replace">if set, and the document already exists, we reindex and update it</param>
495588
/// <returns>true on success</returns>
589+
[Obsolete("Use IDatabase.HashSet instead.")]
496590
public async Task<bool> AddHashAsync(RedisKey docId, double score, bool replace)
497591
{
498592
var args = new List<object> { _boxedIndexName, docId, score };
@@ -1250,7 +1344,7 @@ private static Suggestion[] GetSuggestionsNoOptions(RedisResult[] results)
12501344

12511345
for (var i = 0; i < results.Length; i++)
12521346
{
1253-
suggestions[i] = Suggestion.Builder.String((string)results[i]).Build();
1347+
suggestions[i] = Suggestion.Builder.String((string)results[i]).Build(true);
12541348
}
12551349

12561350
return suggestions;
@@ -1268,7 +1362,7 @@ private static Suggestion[] GetSuggestionsWithPayloadAndScores(RedisResult[] res
12681362
suggestion.Score((double)results[i - 2]);
12691363
suggestion.Payload((string)results[i - 1]);
12701364

1271-
suggestions[(i / 3) - 1] = suggestion.Build();
1365+
suggestions[(i / 3) - 1] = suggestion.Build(true);
12721366
}
12731367

12741368
return suggestions;
@@ -1285,7 +1379,7 @@ private static Suggestion[] GetSuggestionsWithPayload(RedisResult[] results)
12851379
suggestion.String((string)results[i - 2]);
12861380
suggestion.Payload((string)results[i - 1]);
12871381

1288-
suggestions[(i / 2) - 1] = suggestion.Build();
1382+
suggestions[(i / 2) - 1] = suggestion.Build(true);
12891383
}
12901384

12911385
return suggestions;
@@ -1302,7 +1396,7 @@ private static Suggestion[] GetSuggestionsWithScores(RedisResult[] results)
13021396
suggestion.String((string)results[i - 2]);
13031397
suggestion.Score((double)results[i - 1]);
13041398

1305-
suggestions[(i / 2) - 1] = suggestion.Build();
1399+
suggestions[(i / 2) - 1] = suggestion.Build(true);
13061400
}
13071401

13081402
return suggestions;

src/NRediSearch/Suggestion.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ public SuggestionBuilder Payload(string payload)
8989
return this;
9090
}
9191

92-
public Suggestion Build()
92+
public Suggestion Build() => Build(false);
93+
94+
internal Suggestion Build(bool fromServer)
9395
{
9496
bool isStringMissing = _string == null;
95-
bool isScoreOutOfRange = (_score < 0.0 || _score > 1.0);
97+
bool isScoreOutOfRange = !fromServer && (_score < 0.0 || _score > 1.0);
9698

9799
if (isStringMissing || isScoreOutOfRange)
98100
{
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using Xunit;
3+
4+
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)]
5+
6+
namespace NRediSearch.Test
7+
{
8+
9+
public class AssemblyInfo
10+
{
11+
public AssemblyInfo()
12+
{
13+
}
14+
}
15+
}

tests/NRediSearch.Test/ClientTests/ClientTest.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,19 +290,23 @@ public void TestSortQueryFlags()
290290
}
291291

292292
[Fact]
293-
public void TestAddHash()
293+
public void TestIndexDefinition()
294294
{
295295
Client cl = GetClient();
296-
297296
Schema sc = new Schema().AddTextField("title", 1.0);
298-
Assert.True(cl.CreateIndex(sc, new ConfiguredIndexOptions()));
297+
ConfiguredIndexOptions options = new ConfiguredIndexOptions(
298+
new IndexDefinition( prefixes: new string[]{cl.IndexName}));
299+
Assert.True(cl.CreateIndex(sc, options));
300+
299301
RedisKey hashKey = (string)cl.IndexName + ":foo";
300302
Db.KeyDelete(hashKey);
301303
Db.HashSet(hashKey, "title", "hello world");
302304

303305
try
304306
{
307+
#pragma warning disable 0618
305308
Assert.True(cl.AddHash(hashKey, 1, false));
309+
#pragma warning restore 0618
306310
}
307311
catch (RedisServerException e)
308312
{

0 commit comments

Comments
 (0)