diff --git a/docs/reference/esql/functions/kibana/definition/not_in.json b/docs/reference/esql/functions/kibana/definition/not_in.json
new file mode 100644
index 0000000000000..3fa25d793b503
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/definition/not_in.json
@@ -0,0 +1,263 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "operator",
+ "operator" : "NOT IN",
+ "name" : "not_in",
+ "description" : "The `NOT IN` operator allows testing whether a field or expression does *not* equal any element in a list of literals, fields or expressions.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "cartesian_point",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "cartesian_point",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "cartesian_shape",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "cartesian_shape",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "double",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "double",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "geo_point",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "geo_point",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "geo_shape",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "geo_shape",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "text",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "long",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "long",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "text",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "text",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "text",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "version",
+ "optional" : false,
+ "description" : "An expression."
+ },
+ {
+ "name" : "inlist",
+ "type" : "version",
+ "optional" : false,
+ "description" : "A list of items."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ }
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/esql/functions/kibana/definition/not_like.json b/docs/reference/esql/functions/kibana/definition/not_like.json
new file mode 100644
index 0000000000000..bba70d14d7cb7
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/definition/not_like.json
@@ -0,0 +1,47 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "operator",
+ "operator" : "NOT LIKE",
+ "name" : "not_like",
+ "description" : "Use `NOT LIKE` to filter data based on string patterns using wildcards. `NOT LIKE`\nusually acts on a field placed on the left-hand side of the operator, but it can\nalso act on a constant (literal) expression. The right-hand side of the operator\nrepresents the pattern.\n\nThe following wildcard characters are supported:\n\n* `*` matches zero or more characters.\n* `?` matches one character.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "str",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "A literal expression."
+ },
+ {
+ "name" : "pattern",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Pattern."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "str",
+ "type" : "text",
+ "optional" : false,
+ "description" : "A literal expression."
+ },
+ {
+ "name" : "pattern",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Pattern."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ }
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/esql/functions/kibana/definition/not_rlike.json b/docs/reference/esql/functions/kibana/definition/not_rlike.json
new file mode 100644
index 0000000000000..09abd5ab567e8
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/definition/not_rlike.json
@@ -0,0 +1,47 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "operator",
+ "operator" : "NOT RLIKE",
+ "name" : "not_rlike",
+ "description" : "Use `NOT RLIKE` to filter data based on string patterns using using\n<>. `NOT RLIKE` usually acts on a field placed on\nthe left-hand side of the operator, but it can also act on a constant (literal)\nexpression. The right-hand side of the operator represents the pattern.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "str",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "A literal value."
+ },
+ {
+ "name" : "pattern",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "A regular expression."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "str",
+ "type" : "text",
+ "optional" : false,
+ "description" : "A literal value."
+ },
+ {
+ "name" : "pattern",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "A regular expression."
+ }
+ ],
+ "variadic" : true,
+ "returnType" : "boolean"
+ }
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/esql/functions/kibana/docs/not_in.md b/docs/reference/esql/functions/kibana/docs/not_in.md
new file mode 100644
index 0000000000000..e9e5a7b384d1c
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/docs/not_in.md
@@ -0,0 +1,7 @@
+
+
+### NOT_IN
+The `NOT IN` operator allows testing whether a field or expression does *not* equal any element in a list of literals, fields or expressions.
+
diff --git a/docs/reference/esql/functions/kibana/docs/not_like.md b/docs/reference/esql/functions/kibana/docs/not_like.md
new file mode 100644
index 0000000000000..fd1cf7a68630f
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/docs/not_like.md
@@ -0,0 +1,15 @@
+
+
+### NOT_LIKE
+Use `NOT LIKE` to filter data based on string patterns using wildcards. `NOT LIKE`
+usually acts on a field placed on the left-hand side of the operator, but it can
+also act on a constant (literal) expression. The right-hand side of the operator
+represents the pattern.
+
+The following wildcard characters are supported:
+
+* `*` matches zero or more characters.
+* `?` matches one character.
+
diff --git a/docs/reference/esql/functions/kibana/docs/not_rlike.md b/docs/reference/esql/functions/kibana/docs/not_rlike.md
new file mode 100644
index 0000000000000..dac23438c1612
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/docs/not_rlike.md
@@ -0,0 +1,10 @@
+
+
+### NOT_RLIKE
+Use `NOT RLIKE` to filter data based on string patterns using using
+<>. `NOT RLIKE` usually acts on a field placed on
+the left-hand side of the operator, but it can also act on a constant (literal)
+expression. The right-hand side of the operator represents the pattern.
+
diff --git a/docs/reference/esql/functions/types/not_in.asciidoc b/docs/reference/esql/functions/types/not_in.asciidoc
new file mode 100644
index 0000000000000..6ed2c250ef0ac
--- /dev/null
+++ b/docs/reference/esql/functions/types/not_in.asciidoc
@@ -0,0 +1,22 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Supported types*
+
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+field | inlist | result
+boolean | boolean | boolean
+cartesian_point | cartesian_point | boolean
+cartesian_shape | cartesian_shape | boolean
+double | double | boolean
+geo_point | geo_point | boolean
+geo_shape | geo_shape | boolean
+integer | integer | boolean
+ip | ip | boolean
+keyword | keyword | boolean
+keyword | text | boolean
+long | long | boolean
+text | keyword | boolean
+text | text | boolean
+version | version | boolean
+|===
diff --git a/docs/reference/esql/functions/types/not_like.asciidoc b/docs/reference/esql/functions/types/not_like.asciidoc
new file mode 100644
index 0000000000000..fffa6dc0b8371
--- /dev/null
+++ b/docs/reference/esql/functions/types/not_like.asciidoc
@@ -0,0 +1,10 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Supported types*
+
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+str | pattern | result
+keyword | keyword | boolean
+text | keyword | boolean
+|===
diff --git a/docs/reference/esql/functions/types/not_rlike.asciidoc b/docs/reference/esql/functions/types/not_rlike.asciidoc
new file mode 100644
index 0000000000000..fffa6dc0b8371
--- /dev/null
+++ b/docs/reference/esql/functions/types/not_rlike.asciidoc
@@ -0,0 +1,10 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Supported types*
+
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+str | pattern | result
+keyword | keyword | boolean
+text | keyword | boolean
+|===
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java
index 67dec69b51393..30ac9fc69ed9c 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java
@@ -823,12 +823,13 @@ public static void renderSignature() throws IOException {
if (System.getProperty("generateDocs") == null) {
return;
}
- String rendered = buildSignatureSvg(functionName());
+ String name = functionName();
+ String rendered = buildSignatureSvg(name);
if (rendered == null) {
LogManager.getLogger(getTestClass()).info("Skipping rendering signature because the function isn't registered");
} else {
LogManager.getLogger(getTestClass()).info("Writing function signature");
- writeToTempDir("signature", rendered, "svg");
+ writeToTempDir("signature", name, "svg", rendered);
}
}
@@ -890,10 +891,13 @@ private static Map, DataType> signatures() {
@AfterClass
public static void renderDocs() throws IOException {
+ renderDocs(functionName());
+ }
+
+ protected static void renderDocs(String name) throws IOException {
if (System.getProperty("generateDocs") == null) {
return;
}
- String name = functionName();
if (binaryOperator(name) != null || unaryOperator(name) != null || searchOperator(name) != null || likeOrInOperator(name)) {
renderDocsForOperators(name);
return;
@@ -922,12 +926,12 @@ public static void renderDocs() throws IOException {
description.isAggregation()
);
}
- renderTypes(description.args());
- renderParametersList(description.argNames(), description.argDescriptions());
+ renderTypes(name, description.args());
+ renderParametersList(name, description.argNames(), description.argDescriptions());
FunctionInfo info = EsqlFunctionRegistry.functionInfo(definition);
- renderDescription(description.description(), info.detailedDescription(), info.note());
- boolean hasExamples = renderExamples(info);
- boolean hasAppendix = renderAppendix(info.appendix());
+ renderDescription(name, description.description(), info.detailedDescription(), info.note());
+ boolean hasExamples = renderExamples(name, info);
+ boolean hasAppendix = renderAppendix(name, info.appendix());
renderFullLayout(name, info.preview(), hasExamples, hasAppendix);
renderKibanaInlineDocs(name, info);
renderKibanaFunctionDefinition(name, info, description.args(), description.variadic());
@@ -944,7 +948,7 @@ public static void renderDocs() throws IOException {
+ "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.\"]\n";
- private static void renderTypes(List args) throws IOException {
+ private static void renderTypes(String name, List args) throws IOException {
StringBuilder header = new StringBuilder();
List argNames = args.stream().map(EsqlFunctionRegistry.ArgSignature::name).toList();
for (String arg : argNames) {
@@ -984,11 +988,11 @@ private static void renderTypes(List args) th
[%header.monospaced.styled,format=dsv,separator=|]
|===
""" + header + "\n" + table.stream().collect(Collectors.joining("\n")) + "\n|===\n";
- LogManager.getLogger(getTestClass()).info("Writing function types for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("types", rendered, "asciidoc");
+ LogManager.getLogger(getTestClass()).info("Writing function types for [{}]:\n{}", name, rendered);
+ writeToTempDir("types", name, "asciidoc", rendered);
}
- private static void renderParametersList(List argNames, List argDescriptions) throws IOException {
+ private static void renderParametersList(String name, List argNames, List argDescriptions) throws IOException {
StringBuilder builder = new StringBuilder();
builder.append(DOCS_WARNING);
builder.append("*Parameters*\n");
@@ -996,11 +1000,11 @@ private static void renderParametersList(List argNames, List arg
builder.append("\n`").append(argNames.get(a)).append("`::\n").append(argDescriptions.get(a)).append('\n');
}
String rendered = builder.toString();
- LogManager.getLogger(getTestClass()).info("Writing parameters for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("parameters", rendered, "asciidoc");
+ LogManager.getLogger(getTestClass()).info("Writing parameters for [{}]:\n{}", name, rendered);
+ writeToTempDir("parameters", name, "asciidoc", rendered);
}
- private static void renderDescription(String description, String detailedDescription, String note) throws IOException {
+ private static void renderDescription(String name, String description, String detailedDescription, String note) throws IOException {
String rendered = DOCS_WARNING + """
*Description*
@@ -1013,11 +1017,11 @@ private static void renderDescription(String description, String detailedDescrip
if (Strings.isNullOrEmpty(note) == false) {
rendered += "\nNOTE: " + note + "\n";
}
- LogManager.getLogger(getTestClass()).info("Writing description for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("description", rendered, "asciidoc");
+ LogManager.getLogger(getTestClass()).info("Writing description for [{}]:\n{}", name, rendered);
+ writeToTempDir("description", name, "asciidoc", rendered);
}
- private static boolean renderExamples(FunctionInfo info) throws IOException {
+ private static boolean renderExamples(String name, FunctionInfo info) throws IOException {
if (info == null || info.examples().length == 0) {
return false;
}
@@ -1051,20 +1055,20 @@ private static boolean renderExamples(FunctionInfo info) throws IOException {
}
builder.append('\n');
String rendered = builder.toString();
- LogManager.getLogger(getTestClass()).info("Writing examples for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("examples", rendered, "asciidoc");
+ LogManager.getLogger(getTestClass()).info("Writing examples for [{}]:\n{}", name, rendered);
+ writeToTempDir("examples", name, "asciidoc", rendered);
return true;
}
- private static boolean renderAppendix(String appendix) throws IOException {
+ private static boolean renderAppendix(String name, String appendix) throws IOException {
if (appendix.isEmpty()) {
return false;
}
String rendered = DOCS_WARNING + appendix + "\n";
- LogManager.getLogger(getTestClass()).info("Writing appendix for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("appendix", rendered, "asciidoc");
+ LogManager.getLogger(getTestClass()).info("Writing appendix for [{}]:\n{}", name, rendered);
+ writeToTempDir("appendix", name, "asciidoc", rendered);
return true;
}
@@ -1091,11 +1095,11 @@ private static void renderFullLayout(String name, boolean preview, boolean hasEx
if (hasAppendix) {
rendered += "include::../appendix/" + name + ".asciidoc[]\n";
}
- LogManager.getLogger(getTestClass()).info("Writing layout for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("layout", rendered, "asciidoc");
+ LogManager.getLogger(getTestClass()).info("Writing layout for [{}]:\n{}", name, rendered);
+ writeToTempDir("layout", name, "asciidoc", rendered);
}
- private static Constructor> constructorWithFunctionInfo(Class> clazz) {
+ protected static Constructor> constructorWithFunctionInfo(Class> clazz) {
for (Constructor> ctor : clazz.getConstructors()) {
FunctionInfo functionInfo = ctor.getAnnotation(FunctionInfo.class);
if (functionInfo != null) {
@@ -1110,6 +1114,10 @@ private static void renderDocsForOperators(String name) throws IOException {
assert ctor != null;
FunctionInfo functionInfo = ctor.getAnnotation(FunctionInfo.class);
assert functionInfo != null;
+ renderDocsForOperators(name, ctor, functionInfo);
+ }
+
+ protected static void renderDocsForOperators(String name, Constructor> ctor, FunctionInfo functionInfo) throws IOException {
renderKibanaInlineDocs(name, functionInfo);
var params = ctor.getParameters();
@@ -1127,7 +1135,7 @@ private static void renderDocsForOperators(String name) throws IOException {
}
}
renderKibanaFunctionDefinition(name, functionInfo, args, likeOrInOperator(name));
- renderTypes(args);
+ renderTypes(name, args);
}
private static void renderKibanaInlineDocs(String name, FunctionInfo info) throws IOException {
@@ -1151,8 +1159,8 @@ private static void renderKibanaInlineDocs(String name, FunctionInfo info) throw
builder.append("Note: ").append(removeAsciidocLinks(info.note())).append("\n");
}
String rendered = builder.toString();
- LogManager.getLogger(getTestClass()).info("Writing kibana inline docs for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("kibana/docs", rendered, "md");
+ LogManager.getLogger(getTestClass()).info("Writing kibana inline docs for [{}]:\n{}", name, rendered);
+ writeToTempDir("kibana/docs", name, "md", rendered);
}
private static void renderKibanaFunctionDefinition(
@@ -1244,8 +1252,8 @@ private static void renderKibanaFunctionDefinition(
builder.field("snapshot_only", EsqlFunctionRegistry.isSnapshotOnly(name));
String rendered = Strings.toString(builder.endObject());
- LogManager.getLogger(getTestClass()).info("Writing kibana function definition for [{}]:\n{}", functionName(), rendered);
- writeToTempDir("kibana/definition", rendered, "json");
+ LogManager.getLogger(getTestClass()).info("Writing kibana function definition for [{}]:\n{}", name, rendered);
+ writeToTempDir("kibana/definition", name, "json", rendered);
}
private static String removeAsciidocLinks(String asciidoc) {
@@ -1340,7 +1348,10 @@ private static String unaryOperator(String name) {
* If this tests is for a like or rlike operator return true, otherwise return {@code null}.
*/
private static boolean likeOrInOperator(String name) {
- return name.equalsIgnoreCase("rlike") || name.equalsIgnoreCase("like") || name.equalsIgnoreCase("in");
+ return switch (name.toLowerCase(Locale.ENGLISH)) {
+ case "rlike", "like", "in", "not_rlike", "not_like", "not_in" -> true;
+ default -> false;
+ };
}
/**
@@ -1350,11 +1361,11 @@ private static boolean likeOrInOperator(String name) {
* don't have write permission to the docs.
*
*/
- private static void writeToTempDir(String subdir, String str, String extension) throws IOException {
+ private static void writeToTempDir(String subdir, String name, 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("functions").resolve(subdir);
Files.createDirectories(dir);
- Path file = dir.resolve(functionName() + "." + extension);
+ Path file = dir.resolve(name + "." + extension);
Files.writeString(file, str);
LogManager.getLogger(getTestClass()).info("Wrote to file: {}", file);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java
index 589477a8bebdc..26340be224082 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java
@@ -20,7 +20,9 @@
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase;
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+import org.junit.AfterClass;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@@ -150,4 +152,9 @@ static Expression buildRLike(Logger logger, Source source, List args
? new RLike(source, expression, new RLikePattern(patternString), true)
: new RLike(source, expression, new RLikePattern(patternString));
}
+
+ @AfterClass
+ public static void renderNotRLike() throws IOException {
+ WildcardLikeTests.renderNot(constructorWithFunctionInfo(RLike.class), "RLIKE", d -> d);
+ }
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java
index e60c5f77ab42e..7f04f076ed15f 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java
@@ -9,6 +9,7 @@
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
+import com.unboundid.util.NotNull;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.xpack.esql.core.expression.Expression;
@@ -18,11 +19,19 @@
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase;
+import org.elasticsearch.xpack.esql.expression.function.Example;
+import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
import org.elasticsearch.xpack.esql.expression.function.FunctionName;
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+import org.junit.AfterClass;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
import java.util.function.Supplier;
import static org.hamcrest.Matchers.equalTo;
@@ -87,4 +96,67 @@ static Expression buildWildcardLike(Source source, List args) {
}
return new WildcardLike(source, expression, new WildcardPattern(((BytesRef) pattern.fold(FoldContext.small())).utf8ToString()));
}
+
+ @AfterClass
+ public static void renderNotLike() throws IOException {
+ renderNot(constructorWithFunctionInfo(WildcardLike.class), "LIKE", d -> d);
+ }
+
+ public static void renderNot(@NotNull Constructor> ctor, String name, Function description) throws IOException {
+ FunctionInfo orig = ctor.getAnnotation(FunctionInfo.class);
+ assert orig != null;
+ FunctionInfo functionInfo = new FunctionInfo() {
+ @Override
+ public Class extends Annotation> annotationType() {
+ return orig.annotationType();
+ }
+
+ @Override
+ public String operator() {
+ return "NOT " + name;
+ }
+
+ @Override
+ public String[] returnType() {
+ return orig.returnType();
+ }
+
+ @Override
+ public boolean preview() {
+ return orig.preview();
+ }
+
+ @Override
+ public String description() {
+ return description.apply(orig.description().replace(name, "NOT " + name));
+ }
+
+ @Override
+ public String detailedDescription() {
+ return "";
+ }
+
+ @Override
+ public String note() {
+ return orig.note().replace(name, "NOT " + name);
+ }
+
+ @Override
+ public String appendix() {
+ return orig.appendix().replace(name, "NOT " + name);
+ }
+
+ @Override
+ public boolean isAggregation() {
+ return orig.isAggregation();
+ }
+
+ @Override
+ public Example[] examples() {
+ // throw away examples
+ return new Example[] {};
+ }
+ };
+ renderDocsForOperators("not_" + name.toLowerCase(Locale.ENGLISH), ctor, functionInfo);
+ }
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java
index 80f67ec8e5e3a..03a4b063d6294 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java
@@ -19,7 +19,10 @@
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase;
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.WildcardLikeTests;
+import org.junit.AfterClass;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -333,4 +336,14 @@ private static void bytesRefs(List suppliers, int items) {
protected Expression build(Source source, List args) {
return new In(source, args.get(args.size() - 1), args.subList(0, args.size() - 1));
}
+
+ @AfterClass
+ public static void renderNotIn() throws IOException {
+ WildcardLikeTests.renderNot(
+ constructorWithFunctionInfo(In.class),
+ "IN",
+ d -> "The `NOT IN` operator allows testing whether a field or expression does *not* equal any element "
+ + "in a list of literals, fields or expressions."
+ );
+ }
}