Skip to content

Commit dc73837

Browse files
authored
ESQL: Skip multivalues in LOOKUP JOIN matches (#120519) (#120646)
Manual 8.x backport of #120519
1 parent 4c24fa7 commit dc73837

File tree

6 files changed

+266
-141
lines changed

6 files changed

+266
-141
lines changed

x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@
3939
*/
4040
public abstract class QueryList {
4141
protected final Block block;
42+
protected final boolean onlySingleValues;
4243

43-
protected QueryList(Block block) {
44+
protected QueryList(Block block, boolean onlySingleValues) {
4445
this.block = block;
46+
this.onlySingleValues = onlySingleValues;
4547
}
4648

4749
/**
@@ -51,6 +53,12 @@ int getPositionCount() {
5153
return block.getPositionCount();
5254
}
5355

56+
/**
57+
* Returns a copy of this query list that only returns queries for single-valued positions.
58+
* That is, it returns `null` queries for either multivalued or null positions.
59+
*/
60+
public abstract QueryList onlySingleValues();
61+
5462
/**
5563
* Returns the query at the given position.
5664
*/
@@ -93,7 +101,7 @@ public static QueryList rawTermQueryList(MappedFieldType field, SearchExecutionC
93101
case COMPOSITE -> throw new IllegalArgumentException("can't read values from [composite] block");
94102
case UNKNOWN -> throw new IllegalArgumentException("can't read values from [" + block + "]");
95103
};
96-
return new TermQueryList(field, searchExecutionContext, block, blockToJavaObject);
104+
return new TermQueryList(field, searchExecutionContext, block, false, blockToJavaObject);
97105
}
98106

99107
/**
@@ -103,7 +111,7 @@ public static QueryList rawTermQueryList(MappedFieldType field, SearchExecutionC
103111
public static QueryList ipTermQueryList(MappedFieldType field, SearchExecutionContext searchExecutionContext, BytesRefBlock block) {
104112
BytesRef scratch = new BytesRef();
105113
byte[] ipBytes = new byte[InetAddressPoint.BYTES];
106-
return new TermQueryList(field, searchExecutionContext, block, offset -> {
114+
return new TermQueryList(field, searchExecutionContext, block, false, offset -> {
107115
final var bytes = block.getBytesRef(offset, scratch);
108116
if (ipBytes.length != bytes.length) {
109117
// Lucene only support 16-byte IP addresses, even IPv4 is encoded in 16 bytes
@@ -123,6 +131,7 @@ public static QueryList dateTermQueryList(MappedFieldType field, SearchExecution
123131
field,
124132
searchExecutionContext,
125133
block,
134+
false,
126135
field instanceof RangeFieldMapper.RangeFieldType rangeFieldType
127136
? offset -> rangeFieldType.dateTimeFormatter().formatMillis(block.getLong(offset))
128137
: block::getLong
@@ -133,7 +142,7 @@ public static QueryList dateTermQueryList(MappedFieldType field, SearchExecution
133142
* Returns a list of geo_shape queries for the given field and the input block.
134143
*/
135144
public static QueryList geoShapeQueryList(MappedFieldType field, SearchExecutionContext searchExecutionContext, Block block) {
136-
return new GeoShapeQueryList(field, searchExecutionContext, block);
145+
return new GeoShapeQueryList(field, searchExecutionContext, block, false);
137146
}
138147

139148
private static class TermQueryList extends QueryList {
@@ -145,18 +154,27 @@ private TermQueryList(
145154
MappedFieldType field,
146155
SearchExecutionContext searchExecutionContext,
147156
Block block,
157+
boolean onlySingleValues,
148158
IntFunction<Object> blockValueReader
149159
) {
150-
super(block);
160+
super(block, onlySingleValues);
151161
this.field = field;
152162
this.searchExecutionContext = searchExecutionContext;
153163
this.blockValueReader = blockValueReader;
154164
}
155165

166+
@Override
167+
public TermQueryList onlySingleValues() {
168+
return new TermQueryList(field, searchExecutionContext, block, true, blockValueReader);
169+
}
170+
156171
@Override
157172
Query getQuery(int position) {
158-
final int first = block.getFirstValueIndex(position);
159173
final int count = block.getValueCount(position);
174+
if (onlySingleValues && count != 1) {
175+
return null;
176+
}
177+
final int first = block.getFirstValueIndex(position);
160178
return switch (count) {
161179
case 0 -> null;
162180
case 1 -> field.termQuery(blockValueReader.apply(first), searchExecutionContext);
@@ -179,19 +197,32 @@ private static class GeoShapeQueryList extends QueryList {
179197
private final IntFunction<Geometry> blockValueReader;
180198
private final IntFunction<Query> shapeQuery;
181199

182-
private GeoShapeQueryList(MappedFieldType field, SearchExecutionContext searchExecutionContext, Block block) {
183-
super(block);
200+
private GeoShapeQueryList(
201+
MappedFieldType field,
202+
SearchExecutionContext searchExecutionContext,
203+
Block block,
204+
boolean onlySingleValues
205+
) {
206+
super(block, onlySingleValues);
184207

185208
this.field = field;
186209
this.searchExecutionContext = searchExecutionContext;
187210
this.blockValueReader = blockToGeometry(block);
188211
this.shapeQuery = shapeQuery();
189212
}
190213

214+
@Override
215+
public GeoShapeQueryList onlySingleValues() {
216+
return new GeoShapeQueryList(field, searchExecutionContext, block, true);
217+
}
218+
191219
@Override
192220
Query getQuery(int position) {
193-
final int first = block.getFirstValueIndex(position);
194221
final int count = block.getValueCount(position);
222+
if (onlySingleValues && count != 1) {
223+
return null;
224+
}
225+
final int first = block.getFirstValueIndex(position);
195226
return switch (count) {
196227
case 0 -> null;
197228
case 1 -> shapeQuery.apply(first);

0 commit comments

Comments
 (0)