Pārlūkot izejas kodu

ESQL: Add telemetry support for Lookup Join On Expression (#134942)

Add telemetry support for Lookup Join On Expression, adding LOOKUP JOIN ON EXPRESSION label
Keep the existing LOOKUP JOIN telemetry label for lookup join on fields to provide BWC
Julian Kiryakov 2 nedēļas atpakaļ
vecāks
revīzija
f1ff526bbe

+ 5 - 0
docs/changelog/134942.yaml

@@ -0,0 +1,5 @@
+pr: 134942
+summary: Add telemetry support for Lookup Join On Expression
+area: ES|QL
+type: enhancement
+issues: []

+ 23 - 0
x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/TelemetryIT.java

@@ -146,6 +146,23 @@ public class TelemetryIT extends AbstractEsqlIntegTestCase {
                     Map.ofEntries(Map.entry("TO_STRING", 1)),
                     true
                 ) },
+            new Object[] {
+                new Test(
+                    """
+                        FROM idx
+                        | EVAL y = to_str(host)
+                        | RENAME host as host_left
+                        | LOOKUP JOIN lookup_idx ON host_left == host
+                        """,
+                    Map.ofEntries(
+                        Map.entry("RENAME", 1),
+                        Map.entry("FROM", 1),
+                        Map.entry("EVAL", 1),
+                        Map.entry("LOOKUP JOIN ON EXPRESSION", 1)
+                    ),
+                    Map.ofEntries(Map.entry("TO_STRING", 1)),
+                    true
+                ) },
             new Object[] {
                 new Test(
                     "TS time_series_idx | LIMIT 10",
@@ -184,6 +201,12 @@ public class TelemetryIT extends AbstractEsqlIntegTestCase {
     }
 
     public void testMetrics() throws Exception {
+        if (testCase.query().contains("LOOKUP JOIN lookup_idx ON host_left == host")) {
+            assumeTrue(
+                "requires LOOKUP JOIN ON boolean expression capability",
+                EsqlCapabilities.Cap.LOOKUP_JOIN_ON_BOOLEAN_EXPRESSION.isEnabled()
+            );
+        }
         DiscoveryNode dataNode = randomDataNode();
         testQuery(dataNode, testCase);
     }

+ 2 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/telemetry/FeatureMetric.java

@@ -63,7 +63,8 @@ public enum FeatureMetric {
     DROP(Drop.class::isInstance),
     KEEP(Keep.class::isInstance),
     RENAME(Rename.class::isInstance),
-    LOOKUP_JOIN(LookupJoin.class::isInstance),
+    LOOKUP_JOIN(plan -> plan instanceof LookupJoin lookupJoin && lookupJoin.config().joinOnConditions() == null),
+    LOOKUP_JOIN_ON_EXPRESSION(plan -> plan instanceof LookupJoin lookupJoin && lookupJoin.config().joinOnConditions() != null),
     LOOKUP(Lookup.class::isInstance),
     CHANGE_POINT(ChangePoint.class::isInstance),
     INLINE_STATS(InlineStats.class::isInstance),

+ 78 - 21
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/telemetry/VerifierMetricsTests.java

@@ -34,6 +34,7 @@ import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.INLINE_STATS;
 import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.KEEP;
 import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.LIMIT;
 import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.LOOKUP_JOIN;
+import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.LOOKUP_JOIN_ON_EXPRESSION;
 import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.MV_EXPAND;
 import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.RENAME;
 import static org.elasticsearch.xpack.esql.telemetry.FeatureMetric.ROW;
@@ -65,7 +66,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
         assertEquals(1, function("concat", c));
     }
@@ -87,7 +89,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
         assertEquals(1, function("length", c));
     }
@@ -109,7 +112,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
         assertEquals(1, function("concat", c));
     }
@@ -131,7 +135,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
     }
 
@@ -152,7 +157,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
     }
 
@@ -173,7 +179,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
         assertEquals(1, function("max", c));
     }
@@ -195,7 +202,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
     }
 
@@ -216,7 +224,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
     }
 
@@ -257,7 +266,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, drop(c));
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(0, inlineStats(c));
 
         assertEquals(1, function("length", c));
@@ -343,7 +353,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(1L, keep(c));
         assertEquals(0, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(1, function("to_string", c));
     }
 
@@ -374,7 +385,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(1L, keep(c));
         assertEquals(0, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
     }
 
     public void testShowInfo() {
@@ -395,7 +407,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(1, function("count", c));
     }
 
@@ -417,7 +430,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
     }
 
     public void testDropAndRename() {
@@ -438,7 +452,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, keep(c));
         assertEquals(1L, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(1, function("count", c));
     }
 
@@ -465,7 +480,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(1L, keep(c));
         assertEquals(0, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
     }
 
     public void testCategorize() {
@@ -490,7 +506,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(1L, keep(c));
         assertEquals(0, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(1, function("count", c));
         assertEquals(1, function("categorize", c));
     }
@@ -517,7 +534,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
         assertEquals(1L, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(1, function("max", c));
     }
 
@@ -544,7 +562,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
         assertEquals(1L, inlineStats(c));
-        assertEquals(0, lookupjoin(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(1, function("max", c));
     }
 
@@ -570,7 +589,40 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
         assertEquals(0, inlineStats(c));
-        assertEquals(1L, lookupjoin(c));
+        assertEquals(1L, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
+        assertEquals(1, function("max", c));
+    }
+
+    public void testBinaryPlanAfterStatsExpressionJoin() {
+        assumeTrue(
+            "requires LOOKUP JOIN ON boolean expression capability",
+            EsqlCapabilities.Cap.LOOKUP_JOIN_ON_BOOLEAN_EXPRESSION.isEnabled()
+        );
+        Counters c = esql("""
+            from employees
+            | eval language_code = languages
+            | stats m = max(salary) by language_code
+            | rename language_code as language_code_left
+            | lookup join languages_lookup on language_code_left >= language_code""");
+        assertEquals(0, dissect(c));
+        assertEquals(1L, eval(c));
+        assertEquals(0, grok(c));
+        assertEquals(0, limit(c));
+        assertEquals(0, sort(c));
+        assertEquals(1L, stats(c));
+        assertEquals(0, where(c));
+        assertEquals(0, enrich(c));
+        assertEquals(0, mvExpand(c));
+        assertEquals(0, show(c));
+        assertEquals(0, row(c));
+        assertEquals(1L, from(c));
+        assertEquals(0, drop(c));
+        assertEquals(0, keep(c));
+        assertEquals(1L, rename(c));
+        assertEquals(0, inlineStats(c));
+        assertEquals(0, lookupJoinOnFields(c));
+        assertEquals(1L, lookupJoinOnExpression(c));
         assertEquals(1, function("max", c));
     }
 
@@ -597,7 +649,8 @@ public class VerifierMetricsTests extends ESTestCase {
         assertEquals(0, keep(c));
         assertEquals(0, rename(c));
         assertEquals(1L, inlineStats(c));
-        assertEquals(1L, lookupjoin(c));
+        assertEquals(1L, lookupJoinOnFields(c));
+        assertEquals(0, lookupJoinOnExpression(c));
         assertEquals(1, function("max", c));
     }
 
@@ -665,10 +718,14 @@ public class VerifierMetricsTests extends ESTestCase {
         return c.get(FEATURES_PREFIX + INLINE_STATS);
     }
 
-    private long lookupjoin(Counters c) {
+    private long lookupJoinOnFields(Counters c) {
         return c.get(FEATURES_PREFIX + LOOKUP_JOIN);
     }
 
+    private long lookupJoinOnExpression(Counters c) {
+        return c.get(FEATURES_PREFIX + LOOKUP_JOIN_ON_EXPRESSION);
+    }
+
     private long function(String function, Counters c) {
         return c.get(FUNC_PREFIX + function);
     }

+ 151 - 147
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/60_usage.yml

@@ -7,11 +7,11 @@ setup:
           parameters: [ method, path, parameters, capabilities ]
           capabilities: [ function_stats ]
       reason: "META command removed which changes the count of the data returned"
-      test_runner_features: [capabilities]
+      test_runner_features: [ capabilities ]
 
   - do:
       indices.create:
-        index:  test
+        index: test
         body:
           settings:
             number_of_shards: 1
@@ -27,11 +27,11 @@ setup:
 ---
 "Basic ESQL usage output (telemetry) snapshot version":
   - requires:
-      test_runner_features: [capabilities]
+      test_runner_features: [ capabilities ]
       capabilities:
         - method: POST
           path: /_query
-          parameters: []
+          parameters: [ ]
           # A snapshot function was removed in match_function_options, it can't work on mixed cluster tests otherwise.
           capabilities:
             - snapshot_test_for_telemetry_v2
@@ -47,48 +47,49 @@ setup:
             - inline_stats
       reason: "Test that should only be executed on snapshot versions"
 
-  - do: {xpack.usage: {}}
+  - do: { xpack.usage: { } }
   - match: { esql.available: true }
   - match: { esql.enabled: true }
-  - length: { esql.features: 26 }
-  - set: {esql.features.dissect: dissect_counter}
-  - set: {esql.features.drop: drop_counter}
-  - set: {esql.features.eval: eval_counter}
-  - set: {esql.features.enrich: enrich_counter}
-  - set: {esql.features.from: from_counter}
-  - set: {esql.features.grok: grok_counter}
-  - set: {esql.features.keep: keep_counter}
-  - set: {esql.features.limit: limit_counter}
-  - set: {esql.features.mv_expand: mv_expand_counter}
-  - set: {esql.features.rename: rename_counter}
-  - set: {esql.features.row: row_counter}
-  - set: {esql.features.show: show_counter}
-  - set: {esql.features.sort: sort_counter}
-  - set: {esql.features.stats: stats_counter}
-  - set: {esql.features.where: where_counter}
-  - set: {esql.features.lookup_join: lookup_join_counter}
-  - set: {esql.features.lookup: lookup_counter}
-  - set: {esql.features.change_point: change_point_counter}
-  - set: {esql.features.inline_stats: inline_stats_counter}
-  - set: {esql.features.rerank: rerank_counter}
-  - set: {esql.features.insist: insist_counter}
-  - set: {esql.features.fork: fork_counter}
-  - set: {esql.features.fuse: fuse_counter}
-  - set: {esql.features.completion: completion_counter}
-  - set: {esql.features.sample: sample_counter}
+  - length: { esql.features: 27 }
+  - set: { esql.features.dissect: dissect_counter }
+  - set: { esql.features.drop: drop_counter }
+  - set: { esql.features.eval: eval_counter }
+  - set: { esql.features.enrich: enrich_counter }
+  - set: { esql.features.from: from_counter }
+  - set: { esql.features.grok: grok_counter }
+  - set: { esql.features.keep: keep_counter }
+  - set: { esql.features.limit: limit_counter }
+  - set: { esql.features.mv_expand: mv_expand_counter }
+  - set: { esql.features.rename: rename_counter }
+  - set: { esql.features.row: row_counter }
+  - set: { esql.features.show: show_counter }
+  - set: { esql.features.sort: sort_counter }
+  - set: { esql.features.stats: stats_counter }
+  - set: { esql.features.where: where_counter }
+  - set: { esql.features.lookup_join: lookup_join_counter }
+  - set: { esql.features.lookup_join_on_expression: lookup_join_on_expression_counter }
+  - set: { esql.features.lookup: lookup_counter }
+  - set: { esql.features.change_point: change_point_counter }
+  - set: { esql.features.inline_stats: inline_stats_counter }
+  - set: { esql.features.rerank: rerank_counter }
+  - set: { esql.features.insist: insist_counter }
+  - set: { esql.features.fork: fork_counter }
+  - set: { esql.features.fuse: fuse_counter }
+  - set: { esql.features.completion: completion_counter }
+  - set: { esql.features.sample: sample_counter }
   - length: { esql.queries: 3 }
-  - set: {esql.queries.rest.total: rest_total_counter}
-  - set: {esql.queries.rest.failed: rest_failed_counter}
-  - set: {esql.queries.kibana.total: kibana_total_counter}
-  - set: {esql.queries.kibana.failed: kibana_failed_counter}
-  - set: {esql.queries._all.total: all_total_counter}
-  - set: {esql.queries._all.failed: all_failed_counter}
-  - set: {esql.functions.max: functions_max}
-  - set: {esql.functions.min: functions_min}
-  - set: {esql.functions.cos: functions_cos}
-  - set: {esql.functions.to_long: functions_to_long}
-  - set: {esql.functions.coalesce: functions_coalesce}
-  - set: {esql.functions.categorize: functions_categorize}
+  - set: { esql.queries.rest.total: rest_total_counter }
+  - set: { esql.queries.rest.failed: rest_failed_counter }
+  - set: { esql.queries.kibana.total: kibana_total_counter }
+  - set: { esql.queries.kibana.failed: kibana_failed_counter }
+  - set: { esql.queries._all.total: all_total_counter }
+  - set: { esql.queries._all.failed: all_failed_counter }
+  - set: { esql.functions.max: functions_max }
+  - set: { esql.functions.min: functions_min }
+  - set: { esql.functions.cos: functions_cos }
+  - set: { esql.functions.to_long: functions_to_long }
+  - set: { esql.functions.coalesce: functions_coalesce }
+  - set: { esql.functions.categorize: functions_categorize }
 
   - do:
       esql.query:
@@ -99,93 +100,95 @@ setup:
             | limit 5
             | stats m = max(data) by categorize(message)'
 
-  - do: {xpack.usage: {}}
+  - do: { xpack.usage: { } }
   - match: { esql.available: true }
   - match: { esql.enabled: true }
-  - match: {esql.features.dissect: $dissect_counter}
-  - match: {esql.features.eval: $eval_counter}
-  - match: {esql.features.grok: $grok_counter}
-  - gt: {esql.features.limit: $limit_counter}
-  - gt: {esql.features.sort: $sort_counter}
-  - gt: {esql.features.stats: $stats_counter}
-  - gt: {esql.features.where: $where_counter}
-  - match: {esql.features.lookup_join: $lookup_join_counter}
-  - match: {esql.features.lookup: $lookup_counter}
-  - match: {esql.features.change_point: $change_point_counter}
-  - match: {esql.features.inline_stats: $inline_stats_counter}
-  - match: {esql.features.rerank: $rerank_counter}
-  - match: {esql.features.insist: $insist_counter}
-  - match: {esql.features.fork: $fork_counter}
-  - match: {esql.features.fuse: $fuse_counter}
-  - match: {esql.features.completion: $completion_counter}
-  - match: {esql.features.sample: $sample_counter}
-  - gt: {esql.queries.rest.total: $rest_total_counter}
-  - match: {esql.queries.rest.failed: $rest_failed_counter}
-  - match: {esql.queries.kibana.total: $kibana_total_counter}
-  - match: {esql.queries.kibana.failed: $kibana_failed_counter}
-  - gt: {esql.queries._all.total: $all_total_counter}
-  - match: {esql.queries._all.failed: $all_failed_counter}
-  - gt: {esql.functions.max: $functions_max}
-  - match: {esql.functions.min: $functions_min}
-  - match: {esql.functions.cos: $functions_cos}
-  - gt: {esql.functions.to_long: $functions_to_long}
-  - match: {esql.functions.coalesce: $functions_coalesce}
-  - gt: {esql.functions.categorize: $functions_categorize}
+  - match: { esql.features.dissect: $dissect_counter }
+  - match: { esql.features.eval: $eval_counter }
+  - match: { esql.features.grok: $grok_counter }
+  - gt: { esql.features.limit: $limit_counter }
+  - gt: { esql.features.sort: $sort_counter }
+  - gt: { esql.features.stats: $stats_counter }
+  - gt: { esql.features.where: $where_counter }
+  - match: { esql.features.lookup_join: $lookup_join_counter }
+  - match: { esql.features.lookup_join_on_expression: $lookup_join_on_expression_counter }
+  - match: { esql.features.lookup: $lookup_counter }
+  - match: { esql.features.change_point: $change_point_counter }
+  - match: { esql.features.inline_stats: $inline_stats_counter }
+  - match: { esql.features.rerank: $rerank_counter }
+  - match: { esql.features.insist: $insist_counter }
+  - match: { esql.features.fork: $fork_counter }
+  - match: { esql.features.fuse: $fuse_counter }
+  - match: { esql.features.completion: $completion_counter }
+  - match: { esql.features.sample: $sample_counter }
+  - gt: { esql.queries.rest.total: $rest_total_counter }
+  - match: { esql.queries.rest.failed: $rest_failed_counter }
+  - match: { esql.queries.kibana.total: $kibana_total_counter }
+  - match: { esql.queries.kibana.failed: $kibana_failed_counter }
+  - gt: { esql.queries._all.total: $all_total_counter }
+  - match: { esql.queries._all.failed: $all_failed_counter }
+  - gt: { esql.functions.max: $functions_max }
+  - match: { esql.functions.min: $functions_min }
+  - match: { esql.functions.cos: $functions_cos }
+  - gt: { esql.functions.to_long: $functions_to_long }
+  - match: { esql.functions.coalesce: $functions_coalesce }
+  - gt: { esql.functions.categorize: $functions_categorize }
   # Testing for the entire function set isn't feasible, so we just check that we return the correct count as an approximation.
   # - length: {esql.functions: 179} # check the "sister" test below for a likely update to the same esql.functions length check
 ---
 "Basic ESQL usage output (telemetry) non-snapshot version":
   - requires:
-      test_runner_features: [capabilities]
+      test_runner_features: [ capabilities ]
       capabilities:
         - method: POST
           path: /_query
-          parameters: []
+          parameters: [ ]
           capabilities: [ non_snapshot_test_for_telemetry_v2, fn_contains ]
       reason: "Test that should only be executed on release versions"
 
-  - do: {xpack.usage: {}}
+  - do: { xpack.usage: { } }
   - match: { esql.available: true }
   - match: { esql.enabled: true }
-  - length: { esql.features: 26 }
-  - set: {esql.features.dissect: dissect_counter}
-  - set: {esql.features.drop: drop_counter}
-  - set: {esql.features.eval: eval_counter}
-  - set: {esql.features.enrich: enrich_counter}
-  - set: {esql.features.from: from_counter}
-  - set: {esql.features.grok: grok_counter}
-  - set: {esql.features.keep: keep_counter}
-  - set: {esql.features.limit: limit_counter}
-  - set: {esql.features.mv_expand: mv_expand_counter}
-  - set: {esql.features.rename: rename_counter}
-  - set: {esql.features.row: row_counter}
-  - set: {esql.features.show: show_counter}
-  - set: {esql.features.sort: sort_counter}
-  - set: {esql.features.stats: stats_counter}
-  - set: {esql.features.where: where_counter}
-  - set: {esql.features.lookup_join: lookup_join_counter}
-  - set: {esql.features.lookup: lookup_counter}
-  - set: {esql.features.change_point: change_point_counter}
-  - set: {esql.features.inline_stats: inline_stats_counter}
-  - set: {esql.features.rerank: rerank_counter}
-  - set: {esql.features.insist: insist_counter}
-  - set: {esql.features.fork: fork_counter}
-  - set: {esql.features.fuse: fuse_counter}
-  - set: {esql.features.completion: completion_counter}
-  - set: {esql.features.sample: sample_counter}
+  - length: { esql.features: 27 }
+  - set: { esql.features.dissect: dissect_counter }
+  - set: { esql.features.drop: drop_counter }
+  - set: { esql.features.eval: eval_counter }
+  - set: { esql.features.enrich: enrich_counter }
+  - set: { esql.features.from: from_counter }
+  - set: { esql.features.grok: grok_counter }
+  - set: { esql.features.keep: keep_counter }
+  - set: { esql.features.limit: limit_counter }
+  - set: { esql.features.mv_expand: mv_expand_counter }
+  - set: { esql.features.rename: rename_counter }
+  - set: { esql.features.row: row_counter }
+  - set: { esql.features.show: show_counter }
+  - set: { esql.features.sort: sort_counter }
+  - set: { esql.features.stats: stats_counter }
+  - set: { esql.features.where: where_counter }
+  - set: { esql.features.lookup_join: lookup_join_counter }
+  - set: { esql.features.lookup_join_on_expression: lookup_join_on_expression_counter }
+  - set: { esql.features.lookup: lookup_counter }
+  - set: { esql.features.change_point: change_point_counter }
+  - set: { esql.features.inline_stats: inline_stats_counter }
+  - set: { esql.features.rerank: rerank_counter }
+  - set: { esql.features.insist: insist_counter }
+  - set: { esql.features.fork: fork_counter }
+  - set: { esql.features.fuse: fuse_counter }
+  - set: { esql.features.completion: completion_counter }
+  - set: { esql.features.sample: sample_counter }
   - length: { esql.queries: 3 }
-  - set: {esql.queries.rest.total: rest_total_counter}
-  - set: {esql.queries.rest.failed: rest_failed_counter}
-  - set: {esql.queries.kibana.total: kibana_total_counter}
-  - set: {esql.queries.kibana.failed: kibana_failed_counter}
-  - set: {esql.queries._all.total: all_total_counter}
-  - set: {esql.queries._all.failed: all_failed_counter}
-  - set: {esql.functions.max: functions_max}
-  - set: {esql.functions.min: functions_min}
-  - set: {esql.functions.cos: functions_cos}
-  - set: {esql.functions.to_long: functions_to_long}
-  - set: {esql.functions.coalesce: functions_coalesce}
-  - set: {esql.functions.categorize: functions_categorize}
+  - set: { esql.queries.rest.total: rest_total_counter }
+  - set: { esql.queries.rest.failed: rest_failed_counter }
+  - set: { esql.queries.kibana.total: kibana_total_counter }
+  - set: { esql.queries.kibana.failed: kibana_failed_counter }
+  - set: { esql.queries._all.total: all_total_counter }
+  - set: { esql.queries._all.failed: all_failed_counter }
+  - set: { esql.functions.max: functions_max }
+  - set: { esql.functions.min: functions_min }
+  - set: { esql.functions.cos: functions_cos }
+  - set: { esql.functions.to_long: functions_to_long }
+  - set: { esql.functions.coalesce: functions_coalesce }
+  - set: { esql.functions.categorize: functions_categorize }
 
   - do:
       esql.query:
@@ -196,51 +199,52 @@ setup:
             | limit 5
             | stats m = max(data) by categorize(message)'
 
-  - do: {xpack.usage: {}}
+  - do: { xpack.usage: { } }
   - match: { esql.available: true }
   - match: { esql.enabled: true }
-  - match: {esql.features.dissect: $dissect_counter}
-  - match: {esql.features.eval: $eval_counter}
-  - match: {esql.features.grok: $grok_counter}
-  - gt: {esql.features.limit: $limit_counter}
-  - gt: {esql.features.sort: $sort_counter}
-  - gt: {esql.features.stats: $stats_counter}
-  - gt: {esql.features.where: $where_counter}
-  - match: {esql.features.lookup_join: $lookup_join_counter}
-  - match: {esql.features.lookup: $lookup_counter}
-  - match: {esql.features.change_point: $change_point_counter}
-  - match: {esql.features.inline_stats: $inline_stats_counter}
-  - match: {esql.features.rerank: $rerank_counter}
-  - match: {esql.features.insist: $insist_counter}
-  - match: {esql.features.fork: $fork_counter}
-  - match: {esql.features.fuse: $fuse_counter}
-  - match: {esql.features.completion: $completion_counter}
-  - gt: {esql.queries.rest.total: $rest_total_counter}
-  - match: {esql.queries.rest.failed: $rest_failed_counter}
-  - match: {esql.queries.kibana.total: $kibana_total_counter}
-  - match: {esql.queries.kibana.failed: $kibana_failed_counter}
-  - gt: {esql.queries._all.total: $all_total_counter}
-  - match: {esql.queries._all.failed: $all_failed_counter}
-  - gt: {esql.functions.max: $functions_max}
-  - match: {esql.functions.min: $functions_min}
-  - match: {esql.functions.cos: $functions_cos}
-  - gt: {esql.functions.to_long: $functions_to_long}
-  - match: {esql.functions.coalesce: $functions_coalesce}
-  - gt: {esql.functions.categorize: $functions_categorize}
+  - match: { esql.features.dissect: $dissect_counter }
+  - match: { esql.features.eval: $eval_counter }
+  - match: { esql.features.grok: $grok_counter }
+  - gt: { esql.features.limit: $limit_counter }
+  - gt: { esql.features.sort: $sort_counter }
+  - gt: { esql.features.stats: $stats_counter }
+  - gt: { esql.features.where: $where_counter }
+  - match: { esql.features.lookup_join: $lookup_join_counter }
+  - match: { esql.features.lookup_join_on_expression: $lookup_join_on_expression_counter }
+  - match: { esql.features.lookup: $lookup_counter }
+  - match: { esql.features.change_point: $change_point_counter }
+  - match: { esql.features.inline_stats: $inline_stats_counter }
+  - match: { esql.features.rerank: $rerank_counter }
+  - match: { esql.features.insist: $insist_counter }
+  - match: { esql.features.fork: $fork_counter }
+  - match: { esql.features.fuse: $fuse_counter }
+  - match: { esql.features.completion: $completion_counter }
+  - gt: { esql.queries.rest.total: $rest_total_counter }
+  - match: { esql.queries.rest.failed: $rest_failed_counter }
+  - match: { esql.queries.kibana.total: $kibana_total_counter }
+  - match: { esql.queries.kibana.failed: $kibana_failed_counter }
+  - gt: { esql.queries._all.total: $all_total_counter }
+  - match: { esql.queries._all.failed: $all_failed_counter }
+  - gt: { esql.functions.max: $functions_max }
+  - match: { esql.functions.min: $functions_min }
+  - match: { esql.functions.cos: $functions_cos }
+  - gt: { esql.functions.to_long: $functions_to_long }
+  - match: { esql.functions.coalesce: $functions_coalesce }
+  - gt: { esql.functions.categorize: $functions_categorize }
   # - length: {esql.functions: 151} # check the "sister" test above for a likely update to the same esql.functions length check
 
 ---
 took:
   - requires:
-      test_runner_features: [capabilities]
+      test_runner_features: [ capabilities ]
       capabilities:
         - method: POST
           path: /_query
-          parameters: []
-          capabilities: [usage_contains_took]
+          parameters: [ ]
+          capabilities: [ usage_contains_took ]
       reason: "Test that should only be executed on snapshot versions"
 
-  - do: {xpack.usage: {}}
+  - do: { xpack.usage: { } }
   - exists: esql.took.lt_10ms
   - exists: esql.took.lt_1s
   - exists: esql.took.lt_10s