From b18594a1d25abb504a839c2d87570a865405ae29 Mon Sep 17 00:00:00 2001 From: Mridula Date: Fri, 20 Feb 2026 14:33:17 +0000 Subject: [PATCH 1/4] ES|QL: Validate TOP_SNIPPETS query argument is foldable at verification time Implements compile-time validation for the TOP_SNIPPETS function to ensure that its query parameter is foldable (constant). This follows the same pattern as other full-text functions like Match. Changes: - TopSnippets.java: Implements PostOptimizationVerificationAware interface and adds postOptimizationVerification() method using Foldables.resolveTypeQuery() - TopSnippetsValidationTests.java: New unit tests for validation logic - TopSnippetsTests.java: Updated assertion style per code review feedback - LogicalPlanOptimizerTests.java: Added post-optimization validation tests - top-snippets.csv-spec: Added integration tests for foldable query validation - docs/changelog/142462.yaml: Added changelog entry Fixes #142462 --- docs/changelog/142462.yaml | 6 ++ .../src/main/resources/top-snippets.csv-spec | 36 +++++++++++ .../function/scalar/string/TopSnippets.java | 13 +++- .../scalar/string/TopSnippetsTests.java | 21 ++++--- .../string/TopSnippetsValidationTests.java | 59 +++++++++++++++++++ .../optimizer/LogicalPlanOptimizerTests.java | 20 +++++++ 6 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 docs/changelog/142462.yaml create mode 100644 x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java diff --git a/docs/changelog/142462.yaml b/docs/changelog/142462.yaml new file mode 100644 index 0000000000000..40559109f5d2d --- /dev/null +++ b/docs/changelog/142462.yaml @@ -0,0 +1,6 @@ +pr: 142462 +summary: "ES|QL: Validate TOP_SNIPPETS query argument is foldable at verification time" +area: ES|QL +type: bug +issues: + - 142462 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/top-snippets.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/top-snippets.csv-spec index d93e35f701056..9351b8115c103 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/top-snippets.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/top-snippets.csv-spec @@ -208,3 +208,39 @@ ROW content = "Sauron, the Dark Lord, has gathered to him all the Rings of Power snippets:keyword [All he lacks in his plans for dominion is the One Ring - the ring that rules them all - which has fallen into the hands of, ring that rules them all - which has fallen into the hands of the hobbit\, Bilbo Baggins.] ; + +topSnippetsWithConstantQuery +required_capability: top_snippets_function + +FROM employees +| EVAL snippets = TOP_SNIPPETS(first_name, "John") +| KEEP emp_no, first_name, snippets +| SORT emp_no +| LIMIT 5 +; + +emp_no:integer | first_name:keyword | snippets:keyword +10001 | Georgi | null +10002 | Bezalel | null +10003 | Parto | null +10004 | Chirstian | null +10005 | Kyoichi | null +; + +topSnippetsWithFoldableConcatQuery +required_capability: top_snippets_function + +FROM employees +| EVAL snippets = TOP_SNIPPETS(first_name, CONCAT("Jo", "hn")) +| KEEP emp_no, first_name, snippets +| SORT emp_no +| LIMIT 5 +; + +emp_no:integer | first_name:keyword | snippets:keyword +10001 | Georgi | null +10002 | Bezalel | null +10003 | Parto | null +10004 | Chirstian | null +10005 | Kyoichi | null +; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java index 7c67a75cd4e3a..e519b18d4748f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java @@ -20,12 +20,16 @@ import org.elasticsearch.xpack.core.common.chunks.MemoryIndexChunkScorer; import org.elasticsearch.xpack.core.common.chunks.ScoredChunk; import org.elasticsearch.xpack.core.inference.chunking.SentenceBoundaryChunkingSettings; +import org.elasticsearch.xpack.esql.capabilities.PostOptimizationVerificationAware; +import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.InvalidArgumentException; import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.Expressions; import org.elasticsearch.xpack.esql.core.expression.MapExpression; 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.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; @@ -49,11 +53,13 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.THIRD; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPostOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeQuery; import static org.elasticsearch.xpack.esql.expression.function.Options.resolve; import static org.elasticsearch.xpack.esql.expression.function.scalar.util.ChunkUtils.chunkText; import static org.elasticsearch.xpack.esql.expression.function.scalar.util.ChunkUtils.emitChunks; -public class TopSnippets extends EsqlScalarFunction implements OptionalArgument { +public class TopSnippets extends EsqlScalarFunction implements OptionalArgument, PostOptimizationVerificationAware { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry( Expression.class, @@ -277,6 +283,11 @@ static void process( emitChunks(builder, snippets); } + @Override + public void postOptimizationVerification(Failures failures) { + resolveTypeQuery(query(), sourceText(), forPostOptimizationValidation(query(), failures)); + } + @Override public EvalOperator.ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { int numSnippets; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java index 83d3642436004..d24d3f9957a3e 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java @@ -17,17 +17,21 @@ import org.elasticsearch.xpack.core.common.chunks.MemoryIndexChunkScorer; import org.elasticsearch.xpack.core.common.chunks.ScoredChunk; import org.elasticsearch.xpack.core.inference.chunking.SentenceBoundaryChunkingSettings; +import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.FieldAttribute; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.expression.MapExpression; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.core.type.EsField; import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -38,7 +42,12 @@ import static org.elasticsearch.xpack.esql.expression.function.scalar.string.TopSnippets.DEFAULT_NUM_SNIPPETS; import static org.elasticsearch.xpack.esql.expression.function.scalar.string.TopSnippets.DEFAULT_WORD_SIZE; import static org.elasticsearch.xpack.esql.expression.function.scalar.util.ChunkUtils.chunkText; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; public class TopSnippetsTests extends AbstractScalarFunctionTestCase { @@ -245,14 +254,9 @@ public void testSnippetsReturnedInScoringOrder() { List result = process(combinedText, query, 3, 50); - assertNotNull("Should return results for matching query", result); - assertFalse("Should have at least one result", result.isEmpty()); - - assertTrue( - "First snippet should be from the most relevant chunk (contains 'Elasticsearch' multiple times)", - result.get(0).toLowerCase(Locale.ROOT).contains("elasticsearch") - && (result.get(0).contains("powerful") || result.get(0).contains("supports") || result.get(0).contains("companies")) - ); + assertThat(result, hasSize(2)); + assertThat(result.get(0), containsString("Elasticsearch is a powerful search engine")); + assertThat(result.get(1), containsString("Elasticsearch is one option among several alternatives")); } private void verifySnippets(String query, Integer numSnippets, Integer numWords, int expectedNumChunksReturned) { @@ -295,5 +299,4 @@ private List process(String str, String query, int numSnippets, int numW } } } - } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java new file mode 100644 index 0000000000000..ce21f4f962766 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar.string; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.esql.common.Failures; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.FieldAttribute; +import org.elasticsearch.xpack.esql.core.expression.Literal; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.core.type.EsField; + +import java.util.List; +import java.util.Map; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; + +public class TopSnippetsValidationTests extends ESTestCase { + + public void testValidateWithLiteralQuery() { + Expression field = fieldAttribute("body", DataType.TEXT); + Expression query = new Literal(Source.EMPTY, new BytesRef("search terms"), DataType.KEYWORD); + TopSnippets topSnippets = new TopSnippets(Source.synthetic("TOP_SNIPPETS(body, \"search terms\")"), field, query, null); + + Failures failures = new Failures(); + topSnippets.postOptimizationVerification(failures); + + assertThat(failures.failures(), is(empty())); + } + + public void testValidateWithFieldQuery() { + Expression field = fieldAttribute("body", DataType.TEXT); + Expression query = fieldAttribute("title", DataType.KEYWORD); + TopSnippets topSnippets = new TopSnippets(Source.synthetic("TOP_SNIPPETS(body, title)"), field, query, null); + + Failures failures = new Failures(); + topSnippets.postOptimizationVerification(failures); + + assertThat(failures.failures(), hasSize(1)); + assertThat( + failures.failures().iterator().next().message(), + containsString("Query must be a valid string") + ); + } + + private static FieldAttribute fieldAttribute(String name, DataType type) { + return new FieldAttribute(Source.EMPTY, name, new EsField(name, type, Map.of(), true, EsField.TimeSeriesFieldType.NONE)); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 7bbbf6fcd4d4b..f2c62c21ce7fc 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -9963,4 +9963,24 @@ STATS max(max_over_time(network.bytes_in)) by p = pod, bucket(@timestamp, 1 minu // EsRelation[k8s][@timestamp{f}#424, client.ip{f}#428, cluster{f}#425, ..] as(eval3.child(), EsRelation.class); } + + public void testTopSnippetsQueryMustBeFoldable() { + var e = expectThrows( + VerificationException.class, + () -> optimizedPlan("FROM test | EVAL x = TOP_SNIPPETS(first_name, last_name)") + ); + assertThat(e.getMessage(), containsString("Query must be a valid string")); + } + + public void testTopSnippetsQueryFoldableAfterOptimization() { + var plan = optimizedPlan("FROM test | EVAL x = TOP_SNIPPETS(first_name, \"search terms\")"); + var failures = LogicalVerifier.INSTANCE.verify(plan, plan.output()); + assertThat(failures.failures(), is(empty())); + } + + public void testTopSnippetsQueryFoldableConcatConstants() { + var plan = optimizedPlan("FROM test | EVAL x = TOP_SNIPPETS(first_name, CONCAT(\"search\", \" terms\"))"); + var failures = LogicalVerifier.INSTANCE.verify(plan, plan.output()); + assertThat(failures.failures(), is(empty())); + } } From 8b4463908225d2e3846cdbdd49fc172860d74539 Mon Sep 17 00:00:00 2001 From: Mridula Date: Fri, 20 Feb 2026 14:38:18 +0000 Subject: [PATCH 2/4] Update docs/changelog/142763.yaml --- docs/changelog/142763.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/changelog/142763.yaml diff --git a/docs/changelog/142763.yaml b/docs/changelog/142763.yaml new file mode 100644 index 0000000000000..ac77e6d6afde8 --- /dev/null +++ b/docs/changelog/142763.yaml @@ -0,0 +1,6 @@ +area: ES|QL +issues: + - 142462 +pr: 142763 +summary: Validate TOP_SNIPPETS query argument is foldable at verification +type: enhancement From 84c48a3c020f7073f246ddef9e0d6565be36e52b Mon Sep 17 00:00:00 2001 From: elasticsearchmachine Date: Fri, 20 Feb 2026 14:46:27 +0000 Subject: [PATCH 3/4] [CI] Auto commit changes from spotless --- .../expression/function/scalar/string/TopSnippets.java | 2 -- .../function/scalar/string/TopSnippetsTests.java | 8 -------- .../scalar/string/TopSnippetsValidationTests.java | 6 +----- .../xpack/esql/optimizer/LogicalPlanOptimizerTests.java | 5 +---- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java index e519b18d4748f..8eae2497c6cff 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippets.java @@ -24,12 +24,10 @@ import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.InvalidArgumentException; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.Expressions; import org.elasticsearch.xpack.esql.core.expression.MapExpression; 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.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java index d24d3f9957a3e..01e46c7e5852c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsTests.java @@ -17,21 +17,16 @@ import org.elasticsearch.xpack.core.common.chunks.MemoryIndexChunkScorer; import org.elasticsearch.xpack.core.common.chunks.ScoredChunk; import org.elasticsearch.xpack.core.inference.chunking.SentenceBoundaryChunkingSettings; -import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.FieldAttribute; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.expression.MapExpression; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; -import org.elasticsearch.xpack.esql.core.type.EsField; import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; import java.util.ArrayList; import java.util.List; -import java.util.Locale; -import java.util.Map; import java.util.Objects; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -43,11 +38,8 @@ import static org.elasticsearch.xpack.esql.expression.function.scalar.string.TopSnippets.DEFAULT_WORD_SIZE; import static org.elasticsearch.xpack.esql.expression.function.scalar.util.ChunkUtils.chunkText; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; public class TopSnippetsTests extends AbstractScalarFunctionTestCase { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java index ce21f4f962766..44dd18cdaba4b 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/TopSnippetsValidationTests.java @@ -17,7 +17,6 @@ import org.elasticsearch.xpack.esql.core.type.DataType; import org.elasticsearch.xpack.esql.core.type.EsField; -import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.containsString; @@ -47,10 +46,7 @@ public void testValidateWithFieldQuery() { topSnippets.postOptimizationVerification(failures); assertThat(failures.failures(), hasSize(1)); - assertThat( - failures.failures().iterator().next().message(), - containsString("Query must be a valid string") - ); + assertThat(failures.failures().iterator().next().message(), containsString("Query must be a valid string")); } private static FieldAttribute fieldAttribute(String name, DataType type) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index f2c62c21ce7fc..c99e8c5cc50aa 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -9965,10 +9965,7 @@ STATS max(max_over_time(network.bytes_in)) by p = pod, bucket(@timestamp, 1 minu } public void testTopSnippetsQueryMustBeFoldable() { - var e = expectThrows( - VerificationException.class, - () -> optimizedPlan("FROM test | EVAL x = TOP_SNIPPETS(first_name, last_name)") - ); + var e = expectThrows(VerificationException.class, () -> optimizedPlan("FROM test | EVAL x = TOP_SNIPPETS(first_name, last_name)")); assertThat(e.getMessage(), containsString("Query must be a valid string")); } From 810bb14fc8e061462f3cf0222250ecc967344de2 Mon Sep 17 00:00:00 2001 From: elasticsearchmachine Date: Fri, 20 Feb 2026 14:58:37 +0000 Subject: [PATCH 4/4] [CI] Auto commit changes from spotless --- .../xpack/esql/optimizer/LogicalPlanOptimizerTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 1d7114291b9fa..3d1e0f987450a 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -9983,7 +9983,7 @@ public void testTopSnippetsQueryFoldableConcatConstants() { var failures = LogicalVerifier.INSTANCE.verify(plan, plan.output()); assertThat(failures.failures(), is(empty())); } - + public void testPushDownSampleAndLimitThroughUriParts() { assumeTrue("requires compound output capability", EsqlCapabilities.Cap.URI_PARTS_COMMAND.isEnabled()); var query = "FROM test | URI_PARTS parts = \"http://example.com/foo/bar?baz=qux\" | SAMPLE .5";