@@ -20,29 +20,88 @@ public static class RedisIndex
20
20
public static bool IndexDefinitionEquals ( this RedisIndexInfo redisIndexInfo , Type type )
21
21
{
22
22
var serialisedDefinition = SerializeIndex ( type ) ;
23
- var existingSet = redisIndexInfo . Attributes ? . Select ( a => ( Property : a . Attribute ! , a . Type ! ) ) . OrderBy ( a => a . Property ) ;
24
23
var isJson = redisIndexInfo . IndexDefinition ? . Identifier == "JSON" ;
25
24
25
+ var currentOffset = 0 ;
26
26
if ( serialisedDefinition . Length < 5 )
27
27
{
28
28
throw new ArgumentException ( $ "Could not parse the index definition for type: { type . Name } .") ;
29
29
}
30
30
31
- if ( redisIndexInfo . IndexName != serialisedDefinition [ 0 ] )
31
+ if ( redisIndexInfo . IndexDefinition is null )
32
32
{
33
33
return false ;
34
34
}
35
35
36
- if ( redisIndexInfo . IndexDefinition ? . Identifier ? . Equals ( serialisedDefinition [ 2 ] , StringComparison . OrdinalIgnoreCase ) == false )
36
+ // these are properties we cannot process because FT.INFO does not respond with them
37
+ var unprocessableProperties = new string [ ] { "EPSILON" , "EF_RUNTIME" , "PHONETIC" , "STOPWORDS" } ;
38
+
39
+ foreach ( var property in unprocessableProperties )
40
+ {
41
+ if ( serialisedDefinition . Any ( x => x . Equals ( property ) ) )
42
+ {
43
+ throw new ArgumentException ( $ "Could not validate index definition that contains { property } ") ;
44
+ }
45
+ }
46
+
47
+ if ( redisIndexInfo . IndexName != serialisedDefinition [ currentOffset ] )
48
+ {
49
+ return false ;
50
+ }
51
+
52
+ currentOffset += 2 ; // skip to the index type at index 2
53
+
54
+ if ( redisIndexInfo . IndexDefinition ? . Identifier ? . Equals ( serialisedDefinition [ currentOffset ] , StringComparison . OrdinalIgnoreCase ) == false )
55
+ {
56
+ return false ;
57
+ }
58
+
59
+ currentOffset += 2 ; // skip to prefix count
60
+
61
+ if ( ! int . TryParse ( serialisedDefinition [ currentOffset ] , out var numPrefixes ) )
62
+ {
63
+ throw new ArgumentException ( "Could not parse index with unknown number of prefixes" ) ;
64
+ }
65
+
66
+ currentOffset += 2 ; // skip to first prefix
67
+
68
+ if ( redisIndexInfo . IndexDefinition ? . Prefixes is null || redisIndexInfo . IndexDefinition . Prefixes . Length != numPrefixes || serialisedDefinition . Skip ( currentOffset ) . Take ( numPrefixes ) . SequenceEqual ( redisIndexInfo . IndexDefinition . Prefixes ) )
69
+ {
70
+ return false ;
71
+ }
72
+
73
+ currentOffset += numPrefixes ;
74
+
75
+ if ( redisIndexInfo . IndexDefinition ? . Filter is not null && ! redisIndexInfo . IndexDefinition . Filter . Equals ( serialisedDefinition . ElementAt ( currentOffset ) ) )
76
+ {
77
+ return false ;
78
+ }
79
+
80
+ if ( redisIndexInfo . IndexDefinition ? . Filter is not null )
81
+ {
82
+ currentOffset += 2 ;
83
+ }
84
+
85
+ if ( redisIndexInfo . IndexDefinition ? . DefaultLanguage is not null && ! redisIndexInfo . IndexDefinition . DefaultLanguage . Equals ( serialisedDefinition . ElementAt ( currentOffset ) ) )
37
86
{
38
87
return false ;
39
88
}
40
89
41
- if ( redisIndexInfo . IndexDefinition ? . Prefixes . FirstOrDefault ( ) . Equals ( serialisedDefinition [ 5 ] ) == false )
90
+ if ( redisIndexInfo . IndexDefinition ? . DefaultLanguage is not null )
91
+ {
92
+ currentOffset += 2 ;
93
+ }
94
+
95
+ if ( redisIndexInfo . IndexDefinition ? . LanguageField is not null && ! redisIndexInfo . IndexDefinition . LanguageField . Equals ( serialisedDefinition . ElementAt ( currentOffset ) ) )
42
96
{
43
97
return false ;
44
98
}
45
99
100
+ if ( redisIndexInfo . IndexDefinition ? . LanguageField is not null )
101
+ {
102
+ currentOffset += 2 ;
103
+ }
104
+
46
105
var target = redisIndexInfo . Attributes ? . SelectMany ( a =>
47
106
{
48
107
var attr = new List < string > ( ) ;
@@ -58,11 +117,81 @@ public static bool IndexDefinitionEquals(this RedisIndexInfo redisIndexInfo, Typ
58
117
attr . Add ( "AS" ) ;
59
118
}
60
119
120
+ if ( ! isJson && a . Type is not null && a . Type == "VECTOR" )
121
+ {
122
+ attr . Add ( $ "{ a . Attribute ! } .Vector") ;
123
+ attr . Add ( "AS" ) ;
124
+ }
125
+
61
126
attr . Add ( a . Attribute ! ) ;
62
127
63
128
if ( a . Type != null )
64
129
{
65
130
attr . Add ( a . Type ) ;
131
+ if ( a . Type == "TAG" )
132
+ {
133
+ attr . Add ( "SEPARATOR" ) ;
134
+ attr . Add ( a . Separator ?? "|" ) ;
135
+ }
136
+
137
+ if ( a . Type == "TEXT" )
138
+ {
139
+ if ( a . NoStem == true )
140
+ {
141
+ attr . Add ( "NOSTEM" ) ;
142
+ }
143
+
144
+ if ( a . Weight is not null && a . Weight != "1" )
145
+ {
146
+ attr . Add ( "WEIGHT" ) ;
147
+ attr . Add ( a . Weight ) ;
148
+ }
149
+ }
150
+
151
+ if ( a . Type == "VECTOR" )
152
+ {
153
+ if ( a . Algorithm is null )
154
+ {
155
+ throw new InvalidOperationException ( "Encountered Vector field with no algorithm" ) ;
156
+ }
157
+
158
+ attr . Add ( a . Algorithm ) ;
159
+ if ( a . VectorType is null )
160
+ {
161
+ throw new InvalidOperationException ( "Encountered vector field with no Vector Type" ) ;
162
+ }
163
+
164
+ attr . Add ( NumVectorArgs ( a ) . ToString ( ) ) ;
165
+
166
+ attr . Add ( "TYPE" ) ;
167
+ attr . Add ( a . VectorType ) ;
168
+
169
+ if ( a . Dimension is null )
170
+ {
171
+ throw new InvalidOperationException ( "Encountered vector field with no dimension" ) ;
172
+ }
173
+
174
+ attr . Add ( "DIM" ) ;
175
+ attr . Add ( a . Dimension ) ;
176
+
177
+ if ( a . DistanceMetric is not null )
178
+ {
179
+ attr . Add ( "DISTANCE_METRIC" ) ;
180
+ attr . Add ( a . DistanceMetric ) ;
181
+ }
182
+
183
+ if ( a . M is not null )
184
+ {
185
+ attr . Add ( "M" ) ;
186
+ attr . Add ( a . M ) ;
187
+ }
188
+
189
+ if ( a . EfConstruction is not null )
190
+ {
191
+ attr . Add ( "EF_CONSTRUCTION" ) ;
192
+ attr . Add ( a . EfConstruction ) ;
193
+ }
194
+ }
66
195
}
67
196
68
197
if ( a . Sortable == true )
@@ -73,7 +202,21 @@ public static bool IndexDefinitionEquals(this RedisIndexInfo redisIndexInfo, Typ
73
202
return attr . ToArray ( ) ;
74
203
} ) ;
75
204
76
- return target . SequenceEqual ( serialisedDefinition . Skip ( 7 ) ) ;
205
+ return target . SequenceEqual ( serialisedDefinition . Skip ( currentOffset ) ) ;
206
+ }
207
+
208
+ /// <summary>
209
+ /// calculates the number of arguments that would be required based to reverse engineer the index based off what
210
+ /// is in the Info attribute.
211
+ /// </summary>
212
+ /// <param name="attr">The attribute.</param>
213
+ /// <returns>The number of arguments.</returns>
214
+ internal static int NumVectorArgs ( this RedisIndexInfo . RedisIndexInfoAttribute attr )
215
+ {
216
+ var numArgs = 6 ;
217
+ numArgs += attr . M is not null ? 2 : 0 ;
218
+ numArgs += attr . EfConstruction is not null ? 2 : 0 ;
219
+ return numArgs ;
77
220
}
78
221
79
222
/// <summary>
0 commit comments