Skip to content

Commit 59c6621

Browse files
author
tmgordeeva
authored
Min score for time series (#96878)
* Min score for time series Enables min score on time series aggregation.
1 parent 6d09290 commit 59c6621

File tree

5 files changed

+120
-0
lines changed

5 files changed

+120
-0
lines changed

docs/changelog/96878.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 96878
2+
summary: Min score for time series
3+
area: TSDB
4+
type: bug
5+
issues: []

modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/time_series.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,60 @@ setup:
148148
- match: { aggregations.ts.buckets.1.key: { "key": "baz" } }
149149
- match: { aggregations.ts.buckets.2.key: { "key": "foo" } }
150150

151+
---
152+
"Score test filter some":
153+
- skip:
154+
version: " - 8.8.99"
155+
reason: Time series min score fixed in 8.9
156+
157+
- do:
158+
search:
159+
index: tsdb
160+
body:
161+
query:
162+
function_score:
163+
field_value_factor:
164+
field: "val"
165+
factor: 0.1
166+
missing: 1
167+
size: 0
168+
min_score: 0.3
169+
aggs:
170+
ts:
171+
time_series:
172+
keyed: false
173+
174+
- match: { hits.total.value: 6 }
175+
- length: { aggregations: 1 }
176+
177+
- length: { aggregations.ts.buckets: 3 }
178+
179+
---
180+
"Score test filter all":
181+
- skip:
182+
version: " - 8.8.99"
183+
reason: Time series min score fixed in 8.9
184+
185+
- do:
186+
search:
187+
index: tsdb
188+
body:
189+
query:
190+
range:
191+
"@timestamp":
192+
gte: "2021-01-01T00:10:00Z"
193+
size: 0
194+
min_score: 100
195+
aggs:
196+
ts:
197+
time_series:
198+
keyed: false
199+
200+
- match: { hits.total.value: 0 }
201+
- length: { aggregations: 1 }
202+
- length: { aggregations.ts.buckets: 0 }
203+
204+
151205
---
152206
"Sampler aggregation with nested time series aggregation failure":
153207
- skip:

server/src/main/java/org/elasticsearch/search/aggregations/AggregationPhase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public static void preProcess(SearchContext context) {
4444
if (context.aggregations().factories().context() != null
4545
&& context.aggregations().factories().context().isInSortOrderExecutionRequired()) {
4646
TimeSeriesIndexSearcher searcher = new TimeSeriesIndexSearcher(context.searcher(), getCancellationChecks(context));
47+
searcher.setMinimumScore(context.minimumScore());
4748
searcher.setProfiler(context);
4849
try {
4950
searcher.search(context.rewrittenQuery(), bucketCollector);

server/src/main/java/org/elasticsearch/search/aggregations/support/TimeSeriesIndexSearcher.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.lucene.util.BytesRefBuilder;
2323
import org.apache.lucene.util.PriorityQueue;
2424
import org.elasticsearch.cluster.metadata.DataStream;
25+
import org.elasticsearch.common.lucene.search.function.MinScoreScorer;
2526
import org.elasticsearch.index.mapper.DataStreamTimestampFieldMapper;
2627
import org.elasticsearch.index.mapper.TimeSeriesIdFieldMapper;
2728
import org.elasticsearch.search.aggregations.AggregationExecutionContext;
@@ -53,6 +54,8 @@ public class TimeSeriesIndexSearcher {
5354
private final boolean tsidReverse;
5455
private final boolean timestampReverse;
5556

57+
private Float minimumScore = null;
58+
5659
public TimeSeriesIndexSearcher(IndexSearcher searcher, List<Runnable> cancellations) {
5760
try {
5861
this.searcher = new ContextIndexSearcher(
@@ -76,6 +79,10 @@ public TimeSeriesIndexSearcher(IndexSearcher searcher, List<Runnable> cancellati
7679
this.timestampReverse = TIME_SERIES_SORT[1].getOrder() == SortOrder.DESC;
7780
}
7881

82+
public void setMinimumScore(Float minimumScore) {
83+
this.minimumScore = minimumScore;
84+
}
85+
7986
public void search(Query query, BucketCollector bucketCollector) throws IOException {
8087
int seen = 0;
8188
query = searcher.rewrite(query);
@@ -90,6 +97,9 @@ public void search(Query query, BucketCollector bucketCollector) throws IOExcept
9097
}
9198
Scorer scorer = weight.scorer(leaf);
9299
if (scorer != null) {
100+
if (minimumScore != null) {
101+
scorer = new MinScoreScorer(weight, scorer, minimumScore);
102+
}
93103
LeafWalker leafWalker = new LeafWalker(leaf, scorer, bucketCollector, () -> tsidOrd[0]);
94104
if (leafWalker.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
95105
leafWalkers.add(leafWalker);
@@ -197,6 +207,9 @@ private static class LeafWalker {
197207
private final SortedDocValues tsids;
198208
private final SortedNumericDocValues timestamps; // TODO can we have this just a NumericDocValues?
199209
private final BytesRefBuilder scratch = new BytesRefBuilder();
210+
211+
private final Scorer scorer;
212+
200213
int docId = -1;
201214
int tsidOrd;
202215
long timestamp;
@@ -207,6 +220,7 @@ private static class LeafWalker {
207220
this.collector = bucketCollector.getLeafCollector(aggCtx);
208221
liveDocs = context.reader().getLiveDocs();
209222
this.collector.setScorer(scorer);
223+
this.scorer = scorer;
210224
iterator = scorer.iterator();
211225
tsids = DocValues.getSorted(context.reader(), TimeSeriesIdFieldMapper.NAME);
212226
timestamps = DocValues.getSortedNumeric(context.reader(), DataStream.TimestampField.FIXED_TIMESTAMP_FIELD);

server/src/test/java/org/elasticsearch/search/aggregations/support/TimeSeriesIndexSearcherTests.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.apache.lucene.index.IndexWriterConfig;
1919
import org.apache.lucene.index.NumericDocValues;
2020
import org.apache.lucene.index.SortedDocValues;
21+
import org.apache.lucene.search.BoostQuery;
2122
import org.apache.lucene.search.IndexSearcher;
2223
import org.apache.lucene.search.MatchAllDocsQuery;
2324
import org.apache.lucene.search.ScoreMode;
@@ -95,6 +96,51 @@ public void testCollectInOrderAcrossSegments() throws IOException, InterruptedEx
9596
dir.close();
9697
}
9798

99+
public void testCollectMinScoreAcrossSegments() throws IOException, InterruptedException {
100+
Directory dir = newDirectory();
101+
RandomIndexWriter iw = getIndexWriter(dir);
102+
103+
AtomicInteger clock = new AtomicInteger(0);
104+
105+
final int DOC_COUNTS = 5;
106+
Document doc = new Document();
107+
for (int j = 0; j < DOC_COUNTS; j++) {
108+
String tsid = "tsid" + j % 30;
109+
long time = clock.addAndGet(j % 10);
110+
doc.clear();
111+
doc.add(new SortedDocValuesField(TimeSeriesIdFieldMapper.NAME, new BytesRef(tsid)));
112+
doc.add(new NumericDocValuesField(DataStream.TimestampField.FIXED_TIMESTAMP_FIELD, time));
113+
try {
114+
iw.addDocument(doc);
115+
} catch (IOException e) {
116+
throw new UncheckedIOException(e);
117+
}
118+
}
119+
iw.close();
120+
121+
IndexReader reader = DirectoryReader.open(dir);
122+
IndexSearcher searcher = new IndexSearcher(reader);
123+
124+
TimeSeriesIndexSearcher indexSearcher = new TimeSeriesIndexSearcher(searcher, List.of());
125+
indexSearcher.setMinimumScore(2f);
126+
127+
{
128+
var collector = new TimeSeriesCancellationTests.CountingBucketCollector();
129+
var query = new BoostQuery(new MatchAllDocsQuery(), 2f);
130+
indexSearcher.search(query, collector);
131+
assertEquals(collector.count.get(), DOC_COUNTS);
132+
}
133+
{
134+
var collector = new TimeSeriesCancellationTests.CountingBucketCollector();
135+
var query = new BoostQuery(new MatchAllDocsQuery(), 1f);
136+
indexSearcher.search(query, collector);
137+
assertEquals(collector.count.get(), 0);
138+
}
139+
140+
reader.close();
141+
dir.close();
142+
}
143+
98144
/**
99145
* this test fixed the wrong init value of tsidOrd
100146
* See https://github.com/elastic/elasticsearch/issues/85711

0 commit comments

Comments
 (0)