From 1c3ddc78626d12ae7659ba27b26b2fb6450649f4 Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 11:07:05 +0100 Subject: [PATCH 1/8] Remove all warnings from DocsV3Support --- .../expression/function/DocsV3Support.java | 97 +++++++++++++------ 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java index 8181281d04f2f..01c6f65c94fc0 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java @@ -72,6 +72,21 @@ import static org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry.param; import static org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry.paramWithoutAnnotation; +/** + * This class exists to support the new Docs V3 system. + * Between Elasticsearch 8.x and 9.0 the reference documents were completely re-written, with several key changes: + *
    + *
  1. Port from ASCIIDOC to MD (markdown)
  2. + *
  3. Restructures all Elastic docs with clearer separate between reference docs and other docs
  4. + *
  5. All versions published from the main branch, + * requiring version specific information to be included in the docs as appropriate
  6. + *
  7. Including sub-docs inside bigger docs works differently, requiring a new directory structure
  8. + *
  9. Images and Kibana docs cannot be in the same location as snippets
  10. + *
+ * + * For these reasons the docs generating code that used to live inside AbstractFunctionTestCase has been pulled out + * and partially re-written to satisfy the above requirements. + */ public abstract class DocsV3Support { static final String DOCS_WARNING = @@ -190,6 +205,7 @@ public static void renderNegatedOperator( operatorEntry("match_operator", ":", MatchOperator.class, OperatorCategory.SEARCH) ); + /** Each grouping represents a subsection in the docs. Currently, this is manually maintained, but could be partially automated */ public enum OperatorCategory { BINARY, UNARY, @@ -200,6 +216,7 @@ public enum OperatorCategory { SEARCH } + /** Since operators do not exist in the function registry, we need an equivalent registry here in the docs generating code */ public record OperatorConfig(String name, String symbol, Class clazz, OperatorCategory category, boolean variadic) {} private static Map.Entry operatorEntry( @@ -213,7 +230,7 @@ private static Map.Entry operatorEntry( } private static Map.Entry operatorEntry(String name, String symbol, Class clazz, OperatorCategory category) { - return entry(name, new OperatorConfig(name, symbol, clazz, category, false)); + return operatorEntry(name, symbol, clazz, category, false); } protected final String category; @@ -235,7 +252,7 @@ String replaceLinks(String text) { private String replaceAsciidocLinks(String text) { Pattern pattern = Pattern.compile("<<([^>]*)>>"); Matcher matcher = pattern.matcher(text); - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); while (matcher.find()) { String match = matcher.group(1); matcher.appendReplacement(result, getLink(match)); @@ -245,10 +262,10 @@ private String replaceAsciidocLinks(String text) { } private String replaceMacros(String text) { - Pattern pattern = Pattern.compile("\\{([^}]+)}(/[^\\[]+)\\[([^]]+)\\]"); + Pattern pattern = Pattern.compile("\\{([^}]+)}(/[^\\[]+)\\[([^]]+)]"); Matcher matcher = pattern.matcher(text); - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); while (matcher.find()) { String macro = matcher.group(1); String path = matcher.group(2); @@ -385,9 +402,9 @@ private FunctionDocsSupport(String name, Class testClass) { protected void renderSignature() throws IOException { String rendered = buildFunctionSignatureSvg(); if (rendered == null) { - logger.info("Skipping rendering signature because the function isn't registered"); + logger.info("Skipping rendering signature because the function '{}' isn't registered", name); } else { - logger.info("Writing function signature"); + logger.info("Writing function signature: {}", name); writeToTempImageDir(rendered); } } @@ -395,6 +412,15 @@ protected void renderSignature() throws IOException { @Override protected void renderDocs() throws IOException { FunctionDefinition definition = definition(name); + if (definition == null) { + logger.info("Skipping rendering docs because the function '{}' isn't registered", name); + } else { + logger.info("Rendering function docs: {}", name); + renderDocs(definition); + } + } + + private void renderDocs(FunctionDefinition definition) throws IOException { EsqlFunctionRegistry.FunctionDescription description = EsqlFunctionRegistry.description(definition); if (name.equals("case")) { /* @@ -559,6 +585,7 @@ private String addInclude(String section) { } } + /** Operator specific docs generating, since it is currently quite different from the function docs generating */ public static class OperatorsDocsSupport extends DocsV3Support { private final OperatorConfig op; @@ -750,21 +777,7 @@ void renderTypes(String name, List args) thro if (sig.getKey().size() > argNames.size()) { // skip variadic [test] cases (but not those with optional parameters) continue; } - StringBuilder b = new StringBuilder("| "); - for (int i = 0; i < sig.getKey().size(); i++) { - DataType argType = sig.getKey().get(i); - EsqlFunctionRegistry.ArgSignature argSignature = args.get(i); - if (argSignature.mapArg()) { - b.append("named parameters"); - } else { - b.append(argType.esNameIfPossible()); - } - b.append(" | "); - } - b.append("| ".repeat(argNames.size() - sig.getKey().size())); - b.append(sig.getValue().esNameIfPossible()); - b.append(" |"); - table.add(b.toString()); + table.add(getTypeRow(args, sig, argNames)); } Collections.sort(table); if (table.isEmpty()) { @@ -775,11 +788,33 @@ void renderTypes(String name, List args) thro String rendered = DOCS_WARNING + """ **Supported types** - """ + header + "\n" + separator + "\n" + table.stream().collect(Collectors.joining("\n")) + "\n\n"; + """ + header + "\n" + separator + "\n" + String.join("\n", table) + "\n\n"; logger.info("Writing function types for [{}]:\n{}", name, rendered); writeToTempSnippetsDir("types", rendered); } + private static String getTypeRow( + List args, + Map.Entry, DataType> sig, + List argNames + ) { + StringBuilder b = new StringBuilder("| "); + for (int i = 0; i < sig.getKey().size(); i++) { + DataType argType = sig.getKey().get(i); + EsqlFunctionRegistry.ArgSignature argSignature = args.get(i); + if (argSignature.mapArg()) { + b.append("named parameters"); + } else { + b.append(argType.esNameIfPossible()); + } + b.append(" | "); + } + b.append("| ".repeat(argNames.size() - sig.getKey().size())); + b.append(sig.getValue().esNameIfPossible()); + b.append(" |"); + return b.toString(); + } + void renderDescription(String description, String detailedDescription, String note) throws IOException { description = replaceLinks(description.trim()); note = replaceLinks(note); @@ -813,14 +848,14 @@ protected boolean renderExamples(FunctionInfo info) throws IOException { builder.append("**Examples**\n\n"); } for (Example example : info.examples()) { - if (example.description().length() > 0) { + if (example.description().isEmpty() == false) { builder.append(replaceLinks(example.description().trim())); builder.append("\n\n"); } String exampleQuery = loadExampleQuery(example); String exampleResult = loadExampleResult(example); builder.append(exampleQuery).append("\n").append(exampleResult).append("\n"); - if (example.explanation().length() > 0) { + if (example.explanation().isEmpty() == false) { builder.append("\n"); builder.append(replaceLinks(example.explanation().trim())); builder.append("\n\n"); @@ -954,12 +989,12 @@ void renderKibanaFunctionDefinition(String name, FunctionInfo info, List, DataType>> sortedSignatures() { List, DataType>> sortedSignatures = new ArrayList<>(signatures.get().entrySet()); - Collections.sort(sortedSignatures, (lhs, rhs) -> { + sortedSignatures.sort((lhs, rhs) -> { int maxlen = Math.max(lhs.getKey().size(), rhs.getKey().size()); for (int i = 0; i < maxlen; i++) { if (lhs.getKey().size() <= i) { @@ -990,7 +1025,7 @@ protected boolean renderAppendix(String appendix) throws IOException { return true; } - private HashMap> examples = new HashMap<>(); + private final HashMap> examples = new HashMap<>(); protected String loadExampleQuery(Example example) throws IOException { return "```esql\n" + loadExample(example.file(), example.tag()) + "\n```\n"; @@ -1040,7 +1075,7 @@ protected Map loadExampleFile(String csvSpec) throws IOException currentLines = null; currentTag = null; } - } else if (currentTag != null) { + } else if (currentTag != null && currentLines != null) { currentLines.add(line); // Collect lines within the block } } @@ -1050,7 +1085,7 @@ protected Map loadExampleFile(String csvSpec) throws IOException protected String reformatExample(String tag, List lines) { if (tag.endsWith("-result")) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (String line : lines) { sb.append(renderTableLine(line, sb.isEmpty())); } @@ -1070,7 +1105,7 @@ private String renderTableLine(String line, boolean header) { } private String renderTableLine(String[] columns) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append("| "); for (int i = 0; i < columns.length; i++) { if (i > 0) { @@ -1083,7 +1118,7 @@ private String renderTableLine(String[] columns) { } private String renderTableSpacerLine(int columns) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append("| "); for (int i = 0; i < columns; i++) { if (i > 0) { From 338ad98dc9ca5e40d59f111fc45718d873f74442 Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 12:36:11 +0100 Subject: [PATCH 2/8] Split applies_to from preview warning --- .../esql/_snippets/functions/layout/match.md | 5 +- .../esql/_snippets/functions/layout/qstr.md | 5 +- .../_snippets/functions/layout/to_lower.md | 1 - .../_snippets/functions/layout/to_upper.md | 1 - .../esql/_snippets/functions/layout/values.md | 3 +- .../expression/function/aggregate/Values.java | 5 +- .../expression/function/DocsV3Support.java | 85 ++++++++--- .../function/DocsV3SupportTests.java | 133 +++++++++++++++++- 8 files changed, 201 insertions(+), 37 deletions(-) diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/match.md b/docs/reference/query-languages/esql/_snippets/functions/layout/match.md index 82d861909abfd..39ee6fe64b4b7 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/match.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/match.md @@ -2,12 +2,13 @@ ## `MATCH` [esql-match] :::{warning} -###### COMING 9.1.0 - Do not use on production environments. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. +::: +:::{note} +###### COMING 9.1.0 Support for optional named parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md b/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md index 19f6dda64ef1d..2a1362f966255 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md @@ -2,12 +2,13 @@ ## `QSTR` [esql-qstr] :::{warning} -###### COMING 9.1.0 - Do not use on production environments. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. +::: +:::{note} +###### COMING 9.1.0 Support for optional named parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md b/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md index 0327f8a7579e1..ce8eb77640426 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md @@ -3,7 +3,6 @@ ## `TO_LOWER` [esql-to_lower] :::{note} ###### COMING 9.1.0 - Support for multivalued parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md b/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md index bafcb218a54c9..e27bfddd65512 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md @@ -3,7 +3,6 @@ ## `TO_UPPER` [esql-to_upper] :::{note} ###### COMING 9.1.0 - Support for multivalued parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/values.md b/docs/reference/query-languages/esql/_snippets/functions/layout/values.md index 9cc069b0c4f58..0a2e9c2d5adc4 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/values.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/values.md @@ -2,13 +2,12 @@ ## `VALUES` [esql-values] :::{warning} -###### PREVIEW - Do not use on production environments. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. ::: + **Syntax** :::{image} ../../../images/functions/values.svg diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java index 7cda030d86039..5e05c3a448295 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java @@ -23,8 +23,6 @@ import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; import org.elasticsearch.xpack.esql.expression.function.Example; -import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; -import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.FunctionType; import org.elasticsearch.xpack.esql.expression.function.Param; @@ -86,8 +84,7 @@ public class Values extends AggregateFunction implements ToAggregator { a [Circuit Breaker Error](docs-content://troubleshoot/elasticsearch/circuit-breaker-errors.md). ::::""", type = FunctionType.AGGREGATE, - examples = @Example(file = "string", tag = "values-grouped"), - appliesTo = { @FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.PREVIEW) } + examples = @Example(file = "string", tag = "values-grouped") ) public Values( Source source, diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java index 01c6f65c94fc0..ae99fee0cf69d 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java @@ -233,16 +233,50 @@ private static Map.Entry operatorEntry(String name, Stri return operatorEntry(name, symbol, clazz, category, false); } + @FunctionalInterface + interface TempFileWriter { + void writeToTempDir(Path dir, String extension, String str) throws IOException; + } + + private class DocsFileWriter implements TempFileWriter { + @Override + public void writeToTempDir(Path dir, String extension, String str) throws IOException { + Files.createDirectories(dir); + Path file = dir.resolve(name + "." + extension); + Files.writeString(file, str); + logger.info("Wrote to file: {}", file); + } + } + protected final String category; protected final String name; + protected final FunctionDefinition definition; protected final Logger logger; private final Supplier, DataType>> signatures; + private TempFileWriter tempFileWriter; private DocsV3Support(String category, String name, Class testClass, Supplier, DataType>> signatures) { + this(category, name, definition(name), testClass, signatures); + } + + private DocsV3Support( + String category, + String name, + FunctionDefinition definition, + Class testClass, + Supplier, DataType>> signatures + ) { this.category = category; this.name = name; + this.definition = definition == null ? definition(name) : definition; this.logger = LogManager.getLogger(testClass); this.signatures = signatures; + this.tempFileWriter = new DocsFileWriter(); + } + + /** Used in tests to capture output for asserting on the content */ + void setTempFileWriter(TempFileWriter tempFileWriter) { + this.tempFileWriter = tempFileWriter; } String replaceLinks(String text) { @@ -363,7 +397,7 @@ private String makeLink(String key, String prefix, String parentFile) { void writeToTempImageDir(String str) throws IOException { // We have to write to a tempdir because it’s all test are allowed to write to. Gradle can move them. Path dir = PathUtils.get(System.getProperty("java.io.tmpdir")).resolve("esql").resolve("images").resolve(category); - writeToTempDir(dir, "svg", str); + tempFileWriter.writeToTempDir(dir, "svg", str); } void writeToTempSnippetsDir(String subdir, String str) throws IOException { @@ -373,20 +407,13 @@ void writeToTempSnippetsDir(String subdir, String str) throws IOException { .resolve("_snippets") .resolve(category) .resolve(subdir); - writeToTempDir(dir, "md", str); + tempFileWriter.writeToTempDir(dir, "md", str); } void writeToTempKibanaDir(String subdir, String extension, String str) throws IOException { // We have to write to a tempdir because it’s all test are allowed to write to. Gradle can move them. Path dir = PathUtils.get(System.getProperty("java.io.tmpdir")).resolve("esql").resolve("kibana").resolve(subdir).resolve(category); - writeToTempDir(dir, extension, str); - } - - private void writeToTempDir(Path dir, String extension, String str) throws IOException { - Files.createDirectories(dir); - Path file = dir.resolve(name + "." + extension); - Files.writeString(file, str); - logger.info("Wrote to file: {}", file); + tempFileWriter.writeToTempDir(dir, extension, str); } protected abstract void renderSignature() throws IOException; @@ -398,6 +425,15 @@ private FunctionDocsSupport(String name, Class testClass) { super("functions", name, testClass, () -> AbstractFunctionTestCase.signatures(testClass)); } + FunctionDocsSupport( + String name, + Class testClass, + FunctionDefinition definition, + Supplier, DataType>> signatures + ) { + super("functions", name, definition, testClass, signatures); + } + @Override protected void renderSignature() throws IOException { String rendered = buildFunctionSignatureSvg(); @@ -411,7 +447,6 @@ protected void renderSignature() throws IOException { @Override protected void renderDocs() throws IOException { - FunctionDefinition definition = definition(name); if (definition == null) { logger.info("Skipping rendering docs because the function '{}' isn't registered", name); } else { @@ -486,22 +521,16 @@ private String makeCallout(String type, String text) { } private String makePreviewText(boolean preview, FunctionAppliesTo[] functionAppliesTos) { - StringBuilder previewDescription = new StringBuilder(); - for (FunctionAppliesTo appliesTo : functionAppliesTos) { - if (appliesTo.description().isEmpty() == false) { - previewDescription.append(appliesTo.description()).append("\n"); - } - preview = preview || appliesTo.lifeCycle() == FunctionAppliesToLifecycle.PREVIEW; - } String appliesToTextWithAT = appliesToText(functionAppliesTos); String appliesToText = appliesToTextWithoutAppliesTo(functionAppliesTos); StringBuilder previewText = new StringBuilder(); if (preview) { // We have a preview flag, use the WARNING callout - previewText.append(makeCallout("warning", appliesToText + "\n" + PREVIEW_CALLOUT + "\n" + previewDescription + "\n")); - } else if (previewDescription.isEmpty() == false) { + previewText.append(makeCallout("warning", "\n" + PREVIEW_CALLOUT + "\n")).append("\n"); + } + if (appliesToText.isEmpty() == false) { // We have extra descriptive text, nest inside a NOTE for emphasis - previewText.append(makeCallout("note", appliesToText + "\n" + previewDescription)); + previewText.append(makeCallout("note", appliesToText)); } else if (appliesToTextWithAT.isEmpty() == false) { // No additional text, just use the plan applies_to syntax previewText.append(appliesToTextWithAT); @@ -514,6 +543,10 @@ private String appliesToText(FunctionAppliesTo[] functionAppliesTos) { if (functionAppliesTos.length > 0) { appliesToText.append("```{applies_to}\n"); for (FunctionAppliesTo appliesTo : functionAppliesTos) { + if (appliesTo.description().isEmpty() == false) { + // If any of the appliesTo has descriptive text, we need to format things differently + return ""; + } appliesToText.append("product: ") .append(appliesTo.lifeCycle().name()) .append(" ") @@ -531,7 +564,14 @@ private String appliesToTextWithoutAppliesTo(FunctionAppliesTo[] functionApplies appliesToText.append("\n"); for (FunctionAppliesTo appliesTo : functionAppliesTos) { appliesToText.append("###### "); - appliesToText.append(appliesTo.lifeCycle().name()).append(" ").append(appliesTo.version()).append("\n"); + appliesToText.append(appliesTo.lifeCycle().name()); + if (appliesTo.version().isEmpty() == false) { + appliesToText.append(" ").append(appliesTo.version()); + } + appliesToText.append("\n"); + if (appliesTo.description().isEmpty() == false) { + appliesToText.append(appliesTo.description()).append("\n\n"); + } } } return appliesToText.toString(); @@ -740,7 +780,6 @@ void renderDetailedDescription(String detailedDescription, String note) throws I } protected String buildFunctionSignatureSvg() throws IOException { - FunctionDefinition definition = definition(name); return (definition != null) ? RailRoadDiagram.functionSignature(definition) : null; } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java index e17127b98b0bc..ef03cf08f3739 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java @@ -7,10 +7,21 @@ package org.elasticsearch.xpack.esql.expression.function; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.function.Function; +import org.elasticsearch.xpack.esql.core.tree.NodeInfo; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.session.Configuration; import java.io.IOException; import java.lang.reflect.Constructor; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static org.hamcrest.Matchers.equalTo; @@ -278,6 +289,75 @@ public void testRenderingExampleFromClass() throws IOException { assertThat(results.toString(), equalTo(expected)); } + public void testRenderingLayoutFromClass() throws IOException { + String expected = """ + % This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + + ## `COUNT` [esql-count] + :::{warning} + Do not use on production environments. This functionality is in technical preview and + may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview + are not subject to the support SLA of official GA features. + ::: + + :::{note} + ###### COMING 9.1.0 + Support for optional named parameters is only available from 9.1.0 + + ###### DEVELOPMENT + The ability to generate more imaginative answers to the question is under development + + ###### DISCONTINUED 9.0.0 + The ability to count the number of emojis in a string has been discontinued since 9.0.0 + ::: + + **Syntax** + + :::{image} ../../../images/functions/count.svg + :alt: Embedded + :class: text-center + ::: + + + :::{include} ../parameters/count.md + ::: + + :::{include} ../description/count.md + ::: + + :::{include} ../types/count.md + ::: + + :::{include} ../examples/count.md + ::: + """; + FunctionInfo info = functionInfo(TestClass.class); + assert info != null; + FunctionDefinition definition = EsqlFunctionRegistry.def(TestClass.class, TestClass::new, "count"); + var docs = new DocsV3Support.FunctionDocsSupport("count", TestClass.class, definition, TestClass::signatures); + TestDocsFileWriter tempFileWriter = new TestDocsFileWriter("count"); + docs.setTempFileWriter(tempFileWriter); + docs.renderDocs(); + String rendered = tempFileWriter.rendered.get("layout/count.md"); + assertThat(rendered, equalTo(expected)); + } + + private class TestDocsFileWriter implements DocsV3Support.TempFileWriter { + private final String name; + private final Map rendered = new HashMap<>(); + + TestDocsFileWriter(String name) { + this.name = name; + } + + @Override + public void writeToTempDir(Path dir, String extension, String str) throws IOException { + String file = dir.getFileName() + "/" + name + "." + extension; + rendered.put(file, str); + logger.info("Wrote to file: {}", file); + } + } + private static FunctionInfo functionInfo(Class clazz) { Constructor constructor = constructorFor(clazz); if (constructor == null) { @@ -301,9 +381,10 @@ private static Constructor constructorFor(Class clazz) { return constructors[0]; } - public static class TestClass { + public static class TestClass extends Function { @FunctionInfo( returnType = "long", + preview = true, description = "Returns the total number (count) of input values.", type = FunctionType.AGGREGATE, examples = { @@ -325,8 +406,56 @@ public static class TestClass { `NULL`s: `COUNT(TRUE)` and `COUNT(FALSE)` are both 1, but `COUNT(NULL)` is 0.""", file = "stats", tag = "count-or-null" + ) }, + appliesTo = { + @FunctionAppliesTo( + lifeCycle = FunctionAppliesToLifecycle.COMING, + version = "9.1.0", + description = "Support for optional named parameters is only available from 9.1.0" + ), + @FunctionAppliesTo( + lifeCycle = FunctionAppliesToLifecycle.DEVELOPMENT, + description = "The ability to generate more imaginative answers to the question is under development" + ), + @FunctionAppliesTo( + lifeCycle = FunctionAppliesToLifecycle.DISCONTINUED, + version = "9.0.0", + description = "The ability to count the number of emojis in a string has been discontinued since 9.0.0" ) } ) - public TestClass() {} + public TestClass(Source source, @Param(name = "str", type = { "keyword", "text" }, description = """ + String expression. If `null`, the function returns `null`. + The input can be a single- or multi-valued column or an expression.""") Expression field, Configuration configuration) { + super(source, List.of(field)); + } + + public static Map, DataType> signatures() { + return Map.of(List.of(DataType.KEYWORD), DataType.LONG); + } + + @Override + public DataType dataType() { + return DataType.LONG; + } + + @Override + public Expression replaceChildren(List newChildren) { + return null; + } + + @Override + protected NodeInfo info() { + return null; + } + + @Override + public String getWriteableName() { + return "count-or-null"; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + + } } } From 0613bfd89760a5dd15023d14497bdad306198484 Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 12:47:00 +0100 Subject: [PATCH 3/8] Update examples test to use real examples rendering --- .../function/DocsV3SupportTests.java | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java index ef03cf08f3739..c8ffe44d90894 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java @@ -204,6 +204,9 @@ public void testRenderingExampleResultEmojis() throws IOException { public void testRenderingExampleFromClass() throws IOException { String expected = """ + % This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + + **Examples** ```esql FROM employees @@ -272,21 +275,9 @@ public void testRenderingExampleFromClass() throws IOException { | --- | --- | | 1 | 0 | """; - FunctionInfo info = functionInfo(TestClass.class); - assert info != null; - DocsV3Support docs = DocsV3Support.forFunctions("count", TestClass.class); - StringBuilder results = new StringBuilder(); - for (Example example : info.examples()) { - if (example.description().isEmpty() == false) { - results.append("\n"); - results.append(docs.replaceLinks(example.description().trim())); - results.append("\n"); - } - String query = docs.loadExampleQuery(example); - String result = docs.loadExampleResult(example); - results.append("\n").append(query).append("\n").append(result); - } - assertThat(results.toString(), equalTo(expected)); + TestDocsFileWriter tempFileWriter = renderTestClassDocs(); + String rendered = tempFileWriter.rendered.get("examples/count.md"); + assertThat(rendered.trim(), equalTo(expected.trim())); } public void testRenderingLayoutFromClass() throws IOException { @@ -331,6 +322,12 @@ public void testRenderingLayoutFromClass() throws IOException { :::{include} ../examples/count.md ::: """; + TestDocsFileWriter tempFileWriter = renderTestClassDocs(); + String rendered = tempFileWriter.rendered.get("layout/count.md"); + assertThat(rendered.trim(), equalTo(expected.trim())); + } + + private TestDocsFileWriter renderTestClassDocs() throws IOException { FunctionInfo info = functionInfo(TestClass.class); assert info != null; FunctionDefinition definition = EsqlFunctionRegistry.def(TestClass.class, TestClass::new, "count"); @@ -338,8 +335,7 @@ public void testRenderingLayoutFromClass() throws IOException { TestDocsFileWriter tempFileWriter = new TestDocsFileWriter("count"); docs.setTempFileWriter(tempFileWriter); docs.renderDocs(); - String rendered = tempFileWriter.rendered.get("layout/count.md"); - assertThat(rendered, equalTo(expected)); + return tempFileWriter; } private class TestDocsFileWriter implements DocsV3Support.TempFileWriter { From 6aa5510e5d16f3e34152f63f16e265cd54dd1f07 Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 14:58:36 +0100 Subject: [PATCH 4/8] Fix EsqlNodeSubclassTests failures Any class that extends Function needs to correctly implement replaceChildren and info() methods, because it will be found and tested! --- .../esql/expression/function/DocsV3SupportTests.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java index c8ffe44d90894..d0132a5339687 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java @@ -14,7 +14,6 @@ import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; -import org.elasticsearch.xpack.esql.session.Configuration; import java.io.IOException; import java.lang.reflect.Constructor; @@ -421,7 +420,7 @@ public static class TestClass extends Function { ) public TestClass(Source source, @Param(name = "str", type = { "keyword", "text" }, description = """ String expression. If `null`, the function returns `null`. - The input can be a single- or multi-valued column or an expression.""") Expression field, Configuration configuration) { + The input can be a single- or multi-valued column or an expression.""") Expression field) { super(source, List.of(field)); } @@ -436,22 +435,22 @@ public DataType dataType() { @Override public Expression replaceChildren(List newChildren) { - return null; + return new TestClass(source(), newChildren.getFirst()); } @Override protected NodeInfo info() { - return null; + return NodeInfo.create(this, TestClass::new, children().getFirst()); } @Override public String getWriteableName() { - return "count-or-null"; + throw new UnsupportedOperationException(); } @Override public void writeTo(StreamOutput out) throws IOException { - + throw new UnsupportedOperationException(); } } } From a979b813b1d6ddb4de44bc19d183c996839d6f97 Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 15:01:48 +0100 Subject: [PATCH 5/8] Update x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java Co-authored-by: Alexander Spies --- .../xpack/esql/expression/function/DocsV3Support.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java index ae99fee0cf69d..1d09d743b66e7 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java @@ -256,7 +256,7 @@ public void writeToTempDir(Path dir, String extension, String str) throws IOExce private TempFileWriter tempFileWriter; private DocsV3Support(String category, String name, Class testClass, Supplier, DataType>> signatures) { - this(category, name, definition(name), testClass, signatures); + this(category, name, null, testClass, signatures); } private DocsV3Support( From e283662ba1fee014140b6e36f6f397dd01f425ab Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 17:10:35 +0100 Subject: [PATCH 6/8] Refined applies_to, adding serverless and removing some version info --- .../_snippets/functions/layout/categorize.md | 1 + .../esql/_snippets/functions/layout/kql.md | 1 + .../esql/_snippets/functions/layout/match.md | 4 +-- .../esql/_snippets/functions/layout/qstr.md | 2 +- .../esql/_snippets/functions/layout/term.md | 1 + .../layout/to_aggregate_metric_double.md | 3 +- .../_snippets/functions/layout/to_lower.md | 2 +- .../_snippets/functions/layout/to_upper.md | 2 +- .../functions/to_aggregate_metric_double.md | 2 +- .../function/FunctionAppliesTo.java | 2 ++ .../function/FunctionAppliesToLifecycle.java | 26 ++++++++++----- .../expression/function/fulltext/Match.java | 3 +- .../convert/ToAggregateMetricDouble.java | 2 +- .../expression/function/DocsV3Support.java | 33 ++++++++++++------- .../function/DocsV3SupportTests.java | 2 +- 15 files changed, 55 insertions(+), 31 deletions(-) diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/categorize.md b/docs/reference/query-languages/esql/_snippets/functions/layout/categorize.md index f9d782d304bfa..53b9e2f09ae49 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/categorize.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/categorize.md @@ -7,6 +7,7 @@ may be changed or removed in a future release. Elastic will work to fix any issu are not subject to the support SLA of official GA features. ::: + **Syntax** :::{image} ../../../images/functions/categorize.svg diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/kql.md b/docs/reference/query-languages/esql/_snippets/functions/layout/kql.md index b9adaac7f3e50..04c50cfded194 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/kql.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/kql.md @@ -7,6 +7,7 @@ may be changed or removed in a future release. Elastic will work to fix any issu are not subject to the support SLA of official GA features. ::: + **Syntax** :::{image} ../../../images/functions/kql.svg diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/match.md b/docs/reference/query-languages/esql/_snippets/functions/layout/match.md index 39ee6fe64b4b7..e4277941d582e 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/match.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/match.md @@ -8,8 +8,8 @@ are not subject to the support SLA of official GA features. ::: :::{note} -###### COMING 9.1.0 -Support for optional named parameters is only available from 9.1.0 +###### Serverless: GA, Stateful: COMING +Support for optional named parameters is only available in serverless, or in a future {{es}} release ::: **Syntax** diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md b/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md index 2a1362f966255..c7e252c90b8d8 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md @@ -8,7 +8,7 @@ are not subject to the support SLA of official GA features. ::: :::{note} -###### COMING 9.1.0 +###### Serverless: GA, Stateful: COMING 9.1.0 Support for optional named parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/term.md b/docs/reference/query-languages/esql/_snippets/functions/layout/term.md index 64967e792a59c..85499b3bffdc3 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/term.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/term.md @@ -7,6 +7,7 @@ may be changed or removed in a future release. Elastic will work to fix any issu are not subject to the support SLA of official GA features. ::: + **Syntax** :::{image} ../../../images/functions/term.svg diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/to_aggregate_metric_double.md b/docs/reference/query-languages/esql/_snippets/functions/layout/to_aggregate_metric_double.md index 19e9867c5e5b2..9c62b3d63ded8 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/to_aggregate_metric_double.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/to_aggregate_metric_double.md @@ -2,7 +2,8 @@ ## `TO_AGGREGATE_METRIC_DOUBLE` [esql-to_aggregate_metric_double] ```{applies_to} -product: COMING 9.1 +product: COMING +serverless: GA ``` **Syntax** diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md b/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md index ce8eb77640426..3304633f5be90 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md @@ -2,7 +2,7 @@ ## `TO_LOWER` [esql-to_lower] :::{note} -###### COMING 9.1.0 +###### Serverless: GA, Stateful: COMING 9.1.0 Support for multivalued parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md b/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md index e27bfddd65512..386bac6d84387 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md @@ -2,7 +2,7 @@ ## `TO_UPPER` [esql-to_upper] :::{note} -###### COMING 9.1.0 +###### Serverless: GA, Stateful: COMING 9.1.0 Support for multivalued parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/to_aggregate_metric_double.md b/docs/reference/query-languages/esql/kibana/docs/functions/to_aggregate_metric_double.md index 569059d790b0e..d461121e036b2 100644 --- a/docs/reference/query-languages/esql/kibana/docs/functions/to_aggregate_metric_double.md +++ b/docs/reference/query-languages/esql/kibana/docs/functions/to_aggregate_metric_double.md @@ -5,7 +5,7 @@ This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../RE ### TO_AGGREGATE_METRIC_DOUBLE Encode a numeric to an aggregate_metric_double. -``` +```esql ROW x = 3892095203 | EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x) ``` diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesTo.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesTo.java index 8d9fae9761935..f8d5917314632 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesTo.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesTo.java @@ -17,4 +17,6 @@ String version() default ""; String description() default ""; + + boolean serverless() default true; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesToLifecycle.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesToLifecycle.java index 351c6d5b19fd7..5ad512e69c089 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesToLifecycle.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionAppliesToLifecycle.java @@ -8,12 +8,22 @@ package org.elasticsearch.xpack.esql.expression.function; public enum FunctionAppliesToLifecycle { - PREVIEW, - BETA, - DEVELOPMENT, - DEPRECATED, - COMING, - DISCONTINUED, - UNAVAILABLE, - GA + PREVIEW(true), + BETA(false), + DEVELOPMENT(false), + DEPRECATED(true), + COMING(true), + DISCONTINUED(false), + UNAVAILABLE(false), + GA(true); + + private final boolean serverless; + + FunctionAppliesToLifecycle(boolean serverless) { + this.serverless = serverless; + } + + public FunctionAppliesToLifecycle serverlessLifecycle() { + return serverless ? GA : this; + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java index 6e74ca7084d4a..901521f928c0e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java @@ -165,8 +165,7 @@ public class Match extends FullTextFunction implements OptionalArgument, PostAna appliesTo = { @FunctionAppliesTo( lifeCycle = FunctionAppliesToLifecycle.COMING, - version = "9.1.0", - description = "Support for optional named parameters is only available from 9.1.0" + description = "Support for optional named parameters is only available in serverless, or in a future {{es}} release" ) } ) public Match( diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToAggregateMetricDouble.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToAggregateMetricDouble.java index e6e2aa053ce22..23d4d895bb0fc 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToAggregateMetricDouble.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToAggregateMetricDouble.java @@ -71,7 +71,7 @@ public class ToAggregateMetricDouble extends AbstractConvertFunction { examples = { @Example(file = "convert", tag = "toAggregateMetricDouble"), @Example(description = "The expression also accepts multi-values", file = "convert", tag = "toAggregateMetricDoubleMv") }, - appliesTo = { @FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.COMING, version = "9.1") } + appliesTo = { @FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.COMING) } ) public ToAggregateMetricDouble( Source source, diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java index 1d09d743b66e7..684cc0525b2d5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java @@ -71,6 +71,7 @@ import static org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry.mapParam; import static org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry.param; import static org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry.paramWithoutAnnotation; +import static org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle.GA; /** * This class exists to support the new Docs V3 system. @@ -528,12 +529,12 @@ private String makePreviewText(boolean preview, FunctionAppliesTo[] functionAppl // We have a preview flag, use the WARNING callout previewText.append(makeCallout("warning", "\n" + PREVIEW_CALLOUT + "\n")).append("\n"); } - if (appliesToText.isEmpty() == false) { - // We have extra descriptive text, nest inside a NOTE for emphasis - previewText.append(makeCallout("note", appliesToText)); - } else if (appliesToTextWithAT.isEmpty() == false) { + if (appliesToTextWithAT.isEmpty() == false) { // No additional text, just use the plan applies_to syntax previewText.append(appliesToTextWithAT); + } else if (appliesToText.isEmpty() == false) { + // We have extra descriptive text, nest inside a NOTE for emphasis + previewText.append(makeCallout("note", appliesToText)); } return previewText.toString(); } @@ -547,11 +548,12 @@ private String appliesToText(FunctionAppliesTo[] functionAppliesTos) { // If any of the appliesTo has descriptive text, we need to format things differently return ""; } - appliesToText.append("product: ") - .append(appliesTo.lifeCycle().name()) - .append(" ") - .append(appliesTo.version()) - .append("\n"); + appliesToText.append("product: "); + appendLifeCycleAndVersion(appliesToText, appliesTo); + appliesToText.append("\n"); + if (appliesTo.serverless() && appliesTo.lifeCycle().serverlessLifecycle() == GA) { + appliesToText.append("serverless: ").append(GA).append("\n"); + } } appliesToText.append("```\n"); } @@ -564,10 +566,10 @@ private String appliesToTextWithoutAppliesTo(FunctionAppliesTo[] functionApplies appliesToText.append("\n"); for (FunctionAppliesTo appliesTo : functionAppliesTos) { appliesToText.append("###### "); - appliesToText.append(appliesTo.lifeCycle().name()); - if (appliesTo.version().isEmpty() == false) { - appliesToText.append(" ").append(appliesTo.version()); + if (appliesTo.serverless() && appliesTo.lifeCycle().serverlessLifecycle() == GA) { + appliesToText.append("Serverless: ").append(GA).append(", Stateful: "); } + appendLifeCycleAndVersion(appliesToText, appliesTo); appliesToText.append("\n"); if (appliesTo.description().isEmpty() == false) { appliesToText.append(appliesTo.description()).append("\n\n"); @@ -577,6 +579,13 @@ private String appliesToTextWithoutAppliesTo(FunctionAppliesTo[] functionApplies return appliesToText.toString(); } + private void appendLifeCycleAndVersion(StringBuilder appliesToText, FunctionAppliesTo appliesTo) { + appliesToText.append(appliesTo.lifeCycle().name()); + if (appliesTo.version().isEmpty() == false) { + appliesToText.append(" ").append(appliesTo.version()); + } + } + private void renderFullLayout( boolean preview, FunctionAppliesTo[] functionAppliesTos, diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java index d0132a5339687..a9b65576bf92f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java @@ -291,7 +291,7 @@ public void testRenderingLayoutFromClass() throws IOException { ::: :::{note} - ###### COMING 9.1.0 + ###### Serverless: GA, Stateful: COMING 9.1.0 Support for optional named parameters is only available from 9.1.0 ###### DEVELOPMENT From 88997139d79ffe2bd75790156b4d0d849c46ea4e Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 17:25:44 +0100 Subject: [PATCH 7/8] Remove applies_to from LOOKUP JOIN and MV_EXPAND --- docs/reference/query-languages/esql/esql-commands.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/reference/query-languages/esql/esql-commands.md b/docs/reference/query-languages/esql/esql-commands.md index 386edc4e726f6..0da0d1c5c9a88 100644 --- a/docs/reference/query-languages/esql/esql-commands.md +++ b/docs/reference/query-languages/esql/esql-commands.md @@ -666,10 +666,6 @@ FROM employees ## `LOOKUP JOIN` [esql-lookup-join] ::::{warning} -```{applies_to} -stack: preview 9.0, coming 9.1 -serverless: preview -``` This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. :::: @@ -753,10 +749,6 @@ FROM Left ## `MV_EXPAND` [esql-mv_expand] ::::{warning} -```{applies_to} -stack: preview 9.0, coming 9.1 -serverless: preview -``` This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. :::: From 45032bbbba9e7b1d42e563d7d364381a1d11c1ef Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Fri, 28 Mar 2025 18:02:17 +0100 Subject: [PATCH 8/8] Use Elastic Stack instead of Stateful, and remove version from QSTR --- .../query-languages/esql/_snippets/functions/layout/match.md | 2 +- .../query-languages/esql/_snippets/functions/layout/qstr.md | 4 ++-- .../esql/_snippets/functions/layout/to_lower.md | 2 +- .../esql/_snippets/functions/layout/to_upper.md | 2 +- .../xpack/esql/expression/function/fulltext/QueryString.java | 3 +-- .../xpack/esql/expression/function/DocsV3Support.java | 2 +- .../xpack/esql/expression/function/DocsV3SupportTests.java | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/match.md b/docs/reference/query-languages/esql/_snippets/functions/layout/match.md index e4277941d582e..b5368c12e2b4a 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/match.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/match.md @@ -8,7 +8,7 @@ are not subject to the support SLA of official GA features. ::: :::{note} -###### Serverless: GA, Stateful: COMING +###### Serverless: GA, Elastic Stack: COMING Support for optional named parameters is only available in serverless, or in a future {{es}} release ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md b/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md index c7e252c90b8d8..9154ed5343624 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/qstr.md @@ -8,8 +8,8 @@ are not subject to the support SLA of official GA features. ::: :::{note} -###### Serverless: GA, Stateful: COMING 9.1.0 -Support for optional named parameters is only available from 9.1.0 +###### Serverless: GA, Elastic Stack: COMING +Support for optional named parameters is only available in serverless, or in a future {{es}} release ::: **Syntax** diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md b/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md index 3304633f5be90..e567ccf6fd1c4 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/to_lower.md @@ -2,7 +2,7 @@ ## `TO_LOWER` [esql-to_lower] :::{note} -###### Serverless: GA, Stateful: COMING 9.1.0 +###### Serverless: GA, Elastic Stack: COMING 9.1.0 Support for multivalued parameters is only available from 9.1.0 ::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md b/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md index 386bac6d84387..1ab785c4b4cdf 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/to_upper.md @@ -2,7 +2,7 @@ ## `TO_UPPER` [esql-to_upper] :::{note} -###### Serverless: GA, Stateful: COMING 9.1.0 +###### Serverless: GA, Elastic Stack: COMING 9.1.0 Support for multivalued parameters is only available from 9.1.0 ::: diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java index d426308eff775..662cd69e220a1 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java @@ -122,8 +122,7 @@ public class QueryString extends FullTextFunction implements OptionalArgument { appliesTo = { @FunctionAppliesTo( lifeCycle = FunctionAppliesToLifecycle.COMING, - version = "9.1.0", - description = "Support for optional named parameters is only available from 9.1.0" + description = "Support for optional named parameters is only available in serverless, or in a future {{es}} release" ) } ) public QueryString( diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java index 684cc0525b2d5..0d20d90a95c83 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java @@ -567,7 +567,7 @@ private String appliesToTextWithoutAppliesTo(FunctionAppliesTo[] functionApplies for (FunctionAppliesTo appliesTo : functionAppliesTos) { appliesToText.append("###### "); if (appliesTo.serverless() && appliesTo.lifeCycle().serverlessLifecycle() == GA) { - appliesToText.append("Serverless: ").append(GA).append(", Stateful: "); + appliesToText.append("Serverless: ").append(GA).append(", Elastic Stack: "); } appendLifeCycleAndVersion(appliesToText, appliesTo); appliesToText.append("\n"); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java index a9b65576bf92f..a54bc4bb0230f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3SupportTests.java @@ -291,7 +291,7 @@ public void testRenderingLayoutFromClass() throws IOException { ::: :::{note} - ###### Serverless: GA, Stateful: COMING 9.1.0 + ###### Serverless: GA, Elastic Stack: COMING 9.1.0 Support for optional named parameters is only available from 9.1.0 ###### DEVELOPMENT