44
55namespace NRedisStack . Search ;
66
7+ /// <summary>
8+ /// Represents a hybrid search (FT.HYBRID) operation. Note that <see cref="HybridSearchQuery"/> instances can be reused for
9+ /// common queries, by passing the search operands as named parameters.
10+ /// </summary>
711[ Experimental ( Experiments . Server_8_4 , UrlFormat = Experiments . UrlFormat ) ]
812public sealed partial class HybridSearchQuery
913{
14+ private bool _frozen ;
1015 private SearchConfig _search ;
1116 private VectorSearchConfig _vsim ;
1217
18+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
19+ private HybridSearchQuery ThrowIfFrozen ( ) // GetArgs freezes
20+ {
21+ if ( _frozen ) Throw ( ) ;
22+ return this ;
23+
24+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
25+ static void Throw ( ) => throw new InvalidOperationException (
26+ "By default, the query cannot be mutated after being issued (to allow safe parameterized reuse from concurrent callers). If you are using the query sequentially rather than concurrently, you can use " + nameof ( AllowModification ) + " to re-enable changes." ) ;
27+ }
1328 /// <summary>
1429 /// Specify the textual search portion of the query.
30+ /// For a parameterized query, a search like <c>"$key"</c> will search using the parameter named <c>key</c>.
1531 /// </summary>
1632 public HybridSearchQuery Search ( SearchConfig query )
1733 {
34+ ThrowIfFrozen ( ) ;
1835 _search = query ;
1936 return this ;
2037 }
@@ -30,6 +47,7 @@ public HybridSearchQuery VectorSearch(string fieldName, VectorData vectorData)
3047 /// </summary>
3148 public HybridSearchQuery VectorSearch ( VectorSearchConfig config )
3249 {
50+ ThrowIfFrozen ( ) ;
3351 _vsim = config ;
3452 return this ;
3553 }
@@ -42,6 +60,7 @@ public HybridSearchQuery VectorSearch(VectorSearchConfig config)
4260 /// </summary>
4361 public HybridSearchQuery Combine ( Combiner combiner , string ? scoreAlias = null )
4462 {
63+ ThrowIfFrozen ( ) ;
4564 _combiner = combiner ;
4665 _combineScoreAlias = scoreAlias ;
4766 return this ;
@@ -54,6 +73,7 @@ public HybridSearchQuery Combine(Combiner combiner, string? scoreAlias = null)
5473 /// </summary>
5574 public HybridSearchQuery ReturnFields ( params string [ ] fields ) // naming for consistency with SearchQuery
5675 {
76+ ThrowIfFrozen ( ) ;
5777 _loadFieldOrFields = NullIfEmpty ( fields ) ;
5878 return this ;
5979 }
@@ -63,6 +83,7 @@ public HybridSearchQuery Combine(Combiner combiner, string? scoreAlias = null)
6383 /// </summary>
6484 public HybridSearchQuery ReturnFields ( string field ) // naming for consistency with SearchQuery
6585 {
86+ ThrowIfFrozen ( ) ;
6687 _loadFieldOrFields = field ;
6788 return this ;
6889 }
@@ -75,6 +96,7 @@ public HybridSearchQuery Combine(Combiner combiner, string? scoreAlias = null)
7596 /// </summary>
7697 public HybridSearchQuery GroupBy ( string field )
7798 {
99+ ThrowIfFrozen ( ) ;
78100 _groupByFieldOrFields = field ;
79101 return this ;
80102 }
@@ -84,6 +106,7 @@ public HybridSearchQuery GroupBy(string field)
84106 /// </summary>
85107 public HybridSearchQuery GroupBy ( params string [ ] fields )
86108 {
109+ ThrowIfFrozen ( ) ;
87110 _groupByFieldOrFields = NullIfEmpty ( fields ) ;
88111 return this ;
89112 }
@@ -93,6 +116,7 @@ public HybridSearchQuery GroupBy(params string[] fields)
93116 /// </summary>
94117 public HybridSearchQuery Reduce ( Reducer reducer )
95118 {
119+ ThrowIfFrozen ( ) ;
96120 _reducerOrReducers = reducer ;
97121 return this ;
98122 }
@@ -102,6 +126,7 @@ public HybridSearchQuery Reduce(Reducer reducer)
102126 /// </summary>
103127 public HybridSearchQuery Reduce ( params Reducer [ ] reducers )
104128 {
129+ ThrowIfFrozen ( ) ;
105130 _reducerOrReducers = NullIfEmpty ( reducers ) ;
106131 return this ;
107132 }
@@ -116,6 +141,7 @@ public HybridSearchQuery Reduce(params Reducer[] reducers)
116141 [ OverloadResolutionPriority ( 1 ) ] // allow Apply(new("expr", "alias")) to resolve correctly
117142 public HybridSearchQuery Apply ( ApplyExpression applyExpression )
118143 {
144+ ThrowIfFrozen ( ) ;
119145 if ( applyExpression . Alias is null )
120146 {
121147 _applyExpressionOrExpressions = applyExpression . Expression ;
@@ -133,6 +159,7 @@ public HybridSearchQuery Apply(ApplyExpression applyExpression)
133159 /// </summary>
134160 public HybridSearchQuery Apply ( params ApplyExpression [ ] applyExpression )
135161 {
162+ ThrowIfFrozen ( ) ;
136163 _applyExpressionOrExpressions = NullIfEmpty ( applyExpression ) ;
137164 return this ;
138165 }
@@ -145,6 +172,7 @@ public HybridSearchQuery Apply(params ApplyExpression[] applyExpression)
145172 /// <remarks>The default sort order is by score, unless overridden or disabled.</remarks>
146173 public HybridSearchQuery SortBy ( params SortedField [ ] fields )
147174 {
175+ ThrowIfFrozen ( ) ;
148176 _sortByFieldOrFields = NullIfEmpty ( fields ) ;
149177 return this ;
150178 }
@@ -154,6 +182,7 @@ public HybridSearchQuery SortBy(params SortedField[] fields)
154182 /// </summary>
155183 public HybridSearchQuery NoSort ( )
156184 {
185+ ThrowIfFrozen ( ) ;
157186 _sortByFieldOrFields = s_NoSortSentinel ;
158187 return this ;
159188 }
@@ -165,6 +194,7 @@ public HybridSearchQuery NoSort()
165194 /// </summary>
166195 public HybridSearchQuery SortBy ( params string [ ] fields )
167196 {
197+ ThrowIfFrozen ( ) ;
168198 _sortByFieldOrFields = NullIfEmpty ( fields ) ;
169199 return this ;
170200 }
@@ -174,6 +204,7 @@ public HybridSearchQuery SortBy(params string[] fields)
174204 /// </summary>
175205 public HybridSearchQuery SortBy ( SortedField field )
176206 {
207+ ThrowIfFrozen ( ) ;
177208 _sortByFieldOrFields = field ;
178209 return this ;
179210 }
@@ -183,6 +214,7 @@ public HybridSearchQuery SortBy(SortedField field)
183214 /// </summary>
184215 public HybridSearchQuery SortBy ( string field )
185216 {
217+ ThrowIfFrozen ( ) ;
186218 _sortByFieldOrFields = field ;
187219 return this ;
188220 }
@@ -194,6 +226,7 @@ public HybridSearchQuery SortBy(string field)
194226 /// </summary>
195227 public HybridSearchQuery Filter ( string expression )
196228 {
229+ ThrowIfFrozen ( ) ;
197230 _filter = expression ;
198231 return this ;
199232 }
@@ -202,6 +235,7 @@ public HybridSearchQuery Filter(string expression)
202235
203236 public HybridSearchQuery Limit ( int offset , int count )
204237 {
238+ ThrowIfFrozen ( ) ;
205239 if ( offset < 0 ) throw new ArgumentOutOfRangeException ( nameof ( offset ) ) ;
206240 if ( count < 0 ) throw new ArgumentOutOfRangeException ( nameof ( count ) ) ;
207241 _pagingOffset = offset ;
@@ -216,6 +250,7 @@ public HybridSearchQuery Limit(int offset, int count)
216250 /// </summary>
217251 public HybridSearchQuery ExplainScore ( bool explainScore = true )
218252 {
253+ ThrowIfFrozen ( ) ;
219254 _explainScore = explainScore ;
220255 return this ;
221256 }
@@ -227,6 +262,7 @@ public HybridSearchQuery ExplainScore(bool explainScore = true)
227262 /// </summary>
228263 public HybridSearchQuery Timeout ( bool timeout = true )
229264 {
265+ ThrowIfFrozen ( ) ;
230266 _timeout = timeout ;
231267 return this ;
232268 }
@@ -239,20 +275,22 @@ public HybridSearchQuery Timeout(bool timeout = true)
239275 /// </summary>
240276 internal HybridSearchQuery WithCursor ( int count = 0 , TimeSpan maxIdle = default )
241277 {
278+ ThrowIfFrozen ( ) ;
242279 // not currently exposed, while I figure out the API
243280 _cursorCount = count ;
244281 _cursorMaxIdle = maxIdle ;
245282 return this ;
246283 }
247284
248- private IReadOnlyDictionary < string , object > ? _parameters ;
249-
250285 /// <summary>
251- /// Supply parameters for the query.
286+ /// By default, queries are frozen when issued, to allow safe re-use of prepared queries from different callers.
287+ /// If you instead want to make sequential use of a query in a <i>single</i> caller, you can use this method
288+ /// to re-enable modification after issuing each query.
252289 /// </summary>
253- public HybridSearchQuery Parameters ( IReadOnlyDictionary < string , object > parameters )
290+ /// <returns></returns>
291+ public HybridSearchQuery AllowModification ( )
254292 {
255- _parameters = parameters is { Count : 0 } ? null : parameters ; // ignore empty
293+ _frozen = false ;
256294 return this ;
257295 }
258296}
0 commit comments