Skip to content

Commit

Permalink
Better approach, using KEYWORD subfield
Browse files Browse the repository at this point in the history
  • Loading branch information
craigtaverner committed Jan 3, 2025
1 parent a4f3b5e commit c1744c9
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.IntFunction;

/**
Expand Down Expand Up @@ -73,8 +72,7 @@ public static QueryList rawTermQueryList(MappedFieldType field, SearchExecutionC
BytesRefBlock bytesRefBlock = (BytesRefBlock) block;
BytesRef value = bytesRefBlock.getBytesRef(offset, new BytesRef());
if (field.typeName().equals("text")) {
// Text fields involve case-insensitive contains queries, we need to use lowercase on the term query
return new BytesRef(value.utf8ToString().toLowerCase(Locale.ROOT));
throw new IllegalArgumentException("Cannot perform LOOKUP JOIN on TEXT fields");
}
return value;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,20 @@ language.id:integer | language.name:keyword | language.name.keyword:keyword
1 | English | English
;

joinOnNestedNestedFieldRowImplicitKeywords
required_capability: join_lookup_v10

ROW language.name = ["English", "French"]
| MV_EXPAND language.name
| LOOKUP JOIN languages_nested_fields ON language.name
| KEEP language.id, language.name, language.name.keyword, language.code
;

language.id:integer | language.name:keyword | language.name.keyword:keyword | language.code:keyword
1 | English | English | EN
2 | French | French | FR
;

###############################################
# Tests with clientips_lookup index
###############################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@
import org.elasticsearch.xpack.esql.core.expression.Attribute;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.Expressions;
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
import org.elasticsearch.xpack.esql.core.expression.Literal;
import org.elasticsearch.xpack.esql.core.expression.NameId;
import org.elasticsearch.xpack.esql.core.expression.NamedExpression;
import org.elasticsearch.xpack.esql.core.expression.TypedAttribute;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.core.util.Holder;
Expand Down Expand Up @@ -584,35 +586,49 @@ private PhysicalOperation planLookupJoin(LookupJoinExec join, LocalExecutionPlan
throw new IllegalArgumentException("can't plan [" + join + "], found index with mode [" + entry.getValue() + "]");
}
String indexName = entry.getKey();
List<Layout.ChannelAndType> matchFields = new ArrayList<>(join.leftFields().size());
for (Attribute m : join.leftFields()) {
Layout.ChannelAndType t = source.layout.get(m.id());
if (t == null) {
throw new IllegalArgumentException("can't plan [" + join + "][" + m + "]");
if (join.leftFields().size() != join.rightFields().size()) {
throw new IllegalArgumentException("can't plan [" + join + "]: mismatching left and right field count");
}
List<MatchConfig> matchFields = new ArrayList<>(join.leftFields().size());
for (int i = 0; i < join.leftFields().size(); i++) {
TypedAttribute left = (TypedAttribute) join.leftFields().get(i);
FieldAttribute right = (FieldAttribute) join.rightFields().get(i);
Layout.ChannelAndType input = source.layout.get(left.id());
if (input == null) {
throw new IllegalArgumentException("can't plan [" + join + "][" + left + "]");
}
matchFields.add(t);
matchFields.add(new MatchConfig(right, input));
}
if (matchFields.size() != 1) {
throw new IllegalArgumentException("can't plan [" + join + "]");
throw new IllegalArgumentException("can't plan [" + join + "]: multiple join predicates are not supported");
}
// TODO support multiple match fields, and support more than equality predicates
MatchConfig matchConfig = matchFields.getFirst();

return source.with(
new LookupFromIndexOperator.Factory(
sessionId,
parentTask,
context.queryPragmas().enrichMaxWorkers(),
matchFields.getFirst().channel(),
matchConfig.channel(),
lookupFromIndexService,
matchFields.getFirst().type(),
matchConfig.type(),
indexName,
join.leftFields().getFirst().name(),
matchConfig.fieldName(),
join.addedFields().stream().map(f -> (NamedExpression) f).toList(),
join.source()
),
layout
);
}

private record MatchConfig(String fieldName, int channel, DataType type) {
private MatchConfig(FieldAttribute match, Layout.ChannelAndType input) {
// Note, this handles TEXT fields with KEYWORD subfields, and we assume tha has been validated earlier during planning
this(match.exactAttribute().name(), input.channel(), input.type());
}
}

private ExpressionEvaluator.Factory toEvaluator(Expression exp, Layout layout) {
return EvalMapper.toEvaluator(exp, layout);
}
Expand Down

0 comments on commit c1744c9

Please sign in to comment.