Skip to content

Commit fd1c008

Browse files
authored
Enable semantic search in FORK (#125960)
1 parent 3fbcb3c commit fd1c008

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

x-pack/plugin/esql/qa/testFixtures/src/main/resources/fork.csv-spec

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,23 @@ fork1 | 10052
9292
fork2 | 10099
9393
fork2 | 10100
9494
;
95+
96+
forkWithSemanticSearchAndScore
97+
required_capability: fork
98+
required_capability: semantic_text_field_caps
99+
required_capability: metadata_score
100+
101+
FROM semantic_text METADATA _id, _score
102+
| FORK ( WHERE semantic_text_field:"something" | SORT _score DESC | LIMIT 2)
103+
( WHERE semantic_text_field:"something else" | SORT _score DESC | LIMIT 2)
104+
| EVAL _score = round(_score, 4)
105+
| SORT _fork, _score, _id
106+
| KEEP _fork, _score, _id, semantic_text_field
107+
;
108+
109+
_fork:keyword | _score:double | _id:keyword | semantic_text_field:text
110+
fork1 | 2.156063961865257E18 | 3 | be excellent to each other
111+
fork1 | 5.603396578413904E18 | 2 | all we have to decide is what to do with the time that is given to us
112+
fork2 | 2.3447541759648727E18 | 3 | be excellent to each other
113+
fork2 | 6.093784261960139E18 | 2 | all we have to decide is what to do with the time that is given to us
114+
;

x-pack/plugin/esql/qa/testFixtures/src/main/resources/rrf.csv-spec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,21 @@ _score:double | author:keyword | title:keyword | _fork
109109
0.0161 | Ursula K. Le Guin | The Word For World i | fork2
110110
0.0159 | Ursula K. Le Guin | The Dispossessed | fork2
111111
;
112+
113+
rrfWithSemanticSearch
114+
required_capability: rrf
115+
required_capability: semantic_text_field_caps
116+
required_capability: metadata_score
117+
118+
FROM semantic_text METADATA _id, _score, _index
119+
| FORK ( WHERE semantic_text_field:"something" | SORT _score DESC | LIMIT 2)
120+
( WHERE semantic_text_field:"something else" | SORT _score DESC | LIMIT 2)
121+
| RRF
122+
| EVAL _score = round(_score, 4)
123+
| KEEP _fork, _score, _id, semantic_text_field
124+
;
125+
126+
_fork:keyword | _score:double | _id:keyword | semantic_text_field:keyword
127+
[fork1, fork2] | 0.0328 | 2 | all we have to decide is what to do with the time that is given to us
128+
[fork1, fork2] | 0.0323 | 3 | be excellent to each other
129+
;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryBuilderResolver.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
import org.elasticsearch.index.query.QueryBuilder;
1313
import org.elasticsearch.index.query.QueryRewriteContext;
1414
import org.elasticsearch.index.query.Rewriteable;
15+
import org.elasticsearch.xpack.esql.core.expression.Expression;
1516
import org.elasticsearch.xpack.esql.core.util.Holder;
1617
import org.elasticsearch.xpack.esql.plan.logical.EsRelation;
18+
import org.elasticsearch.xpack.esql.plan.logical.Fork;
1719
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
1820
import org.elasticsearch.xpack.esql.planner.TranslatorHandler;
1921
import org.elasticsearch.xpack.esql.plugin.TransportActionServices;
@@ -22,6 +24,8 @@
2224
import java.io.IOException;
2325
import java.util.HashSet;
2426
import java.util.Set;
27+
import java.util.function.Function;
28+
import java.util.stream.Collectors;
2529

2630
/**
2731
* Some {@link FullTextFunction} implementations such as {@link org.elasticsearch.xpack.esql.expression.function.fulltext.Match}
@@ -34,11 +38,7 @@ public final class QueryBuilderResolver {
3438
private QueryBuilderResolver() {}
3539

3640
public static void resolveQueryBuilders(LogicalPlan plan, TransportActionServices services, ActionListener<LogicalPlan> listener) {
37-
var hasFullTextFunctions = plan.anyMatch(p -> {
38-
Holder<Boolean> hasFullTextFunction = new Holder<>(false);
39-
p.forEachExpression(FullTextFunction.class, unused -> hasFullTextFunction.set(true));
40-
return hasFullTextFunction.get();
41-
});
41+
var hasFullTextFunctions = hasFullTextFunctions(plan);
4242
if (hasFullTextFunctions) {
4343
Rewriteable.rewriteAndFetch(
4444
new FullTextFunctionsRewritable(plan),
@@ -69,12 +69,29 @@ private static Set<String> indexNames(LogicalPlan plan) {
6969
return indexNames;
7070
}
7171

72+
private static boolean hasFullTextFunctions(LogicalPlan plan) {
73+
return plan.anyMatch(p -> {
74+
Holder<Boolean> hasFullTextFunction = new Holder<>(false);
75+
p.forEachExpression(FullTextFunction.class, unused -> hasFullTextFunction.set(true));
76+
77+
if (p instanceof Fork fork) {
78+
fork.subPlans().forEach(subPlan -> {
79+
if (hasFullTextFunctions(subPlan)) {
80+
hasFullTextFunction.set(true);
81+
}
82+
});
83+
}
84+
85+
return hasFullTextFunction.get();
86+
});
87+
}
88+
7289
private record FullTextFunctionsRewritable(LogicalPlan plan) implements Rewriteable<QueryBuilderResolver.FullTextFunctionsRewritable> {
7390
@Override
7491
public FullTextFunctionsRewritable rewrite(QueryRewriteContext ctx) throws IOException {
7592
Holder<IOException> exceptionHolder = new Holder<>();
7693
Holder<Boolean> updated = new Holder<>(false);
77-
LogicalPlan newPlan = plan.transformExpressionsDown(FullTextFunction.class, f -> {
94+
LogicalPlan newPlan = transformPlan(plan, f -> {
7895
QueryBuilder builder = f.queryBuilder(), initial = builder;
7996
builder = builder == null ? f.asQuery(TranslatorHandler.TRANSLATOR_HANDLER).toQueryBuilder() : builder;
8097
try {
@@ -91,5 +108,15 @@ public FullTextFunctionsRewritable rewrite(QueryRewriteContext ctx) throws IOExc
91108
}
92109
return updated.get() ? new FullTextFunctionsRewritable(newPlan) : this;
93110
}
111+
112+
private LogicalPlan transformPlan(LogicalPlan plan, Function<FullTextFunction, ? extends Expression> rule) {
113+
return plan.transformExpressionsDown(FullTextFunction.class, rule).transformDown(Fork.class, fork -> {
114+
var subPlans = fork.subPlans()
115+
.stream()
116+
.map(subPlan -> subPlan.transformExpressionsDown(FullTextFunction.class, rule))
117+
.collect(Collectors.toList());
118+
return new Fork(fork.source(), fork.child(), subPlans);
119+
});
120+
}
94121
}
95122
}

0 commit comments

Comments
 (0)