1717
1818/**
1919 * Based on {@link org.apache.lucene.search.DocValuesRangeIterator} but modified for time series and logsdb use cases.
20+ * <p>
21+ * The @timestamp field always has exactly one value and all documents always have a @timestamp value.
22+ * Additionally, the @timestamp field is always the secondary index sort field and sort order is always descending.
23+ * <p>
24+ * This makes the doc value skipper on @timestamp field less effective, and so the doc value skipper for the first index sort field is
25+ * also used to skip to the next primary sort value if for the current skipper represents one value (min and max value are the same) and
26+ * the current value is lower than minTimestamp.
2027 */
2128final class TimestampIterator extends TwoPhaseIterator {
2229
@@ -110,7 +117,7 @@ public int advance(int target) throws IOException {
110117 return doc = target ;
111118 }
112119 break ;
113- case NO :
120+ case NO_AND_SKIP :
114121 if (target > primaryFieldUpTo ) {
115122 primaryFieldSkipper .advance (target );
116123 for (int level = 0 ; level < primaryFieldSkipper .numLevels (); level ++) {
@@ -124,7 +131,12 @@ public int advance(int target) throws IOException {
124131 upTo = primaryFieldUpTo ;
125132 }
126133 }
127-
134+ if (upTo == DocIdSetIterator .NO_MORE_DOCS ) {
135+ return doc = NO_MORE_DOCS ;
136+ }
137+ target = upTo + 1 ;
138+ break ;
139+ case NO :
128140 if (upTo == DocIdSetIterator .NO_MORE_DOCS ) {
129141 return doc = NO_MORE_DOCS ;
130142 }
@@ -144,8 +156,10 @@ public long cost() {
144156 Match match (int level ) {
145157 long minValue = timestampSkipper .minValue (level );
146158 long maxValue = timestampSkipper .maxValue (level );
147- if (minValue > maxTimestamp || maxValue < minTimestamp ) {
159+ if (minValue > maxTimestamp ) {
148160 return Match .NO ;
161+ } else if (maxValue < minTimestamp ) {
162+ return Match .NO_AND_SKIP ;
149163 } else if (minValue >= minTimestamp && maxValue <= maxTimestamp ) {
150164 return Match .YES ;
151165 } else {
@@ -163,7 +177,7 @@ public boolean matches() throws IOException {
163177 final long value = timestamps .longValue ();
164178 yield value >= minTimestamp && value <= maxTimestamp ;
165179 }
166- case NO -> throw new IllegalStateException ("Unpositioned approximation" );
180+ case NO_AND_SKIP , NO -> throw new IllegalStateException ("Unpositioned approximation" );
167181 };
168182 }
169183
@@ -183,6 +197,8 @@ public float matchCost() {
183197 enum Match {
184198 /** None of the documents in the range match */
185199 NO ,
200+ /** Same as NO, but can maybe also skip to next primary sort value */
201+ NO_AND_SKIP ,
186202 /** Document values need to be checked to verify matches */
187203 MAYBE ,
188204 /** All docs in the range match */
0 commit comments