65
65
import org .apache .cassandra .schema .TableMetadata ;
66
66
import org .apache .cassandra .tracing .Tracing ;
67
67
import org .apache .cassandra .utils .InsertionOrderedNavigableSet ;
68
- import org .apache .cassandra .utils .Pair ;
69
68
import org .apache .cassandra .utils .Throwables ;
70
69
71
70
import static org .apache .cassandra .config .CassandraRelevantProperties .SAI_VECTOR_SEARCH_ORDER_CHUNK_SIZE ;
@@ -178,12 +177,12 @@ public UnfilteredRowIterator queryStorage(List<PrimaryKey> keys, ReadExecutionCo
178
177
* This is achieved by creating an on-disk view of the query that maps the expressions to
179
178
* the {@link SSTableIndex}s that will satisfy the expression.
180
179
* <p>
181
- * Each (expression, SSTable indexes) pair is then passed to
182
- * {@link IndexSearchResultIterator#build(Expression, Collection , AbstractBounds, QueryContext, boolean, Runnable)}
183
- * to search the in-memory index associated with the expression and the SSTable indexes, the results of
180
+ * Each {@link QueryViewBuilder.QueryExpressionView} is then passed to
181
+ * {@link IndexSearchResultIterator#build(QueryViewBuilder.QueryExpressionView , AbstractBounds, QueryContext, boolean, Runnable)}
182
+ * to search the in-memory indexes associated with the expression and the SSTable indexes, the results of
184
183
* which are unioned and returned.
185
184
* <p>
186
- * The results from each call to {@link IndexSearchResultIterator#build(Expression, Collection , AbstractBounds, QueryContext, boolean, Runnable)}
185
+ * The results from each call to {@link IndexSearchResultIterator#build(QueryViewBuilder.QueryExpressionView , AbstractBounds, QueryContext, boolean, Runnable)}
187
186
* are added to a {@link KeyRangeIntersectionIterator} and returned if strict filtering is allowed.
188
187
* <p>
189
188
* If strict filtering is not allowed, indexes are split into two groups according to the repaired status of their
@@ -214,30 +213,31 @@ public KeyRangeIterator.Builder getIndexQueryResults(Collection<Expression> expr
214
213
// If strict filtering is enabled, evaluate indexes for both repaired and un-repaired SSTables together.
215
214
// This usually means we are making this local index query in the context of a user query that reads
216
215
// from a single replica and thus can safely perform local intersections.
217
- for (Pair < Expression , Collection < SSTableIndex >> queryViewPair : queryView .view )
218
- builder .add (IndexSearchResultIterator .build (queryViewPair . left , queryViewPair . right , mergeRange , queryContext , true , () -> {}));
216
+ for (QueryViewBuilder . QueryExpressionView queryExpressionView : queryView .view )
217
+ builder .add (IndexSearchResultIterator .build (queryExpressionView , mergeRange , queryContext , true , () -> {}));
219
218
}
220
219
else
221
220
{
222
221
KeyRangeIterator .Builder repairedBuilder = KeyRangeIntersectionIterator .builder (expressions .size (), () -> {});
223
222
224
- for (Pair < Expression , Collection < SSTableIndex >> queryViewPair : queryView .view )
223
+ for (QueryViewBuilder . QueryExpressionView queryExpressionView : queryView .view )
225
224
{
225
+ Expression expression = queryExpressionView .expression ;
226
226
// The initial sizes here reflect little more than an effort to avoid resizing for
227
227
// partition-restricted searches w/ LCS:
228
228
List <SSTableIndex > repaired = new ArrayList <>(5 );
229
229
List <SSTableIndex > unrepaired = new ArrayList <>(5 );
230
230
231
231
// Split SSTable indexes into repaired and un-reparired:
232
- for (SSTableIndex index : queryViewPair . right )
232
+ for (SSTableIndex index : queryExpressionView . sstableIndexes )
233
233
if (index .getSSTable ().isRepaired ())
234
234
repaired .add (index );
235
235
else
236
236
unrepaired .add (index );
237
237
238
238
// Always build an iterator for the un-repaired set, given this must include Memtable indexes...
239
239
IndexSearchResultIterator unrepairedIterator =
240
- IndexSearchResultIterator .build (queryViewPair . left , unrepaired , mergeRange , queryContext , true , () -> {});
240
+ IndexSearchResultIterator .build (expression , queryExpressionView . memtableIndexes , unrepaired , mergeRange , queryContext , true , () -> {});
241
241
242
242
// ...but ignore it if our combined results are empty.
243
243
if (unrepairedIterator .getMaxKeys () > 0 )
@@ -253,7 +253,7 @@ public KeyRangeIterator.Builder getIndexQueryResults(Collection<Expression> expr
253
253
254
254
// ...then only add an iterator to the repaired intersection if repaired SSTable indexes exist.
255
255
if (!repaired .isEmpty ())
256
- repairedBuilder .add (IndexSearchResultIterator .build (queryViewPair . left , repaired , mergeRange , queryContext , false , () -> {}));
256
+ repairedBuilder .add (IndexSearchResultIterator .build (expression , Collections . emptyList () , repaired , mergeRange , queryContext , false , () -> {}));
257
257
}
258
258
259
259
if (repairedBuilder .rangeCount () > 0 )
@@ -274,8 +274,8 @@ private void maybeTriggerGuardrails(QueryViewBuilder.QueryView queryView)
274
274
int referencedIndexes = 0 ;
275
275
276
276
// We want to make sure that no individual column expression touches too many SSTable-attached indexes:
277
- for (Pair < Expression , Collection < SSTableIndex >> expressionSSTables : queryView .view )
278
- referencedIndexes = Math .max (referencedIndexes , expressionSSTables .right .size ());
277
+ for (QueryViewBuilder . QueryExpressionView expressionSSTables : queryView .view )
278
+ referencedIndexes = Math .max (referencedIndexes , expressionSSTables .sstableIndexes .size ());
279
279
280
280
if (Guardrails .saiSSTableIndexesPerQuery .failsOn (referencedIndexes , null ))
281
281
{
@@ -319,14 +319,19 @@ public KeyRangeIterator getTopKRows(RowFilter.Expression expression)
319
319
StorageAttachedIndex index = indexFor (expression );
320
320
assert index != null ;
321
321
Expression planExpression = Expression .create (index ).add (Operator .ANN , expression .getIndexValue ().duplicate ());
322
- // search memtable before referencing sstable indexes; otherwise we may miss newly flushed memtable index
323
- KeyRangeIterator memtableResults = index .memtableIndexManager ().searchMemtableIndexes (queryContext , planExpression , mergeRange );
324
322
325
323
QueryViewBuilder .QueryView queryView = new QueryViewBuilder (Collections .singleton (planExpression ), mergeRange ).build ();
326
324
Runnable onClose = () -> queryView .referencedIndexes .forEach (SSTableIndex ::releaseQuietly );
327
325
328
326
try
329
327
{
328
+ List <KeyRangeIterator > memtableResults = queryView .view
329
+ .stream ()
330
+ .map (v -> v .memtableIndexes )
331
+ .flatMap (Collection ::stream )
332
+ .map (idx -> idx .search (queryContext , planExpression , mergeRange ))
333
+ .collect (Collectors .toList ());
334
+
330
335
List <KeyRangeIterator > sstableIntersections = queryView .view
331
336
.stream ()
332
337
.map (this ::createRowIdIterator )
@@ -360,16 +365,21 @@ private KeyRangeIterator getTopKRows(List<PrimaryKey> rawSourceKeys, RowFilter.E
360
365
Expression planExpression = Expression .create (index );
361
366
planExpression .add (Operator .ANN , expression .getIndexValue ().duplicate ());
362
367
363
- // search memtable before referencing sstable indexes; otherwise we may miss newly flushed memtable index
364
- KeyRangeIterator memtableResults = index .memtableIndexManager ().limitToTopResults (queryContext , sourceKeys , planExpression );
365
368
QueryViewBuilder .QueryView queryView = new QueryViewBuilder (Collections .singleton (planExpression ), mergeRange ).build ();
366
369
Runnable onClose = () -> queryView .referencedIndexes .forEach (SSTableIndex ::releaseQuietly );
367
370
368
371
try
369
372
{
373
+ List <KeyRangeIterator > memtableResults = queryView .view
374
+ .stream ()
375
+ .map (v -> v .memtableIndexes )
376
+ .flatMap (Collection ::stream )
377
+ .map (idx -> idx .limitToTopResults (sourceKeys , planExpression , vectorQueryContext .limit ()))
378
+ .collect (Collectors .toList ());
379
+
370
380
List <KeyRangeIterator > sstableIntersections = queryView .view
371
381
.stream ()
372
- .flatMap (pair -> pair .right .stream ())
382
+ .flatMap (pair -> pair .sstableIndexes .stream ())
373
383
.map (idx -> {
374
384
try
375
385
{
@@ -395,15 +405,15 @@ private KeyRangeIterator getTopKRows(List<PrimaryKey> rawSourceKeys, RowFilter.E
395
405
/**
396
406
* Create row id iterator from different indexes' on-disk searcher of the same sstable
397
407
*/
398
- private KeyRangeIterator createRowIdIterator (Pair < Expression , Collection < SSTableIndex >> indexExpression )
408
+ private KeyRangeIterator createRowIdIterator (QueryViewBuilder . QueryExpressionView indexExpression )
399
409
{
400
- List <KeyRangeIterator > subIterators = indexExpression .right
410
+ List <KeyRangeIterator > subIterators = indexExpression .sstableIndexes
401
411
.stream ()
402
412
.map (index ->
403
413
{
404
414
try
405
415
{
406
- List <KeyRangeIterator > iterators = index .search (indexExpression .left , mergeRange , queryContext );
416
+ List <KeyRangeIterator > iterators = index .search (indexExpression .expression , mergeRange , queryContext );
407
417
// concat the result from multiple segments for the same index
408
418
return KeyRangeConcatIterator .builder (iterators .size ()).add (iterators ).build ();
409
419
}
0 commit comments