1+ using System . Buffers ;
2+ using System . Diagnostics ;
13using NRedisStack . Search ;
24using NRedisStack . Search . Aggregation ;
5+ using StackExchange . Redis ;
36using Xunit ;
47using Xunit . Abstractions ;
58
@@ -12,7 +15,40 @@ public class HybridSearchUnitTests(ITestOutputHelper log)
1215 private ICollection < object > GetArgs ( HybridSearchQuery query , IReadOnlyDictionary < string , object > ? parameters = null )
1316 {
1417 Assert . Equal ( "FT.HYBRID" , query . Command ) ;
15- var args = query . GetArgs ( Index , parameters ) ;
18+ var args = query . GetArgs ( Index , parameters ) . ToArray ( ) ;
19+ byte [ ] buffer = ArrayPool < byte > . Shared . Rent ( 128 ) ;
20+ for ( int i = 0 ; i < args . Length ; i ++ )
21+ {
22+ var val = args [ i ] ;
23+ if ( val is RedisValue v )
24+ {
25+ // force non-readable values to an ASCII-printable string
26+ var bytes = v . GetByteCount ( ) ;
27+ if ( bytes > buffer . Length )
28+ {
29+ ArrayPool < byte > . Shared . Return ( buffer ) ;
30+ buffer = ArrayPool < byte > . Shared . Rent ( bytes ) ;
31+ }
32+
33+ var span = buffer . AsSpan ( 0 , v . CopyTo ( buffer ) ) ;
34+ Debug . Assert ( span . Length == bytes , $ "expected { bytes } , wrote { span . Length } ") ;
35+ var format = false ;
36+ foreach ( var b in span )
37+ {
38+ if ( b < 32 | b >= 127 )
39+ {
40+ format = true ;
41+ break ;
42+ }
43+ }
44+
45+ if ( format ) // not how resp-cli works, but: useful enough for unit tests
46+ {
47+ args [ i ] = Convert . ToBase64String ( buffer , 0 , bytes ) ;
48+ }
49+ }
50+ }
51+ ArrayPool < byte > . Shared . Return ( buffer ) ;
1652 log . WriteLine ( query . Command + " " + string . Join ( " " , args ) ) ;
1753 return args ;
1854 }
@@ -117,13 +153,14 @@ public void BasicSearch_WithBM25StdTanh()
117153 public void BasicVectorSearch ( )
118154 {
119155 HybridSearchQuery query = new ( ) ;
120- query . VectorSearch ( "vfield" , Array . Empty < float > ( ) ) ;
156+ byte [ ] blob = [ ] ;
157+ query . VectorSearch ( "vfield" , VectorData . Raw ( blob ) ) ;
121158
122159 object [ ] expected = [ Index , "VSIM" , "vfield" , "" ] ;
123160 Assert . Equivalent ( expected , GetArgs ( query ) ) ;
124161 }
125162
126- private static readonly ReadOnlyMemory < float > SomeRandomDataHere = new float [ ] { 1 , 2 , 3 , 4 } ;
163+ private static readonly VectorData < float > SomeRandomDataHere = VectorData . LeaseWithValues < float > ( 1 , 2 , 3 , 4 ) ;
127164
128165 private const string SomeRandomVectorValue = "AACAPwAAAEAAAEBAAACAQA==" ;
129166
@@ -622,15 +659,15 @@ public void ParameterizedQuery()
622659 IReadOnlyDictionary < string , object > args = new Dictionary < string , object >
623660 {
624661 { "s" , "abc" } ,
625- { "v" , VectorData . Create ( SomeRandomDataHere ) }
662+ { "v" , SomeRandomDataHere }
626663 } ;
627664 object [ ] expected = [ Index , "SEARCH" , "$s" , "VSIM" , "@field" , "$v" , "PARAMS" , 4 , "s" , "abc" , "v" , SomeRandomVectorValue ] ;
628665 var idx = Array . IndexOf ( expected , "abc" ) ;
629666 Assert . True ( idx >= 0 ) ;
630667 Assert . Equivalent ( expected , GetArgs ( query , args ) ) ;
631668
632669 // issue a second query against the same "query" instance, with different parameter values, this time from an object
633- args = Parameters . From ( new { s = "def" , v = VectorData . Create ( SomeRandomDataHere ) } ) ;
670+ args = Parameters . From ( new { s = "def" , v = SomeRandomDataHere } ) ;
634671
635672 expected [ idx ] = "def" ; // update our expectations
636673 expected [ idx + 2 ] = SomeRandomVectorValue ;
@@ -646,8 +683,9 @@ public void MakeMeOneWithEverything()
646683 [ "x" ] = 42 ,
647684 [ "y" ] = "abc"
648685 } ;
686+ using var vector = VectorData . LeaseWithValues < float > ( 1 , 2 , 3 ) ;
649687 query . Search ( new ( "foo" , Scorer . BM25StdTanh ( 5 ) , "text_score_alias" ) )
650- . VectorSearch ( new HybridSearchQuery . VectorSearchConfig ( "bar" , new float [ ] { 1 , 2 , 3 } ,
688+ . VectorSearch ( new HybridSearchQuery . VectorSearchConfig ( "bar" , vector ,
651689 VectorSearchMethod . NearestNeighbour ( 10 , 100 , "vector_distance_alias" ) )
652690 . WithFilter ( "@foo:bar" ) . WithScoreAlias ( "vector_score_alias" ) )
653691 . Combine ( HybridSearchQuery . Combiner . ReciprocalRankFusion ( 10 , 0.5 ) , "my_combined_alias" )
0 commit comments