22
33namespace StackExchange . Redis ;
44
5- internal sealed class VectorSetSimilaritySearchMessage (
5+ internal abstract class VectorSetSimilaritySearchMessage (
66 int db ,
77 CommandFlags flags ,
88 VectorSetSimilaritySearchMessage . VsimFlags vsimFlags ,
99 RedisKey key ,
10- RedisValue member ,
11- ReadOnlyMemory < float > vector ,
1210 int count ,
1311 double epsilon ,
1412 int searchExplorationFactor ,
1513 string ? filterExpression ,
1614 int maxFilteringEffort ) : Message ( db , flags , RedisCommand . VSIM )
1715{
18- public ResultProcessor < Lease < VectorSetSimilaritySearchResult > ? > GetResultProcessor ( ) => VectorSetSimilaritySearchProcessor . Instance ;
16+ // For "FP32" and "VALUES" scenarios; in the future we might want other vector sizes / encodings - for
17+ // example, there could be some "FP16" or "FP8" transport that requires a ROM-short or ROM-sbyte from
18+ // the calling code. Or, as a convenience, we might want to allow ROM-double input, but transcode that
19+ // to FP32 on the way out.
20+ internal sealed class VectorSetSimilaritySearchBySingleVectorMessage (
21+ int db ,
22+ CommandFlags flags ,
23+ VsimFlags vsimFlags ,
24+ RedisKey key ,
25+ ReadOnlyMemory < float > vector ,
26+ int count ,
27+ double epsilon ,
28+ int searchExplorationFactor ,
29+ string ? filterExpression ,
30+ int maxFilteringEffort ) : VectorSetSimilaritySearchMessage ( db , flags , vsimFlags , key , count , epsilon ,
31+ searchExplorationFactor , filterExpression , maxFilteringEffort )
32+ {
33+ internal override int GetSearchTargetArgCount ( bool packed ) =>
34+ packed ? 2 : 2 + vector . Length ; // FP32 {vector} or VALUES {num} {vector}
35+
36+ internal override void WriteSearchTarget ( bool packed , PhysicalConnection physical )
37+ {
38+ if ( packed )
39+ {
40+ physical . WriteBulkString ( "FP32"u8 ) ;
41+ physical . WriteBulkString ( System . Runtime . InteropServices . MemoryMarshal . AsBytes ( vector . Span ) ) ;
42+ }
43+ else
44+ {
45+ physical . WriteBulkString ( "VALUES"u8 ) ;
46+ physical . WriteBulkString ( vector . Length ) ;
47+ foreach ( var val in vector . Span )
48+ {
49+ physical . WriteBulkString ( val ) ;
50+ }
51+ }
52+ }
53+ }
54+
55+ // for "ELE" scenarios
56+ internal sealed class VectorSetSimilaritySearchByMemberMessage (
57+ int db ,
58+ CommandFlags flags ,
59+ VsimFlags vsimFlags ,
60+ RedisKey key ,
61+ RedisValue member ,
62+ int count ,
63+ double epsilon ,
64+ int searchExplorationFactor ,
65+ string ? filterExpression ,
66+ int maxFilteringEffort ) : VectorSetSimilaritySearchMessage ( db , flags , vsimFlags , key , count , epsilon ,
67+ searchExplorationFactor , filterExpression , maxFilteringEffort )
68+ {
69+ internal override int GetSearchTargetArgCount ( bool packed ) => 2 ; // ELE {member}
70+
71+ internal override void WriteSearchTarget ( bool packed , PhysicalConnection physical )
72+ {
73+ physical . WriteBulkString ( "ELE"u8 ) ;
74+ physical . WriteBulkString ( member ) ;
75+ }
76+ }
77+
78+ internal abstract int GetSearchTargetArgCount ( bool packed ) ;
79+ internal abstract void WriteSearchTarget ( bool packed , PhysicalConnection physical ) ;
80+
81+ public ResultProcessor < Lease < VectorSetSimilaritySearchResult > ? > GetResultProcessor ( ) =>
82+ VectorSetSimilaritySearchProcessor . Instance ;
83+
1984 private sealed class VectorSetSimilaritySearchProcessor : ResultProcessor < Lease < VectorSetSimilaritySearchResult > ? >
2085 {
2186 // keep local, since we need to know what flags were being sent
@@ -38,7 +103,8 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
38103 // in RESP3 mode (only), when both are requested, we get a sub-array per item; weird, but true
39104 bool internalNesting = withScores && withAttribs && connection . Protocol is RedisProtocol . Resp3 ;
40105
41- int rowsPerItem = internalNesting ? 2
106+ int rowsPerItem = internalNesting
107+ ? 2
42108 : 1 + ( ( withScores ? 1 : 0 ) + ( withAttribs ? 1 : 0 ) ) ; // each value is separate root element
43109
44110 var items = result . GetItems ( ) ;
@@ -80,11 +146,13 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
80146 target [ i ] = new VectorSetSimilaritySearchResult ( member , score , attributesJson ) ;
81147 count ++ ;
82148 }
149+
83150 if ( count == target . Length )
84151 {
85152 SetResult ( message , lease ) ;
86153 return true ;
87154 }
155+
88156 lease . Dispose ( ) ; // failed to fill?
89157 }
90158
@@ -111,14 +179,9 @@ internal enum VsimFlags
111179
112180 public override int ArgCount => GetArgCount ( VectorSetAddMessage . UseFp32 ) ;
113181
114- private int GetArgCount ( bool useFp32 )
182+ private int GetArgCount ( bool packed )
115183 {
116- int argCount = 3 ; // {key} and "ELE {member}", "FP32 {vector}" or "VALUES {num}"
117- if ( member . IsNull && ! useFp32 )
118- {
119- argCount += vector . Length ; // {vector} in the VALUES case
120- }
121-
184+ int argCount = 1 + GetSearchTargetArgCount ( packed ) ; // {key} and whatever we need for the vector/element portion
122185 if ( HasFlag ( VsimFlags . WithScores ) ) argCount ++ ; // [WITHSCORES]
123186 if ( HasFlag ( VsimFlags . WithAttributes ) ) argCount ++ ; // [WITHATTRIBS]
124187 if ( HasFlag ( VsimFlags . Count ) ) argCount += 2 ; // [COUNT {count}]
@@ -133,37 +196,15 @@ private int GetArgCount(bool useFp32)
133196
134197 protected override void WriteImpl ( PhysicalConnection physical )
135198 {
136- var useFp32 = VectorSetAddMessage . UseFp32 ; // avoid race in debug mode
137- physical . WriteHeader ( Command , GetArgCount ( useFp32 ) ) ;
199+ // snapshot to avoid race in debug scenarios
200+ bool packed = VectorSetAddMessage . UseFp32 ;
201+ physical . WriteHeader ( Command , GetArgCount ( packed ) ) ;
138202
139203 // Write key
140204 physical . Write ( key ) ;
141205
142206 // Write search target: either "ELE {member}" or vector data
143- if ( ! member . IsNull )
144- {
145- // Member-based search: "ELE {member}"
146- physical . WriteBulkString ( "ELE"u8 ) ;
147- physical . WriteBulkString ( member ) ;
148- }
149- else
150- {
151- // Vector-based search: either "FP32 {vector}" or "VALUES {num} {vector}"
152- if ( useFp32 )
153- {
154- physical . WriteBulkString ( "FP32"u8 ) ;
155- physical . WriteBulkString ( System . Runtime . InteropServices . MemoryMarshal . AsBytes ( vector . Span ) ) ;
156- }
157- else
158- {
159- physical . WriteBulkString ( "VALUES"u8 ) ;
160- physical . WriteBulkString ( vector . Length ) ;
161- foreach ( var val in vector . Span )
162- {
163- physical . WriteBulkString ( val ) ;
164- }
165- }
166- }
207+ WriteSearchTarget ( packed , physical ) ;
167208
168209 if ( HasFlag ( VsimFlags . WithScores ) )
169210 {
0 commit comments