From 98029264b7c69690fb2b647428338684fb1c35e9 Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Wed, 8 Oct 2025 15:26:40 +0100 Subject: [PATCH 1/6] Further simplify SingleValueMatchQuery --- .../querydsl/query/SingleValueMatchQuery.java | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java index 6256260abd4d3..dc20904836732 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java @@ -130,17 +130,10 @@ private ScorerSupplier scorerSupplier( SortedNumericDocValues sortedNumerics, float boost, ScoreMode scoreMode - ) throws IOException { + ) { final int maxDoc = context.reader().maxDoc(); if (DocValues.unwrapSingleton(sortedNumerics) != null) { - // check for dense field - // TODO: check doc values skippers - final PointValues points = context.reader().getPointValues(fieldData.getFieldName()); - if (points != null && points.getDocCount() == maxDoc) { - return new DocIdSetIteratorScorerSupplier(boost, scoreMode, DocIdSetIterator.all(maxDoc)); - } else { - return new PredicateScorerSupplier(boost, scoreMode, maxDoc, MULTI_VALUE_MATCH_COST, sortedNumerics::advanceExact); - } + return new DocIdSetIteratorScorerSupplier(boost, scoreMode, sortedNumerics); } final CheckedIntPredicate predicate = doc -> { if (false == sortedNumerics.advanceExact(doc)) { @@ -160,23 +153,10 @@ private ScorerSupplier scorerSupplier( SortedSetDocValues sortedSetDocValues, float boost, ScoreMode scoreMode - ) throws IOException { + ) { final int maxDoc = context.reader().maxDoc(); if (DocValues.unwrapSingleton(sortedSetDocValues) != null) { - // check for dense field - // TODO: check doc values skippers - final Terms terms = context.reader().terms(fieldData.getFieldName()); - if (terms != null && terms.getDocCount() == maxDoc) { - return new DocIdSetIteratorScorerSupplier(boost, scoreMode, DocIdSetIterator.all(maxDoc)); - } else { - return new PredicateScorerSupplier( - boost, - scoreMode, - maxDoc, - MULTI_VALUE_MATCH_COST, - sortedSetDocValues::advanceExact - ); - } + return new DocIdSetIteratorScorerSupplier(boost, scoreMode, sortedSetDocValues); } final CheckedIntPredicate predicate = doc -> { if (false == sortedSetDocValues.advanceExact(doc)) { From 2e19aba39ad51dce7f3be05f274ece58e2a6ffa0 Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Wed, 8 Oct 2025 15:30:01 +0100 Subject: [PATCH 2/6] Update docs/changelog/136195.yaml --- docs/changelog/136195.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/changelog/136195.yaml diff --git a/docs/changelog/136195.yaml b/docs/changelog/136195.yaml new file mode 100644 index 0000000000000..7e7b357a4c1fe --- /dev/null +++ b/docs/changelog/136195.yaml @@ -0,0 +1,5 @@ +pr: 136195 +summary: Further simplify `SingleValueMatchQuery` +area: Compute Engine +type: enhancement +issues: [] From 30a0dc720ba599ecbba35ca55e48781495739a71 Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Wed, 8 Oct 2025 15:44:26 +0100 Subject: [PATCH 3/6] Update docs/changelog/136195.yaml --- docs/changelog/136195.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog/136195.yaml b/docs/changelog/136195.yaml index 7e7b357a4c1fe..3a54bd77ed12e 100644 --- a/docs/changelog/136195.yaml +++ b/docs/changelog/136195.yaml @@ -1,5 +1,5 @@ pr: 136195 summary: Further simplify `SingleValueMatchQuery` -area: Compute Engine +area: ES|QL type: enhancement issues: [] From 0aea1ddbca96ca98b257e664a3e17d5fdd31a334 Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Wed, 8 Oct 2025 16:30:16 +0100 Subject: [PATCH 4/6] Doesn't work for ordinals :-( --- .../querydsl/query/SingleValueMatchQuery.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java index dc20904836732..875f4832602cf 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java @@ -153,10 +153,24 @@ private ScorerSupplier scorerSupplier( SortedSetDocValues sortedSetDocValues, float boost, ScoreMode scoreMode - ) { + ) throws IOException { final int maxDoc = context.reader().maxDoc(); if (DocValues.unwrapSingleton(sortedSetDocValues) != null) { - return new DocIdSetIteratorScorerSupplier(boost, scoreMode, sortedSetDocValues); + // check for dense field + // TODO: check doc values skippers + // TODO: can we make Ordinals implement nextDoc() and always use DocIdSetIteratorScorerSupplier + final Terms terms = context.reader().terms(fieldData.getFieldName()); + if (terms != null && terms.getDocCount() == maxDoc) { + return new DocIdSetIteratorScorerSupplier(boost, scoreMode, DocIdSetIterator.all(maxDoc)); + } else { + return new PredicateScorerSupplier( + boost, + scoreMode, + maxDoc, + MULTI_VALUE_MATCH_COST, + sortedSetDocValues::advanceExact + ); + } } final CheckedIntPredicate predicate = doc -> { if (false == sortedSetDocValues.advanceExact(doc)) { From 5e5e44df320eb005e8be9a88cbfe9e96e6266439 Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Wed, 15 Oct 2025 15:54:45 +0100 Subject: [PATCH 5/6] And use SDV for ordinals --- .../querydsl/query/SingleValueMatchQuery.java | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java index 0846820662efb..bd6ac181e16a0 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java @@ -156,22 +156,9 @@ private ScorerSupplier scorerSupplier( ScoreMode scoreMode ) throws IOException { final int maxDoc = context.reader().maxDoc(); - if (DocValues.unwrapSingleton(sortedSetDocValues) != null) { - // check for dense field - // TODO: check doc values skippers - // TODO: can we make Ordinals implement nextDoc() and always use DocIdSetIteratorScorerSupplier - final Terms terms = context.reader().terms(fieldData.getFieldName()); - if (terms != null && terms.getDocCount() == maxDoc) { - return new DocIdSetIteratorScorerSupplier(boost, scoreMode, DocIdSetIterator.all(maxDoc)); - } else { - return new PredicateScorerSupplier( - boost, - scoreMode, - maxDoc, - MULTI_VALUE_MATCH_COST, - sortedSetDocValues::advanceExact - ); - } + SortedDocValues sdv = DocValues.unwrapSingleton(DocValues.getSortedSet(context.reader(), fieldData.getFieldName())); + if (sdv != null) { + return new DocIdSetIteratorScorerSupplier(boost, scoreMode, sdv); } final CheckedIntPredicate predicate = doc -> { if (false == sortedSetDocValues.advanceExact(doc)) { From e72397cbd97dfff502b0df5ce0fba5a779250b9e Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Wed, 15 Oct 2025 17:05:41 +0100 Subject: [PATCH 6/6] Handle empty docvalues cases and constant keywords --- .../compute/querydsl/query/SingleValueMatchQuery.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java index bd6ac181e16a0..97eda529ca9e1 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/querydsl/query/SingleValueMatchQuery.java @@ -35,6 +35,7 @@ import org.elasticsearch.index.fielddata.LeafOrdinalsFieldData; import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.index.fielddata.SortedNumericLongValues; +import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; import java.io.IOException; import java.util.Objects; @@ -133,7 +134,8 @@ private ScorerSupplier scorerSupplier( ) throws IOException { final int maxDoc = context.reader().maxDoc(); NumericDocValues ndv = DocValues.unwrapSingleton(DocValues.getSortedNumeric(context.reader(), fieldData.getFieldName())); - if (ndv != null) { + if (ndv != null && ndv.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) { + ndv = DocValues.unwrapSingleton(DocValues.getSortedNumeric(context.reader(), fieldData.getFieldName())); return new DocIdSetIteratorScorerSupplier(boost, scoreMode, ndv); } final CheckedIntPredicate predicate = doc -> { @@ -157,7 +159,8 @@ private ScorerSupplier scorerSupplier( ) throws IOException { final int maxDoc = context.reader().maxDoc(); SortedDocValues sdv = DocValues.unwrapSingleton(DocValues.getSortedSet(context.reader(), fieldData.getFieldName())); - if (sdv != null) { + if (sdv != null && sdv.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) { + sdv = DocValues.unwrapSingleton(DocValues.getSortedSet(context.reader(), fieldData.getFieldName())); return new DocIdSetIteratorScorerSupplier(boost, scoreMode, sdv); } final CheckedIntPredicate predicate = doc -> { @@ -206,6 +209,9 @@ private ScorerSupplier scorerSupplier( @Override public Query rewrite(IndexSearcher indexSearcher) throws IOException { + if (fieldData instanceof ConstantIndexFieldData cfd && cfd.getValue() != null) { + return new MatchAllDocsQuery(); + } for (LeafReaderContext context : indexSearcher.getIndexReader().leaves()) { final LeafReader reader = context.reader(); final int maxDoc = reader.maxDoc();