Skip to content

Commit 9e6fcf6

Browse files
Fix timestamp range query optimization for indices with doc values skipper (#123930)
When running a timestamp range query, as an optimization we check if the query range overlaps with the total range of values within a shard before executing the query on that shard. That way, if the range is disjoint, we can skip execution for that shard. To get the range of values within a shard, we usually use the PointValues index on the shard. However, when the doc values skipper is enabled, the point values are not (as the reason for the skipper is to reduce storage overhead by removing the point values index). In this case, we need to instead get the range of values within the shard by using the skipper. This patch implements that logic. Follow-up to #123191
1 parent 1637303 commit 9e6fcf6

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

server/src/internalClusterTest/java/org/elasticsearch/index/shard/SearchIdleIT.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,15 @@ public void testSearchIdleStats() throws InterruptedException {
235235
}
236236

237237
public void testSearchIdleBoolQueryMatchOneIndex() throws InterruptedException {
238+
checkSearchIdleBoolQueryMatchOneIndex(IndexSettings.DOC_VALUES_SKIPPER.isEnabled());
239+
}
240+
241+
public void testSearchIdleBoolQueryMatchOneIndexWithDocValuesSkipper() throws InterruptedException {
242+
assumeTrue("doc values skipper feature should be enabled", IndexSettings.DOC_VALUES_SKIPPER.isEnabled());
243+
checkSearchIdleBoolQueryMatchOneIndex(false);
244+
}
245+
246+
private void checkSearchIdleBoolQueryMatchOneIndex(boolean disableDocValuesSkippers) throws InterruptedException {
238247
// GIVEN
239248
final String idleIndex = "test1";
240249
final String activeIndex = "test2";
@@ -259,7 +268,7 @@ public void testSearchIdleBoolQueryMatchOneIndex() throws InterruptedException {
259268
.put(IndexSettings.TIME_SERIES_START_TIME.getKey(), "2021-05-12T00:00:00.000Z")
260269
.put(IndexSettings.TIME_SERIES_END_TIME.getKey(), "2021-05-13T23:59:59.999Z");
261270

262-
if (IndexSettings.DOC_VALUES_SKIPPER.isEnabled()) {
271+
if (disableDocValuesSkippers) {
263272
idleIndexSettingsBuilder.put(IndexSettings.USE_DOC_VALUES_SKIPPER.getKey(), false);
264273
activeIndexSettingsBuilder.put(IndexSettings.USE_DOC_VALUES_SKIPPER.getKey(), false);
265274
}

server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.apache.lucene.document.LongPoint;
1717
import org.apache.lucene.document.SortedNumericDocValuesField;
1818
import org.apache.lucene.document.StoredField;
19+
import org.apache.lucene.index.DocValuesSkipper;
1920
import org.apache.lucene.index.IndexReader;
2021
import org.apache.lucene.index.LeafReaderContext;
2122
import org.apache.lucene.index.PointValues;
@@ -69,6 +70,7 @@
6970
import java.time.ZoneOffset;
7071
import java.time.ZonedDateTime;
7172
import java.util.Collections;
73+
import java.util.List;
7274
import java.util.Locale;
7375
import java.util.Map;
7476
import java.util.Objects;
@@ -827,8 +829,24 @@ public Relation isFieldWithinQuery(
827829
QueryRewriteContext context
828830
) throws IOException {
829831
if (isIndexed() == false && pointsMetadataAvailable == false && hasDocValues()) {
830-
// we don't have a quick way to run this check on doc values, so fall back to default assuming we are within bounds
831-
return Relation.INTERSECTS;
832+
if (hasDocValuesSkipper() == false) {
833+
// we don't have a quick way to run this check on doc values, so fall back to default assuming we are within bounds
834+
return Relation.INTERSECTS;
835+
}
836+
long minValue = Long.MAX_VALUE;
837+
long maxValue = Long.MIN_VALUE;
838+
List<LeafReaderContext> leaves = reader.leaves();
839+
if (leaves.size() == 0) {
840+
// no data, so nothing matches
841+
return Relation.DISJOINT;
842+
}
843+
for (LeafReaderContext ctx : leaves) {
844+
DocValuesSkipper skipper = ctx.reader().getDocValuesSkipper(name());
845+
assert skipper != null : "no skipper for field:" + name() + " and reader:" + reader;
846+
minValue = Long.min(minValue, skipper.minValue());
847+
maxValue = Long.max(maxValue, skipper.maxValue());
848+
}
849+
return isFieldWithinQuery(minValue, maxValue, from, to, includeLower, includeUpper, timeZone, dateParser, context);
832850
}
833851
byte[] minPackedValue = PointValues.getMinPackedValue(reader, name());
834852
if (minPackedValue == null) {

server/src/test/java/org/elasticsearch/index/mapper/DateFieldTypeTests.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
package org.elasticsearch.index.mapper;
1010

11+
import org.apache.lucene.document.Field;
1112
import org.apache.lucene.document.LongPoint;
1213
import org.apache.lucene.document.NumericDocValuesField;
1314
import org.apache.lucene.document.SortedNumericDocValuesField;
@@ -85,12 +86,51 @@ public void testIsFieldWithinQueryDateNanos() throws IOException {
8586
isFieldWithinRangeTestCase(ft);
8687
}
8788

89+
public void testIsFieldWithinQueryDateMillisDocValueSkipper() throws IOException {
90+
DateFieldType ft = new DateFieldType(
91+
"my_date",
92+
false,
93+
false,
94+
false,
95+
true,
96+
true,
97+
DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER,
98+
Resolution.MILLISECONDS,
99+
null,
100+
null,
101+
Collections.emptyMap()
102+
);
103+
isFieldWithinRangeTestCase(ft);
104+
}
105+
106+
public void testIsFieldWithinQueryDateNanosDocValueSkipper() throws IOException {
107+
DateFieldType ft = new DateFieldType(
108+
"my_date",
109+
false,
110+
false,
111+
false,
112+
true,
113+
true,
114+
DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER,
115+
Resolution.NANOSECONDS,
116+
null,
117+
null,
118+
Collections.emptyMap()
119+
);
120+
isFieldWithinRangeTestCase(ft);
121+
}
122+
88123
public void isFieldWithinRangeTestCase(DateFieldType ft) throws IOException {
89124

90125
Directory dir = newDirectory();
91126
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
92127
LuceneDocument doc = new LuceneDocument();
93-
LongPoint field = new LongPoint("my_date", ft.parse("2015-10-12"));
128+
Field field;
129+
if (ft.hasDocValuesSkipper()) {
130+
field = SortedNumericDocValuesField.indexedField("my_date", ft.parse("2015-10-12"));
131+
} else {
132+
field = new LongPoint("my_date", ft.parse("2015-10-12"));
133+
}
94134
doc.add(field);
95135
w.addDocument(doc);
96136
field.setLongValue(ft.parse("2016-04-03"));

0 commit comments

Comments
 (0)