Skip to content

Commit 51b315a

Browse files
authored
SOLR-17775: Speed up function queries in 'fl' param. (#3380)
By fetching the data in doc order (mechanical sympathy) up to a cached threshold
1 parent 995b7fa commit 51b315a

File tree

2 files changed

+59
-17
lines changed

2 files changed

+59
-17
lines changed

solr/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ Optimizations
251251

252252
* SOLR-17756: Parallelize index fingerprint computation across segments via a dedicated thread pool (Matthew Biscocho, Luke Kot-Zaniewski)
253253

254+
* SOLR-17775: Speed up function queries in 'fl' param. (Yura Korolov)
255+
254256
Bug Fixes
255257
---------------------
256258
* SOLR-17629: If SQLHandler failed to open the underlying stream (e.g. Solr returns an error; could be user/syntax problem),

solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
package org.apache.solr.response.transform;
1818

1919
import java.io.IOException;
20+
import java.util.Arrays;
2021
import java.util.List;
2122
import java.util.Map;
2223
import org.apache.lucene.index.LeafReaderContext;
2324
import org.apache.lucene.index.ReaderUtil;
25+
import org.apache.lucene.internal.hppc.IntObjectHashMap;
2426
import org.apache.lucene.queries.function.FunctionValues;
2527
import org.apache.lucene.queries.function.ValueSource;
2628
import org.apache.solr.common.SolrDocument;
@@ -38,14 +40,18 @@
3840
* @since solr 4.0
3941
*/
4042
public class ValueSourceAugmenter extends DocTransformer {
43+
private static final Object NULL_SENTINEL = new Object();
4144
public final String name;
4245
public final QParser qparser;
4346
public final ValueSource valueSource;
47+
private final int maxPrefetchSize;
4448

4549
public ValueSourceAugmenter(String name, QParser qparser, ValueSource valueSource) {
4650
this.name = name;
4751
this.qparser = qparser;
4852
this.valueSource = valueSource;
53+
String maxPrefetchSizeRaw = qparser.getParam("preFetchDocs");
54+
this.maxPrefetchSize = maxPrefetchSizeRaw != null ? Integer.parseInt(maxPrefetchSizeRaw) : 1000;
4955
}
5056

5157
@Override
@@ -61,32 +67,66 @@ public void setContext(ResultContext context) {
6167
readerContexts = searcher.getIndexReader().leaves();
6268
fcontext = ValueSource.newContext(searcher);
6369
this.valueSource.createWeight(fcontext, searcher);
70+
final var docList = context.getDocList();
71+
if (docList == null) {
72+
return;
73+
}
74+
75+
final int prefetchSize = Math.min(docList.size(), maxPrefetchSize);
76+
final int[] ids = new int[prefetchSize];
77+
int i = 0;
78+
var iter = docList.iterator();
79+
while (iter.hasNext() && i < prefetchSize) {
80+
ids[i++] = iter.nextDoc();
81+
}
82+
Arrays.sort(ids);
83+
cachedValuesById = new IntObjectHashMap<>(ids.length);
84+
85+
FunctionValues values = null;
86+
int docBase = -1;
87+
int currentIdx = -1;
88+
for (int docid : ids) {
89+
int idx = ReaderUtil.subIndex(docid, readerContexts);
90+
if (currentIdx != idx) {
91+
currentIdx = idx;
92+
LeafReaderContext rcontext = readerContexts.get(idx);
93+
docBase = rcontext.docBase;
94+
values = valueSource.getValues(fcontext, rcontext);
95+
}
96+
int localId = docid - docBase;
97+
var value = values.objectVal(localId);
98+
cachedValuesById.put(docid, value != null ? value : NULL_SENTINEL);
99+
}
64100
} catch (IOException e) {
65-
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
101+
throw new SolrException(
102+
SolrException.ErrorCode.SERVER_ERROR, "exception for valuesource " + valueSource, e);
66103
}
67104
}
68105

69106
Map<Object, Object> fcontext;
70107
SolrIndexSearcher searcher;
71108
List<LeafReaderContext> readerContexts;
109+
IntObjectHashMap<Object> cachedValuesById;
72110

73111
@Override
74-
public void transform(SolrDocument doc, int docid, DocIterationInfo docInfo) {
75-
// This is only good for random-access functions
76-
77-
try {
78-
79-
// TODO: calculate this stuff just once across diff functions
80-
int idx = ReaderUtil.subIndex(docid, readerContexts);
81-
LeafReaderContext rcontext = readerContexts.get(idx);
82-
FunctionValues values = valueSource.getValues(fcontext, rcontext);
83-
int localId = docid - rcontext.docBase;
84-
setValue(doc, values.objectVal(localId));
85-
} catch (IOException e) {
86-
throw new SolrException(
87-
SolrException.ErrorCode.SERVER_ERROR,
88-
"exception at docid " + docid + " for valuesource " + valueSource,
89-
e);
112+
public void transform(SolrDocument doc, int docid, DocIterationInfo docIterationInfo) {
113+
Object cacheValue = (cachedValuesById != null) ? cachedValuesById.get(docid) : null;
114+
if (cacheValue != null) {
115+
setValue(doc, cacheValue != NULL_SENTINEL ? cacheValue : null);
116+
} else {
117+
// Fallback to on-demand calculation for documents not in the pre-calculated set, RTG use case
118+
try {
119+
int idx = ReaderUtil.subIndex(docid, readerContexts);
120+
LeafReaderContext rcontext = readerContexts.get(idx);
121+
FunctionValues values = valueSource.getValues(fcontext, rcontext);
122+
int localId = docid - rcontext.docBase;
123+
setValue(doc, values.objectVal(localId));
124+
} catch (IOException e) {
125+
throw new SolrException(
126+
SolrException.ErrorCode.SERVER_ERROR,
127+
"exception at docid " + docid + " for valuesource " + valueSource,
128+
e);
129+
}
90130
}
91131
}
92132

0 commit comments

Comments
 (0)