From 1c7af69f4eece78b37366037cf8e832e66a3ea02 Mon Sep 17 00:00:00 2001
From: Ryan Ernst <ryan@iernst.net>
Date: Fri, 9 May 2025 11:13:32 -0700
Subject: [PATCH 1/3] Avoid nested docs in painless execute

Painless does not support accessing nested docs (except through
_source). Yet the painless execute api indexes any nested docs that are
found when parsing the sample document. This commit changes the ram
indexing to only index the root document, ignoring any nested docs.

fixes #41004
---
 .../action/PainlessExecuteAction.java         |  3 ++-
 .../action/PainlessExecuteApiTests.java       | 21 +++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java
index 941d97700dd93..7be09ed5a2e98 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java
@@ -831,7 +831,8 @@ private static Response prepareRamIndex(
                     // This is a problem especially for indices that have no mappings, as no fields will be accessible, neither through doc
                     // nor _source (if there are no mappings there are no metadata fields).
                     ParsedDocument parsedDocument = documentMapper.parse(sourceToParse);
-                    indexWriter.addDocuments(parsedDocument.docs());
+                    // only index the root doc since nested docs are not supported in painless anyways
+                    indexWriter.addDocuments(List.of(parsedDocument.rootDoc()));
                     try (IndexReader indexReader = DirectoryReader.open(indexWriter)) {
                         final IndexSearcher searcher = new IndexSearcher(indexReader);
                         searcher.setQueryCache(null);
diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java
index 56dec6cf4a58d..f38c69f31d63f 100644
--- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java
+++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java
@@ -70,6 +70,27 @@ public void testDefaults() throws IOException {
         assertThat(e.getCause().getMessage(), equalTo("cannot resolve symbol [doc]"));
     }
 
+    public void testNestedDocs() throws IOException {
+        ScriptService scriptService = getInstanceFromNode(ScriptService.class);
+        IndexService indexService = createIndex("index", Settings.EMPTY, "doc", "rank", "type=long", "nested", "type=nested");
+
+        Request.ContextSetup contextSetup = new Request.ContextSetup("index", new BytesArray("""
+            {"rank": 4.0, "nested": [{"text": "foo"}, {"text": "bar"}]}"""), new MatchAllQueryBuilder());
+        contextSetup.setXContentType(XContentType.JSON);
+        Request request = new Request(
+            new Script(
+                ScriptType.INLINE,
+                "painless",
+                "doc['rank'].value",
+                Map.of()
+            ),
+            "score",
+            contextSetup
+        );
+        Response response = innerShardOperation(request, scriptService, indexService);
+        assertThat(response.getResult(), equalTo(4.0D));
+    }
+
     public void testFilterExecutionContext() throws IOException {
         ScriptService scriptService = getInstanceFromNode(ScriptService.class);
         IndexService indexService = createIndex("index", Settings.EMPTY, "doc", "field", "type=long");

From 4700660f205cbdbb7a04ad2e99d32a04f585d6cf Mon Sep 17 00:00:00 2001
From: Ryan Ernst <ryan@iernst.net>
Date: Fri, 9 May 2025 11:18:00 -0700
Subject: [PATCH 2/3] Update docs/changelog/127991.yaml

---
 docs/changelog/127991.yaml | 6 ++++++
 1 file changed, 6 insertions(+)
 create mode 100644 docs/changelog/127991.yaml

diff --git a/docs/changelog/127991.yaml b/docs/changelog/127991.yaml
new file mode 100644
index 0000000000000..dead04164ccab
--- /dev/null
+++ b/docs/changelog/127991.yaml
@@ -0,0 +1,6 @@
+pr: 127991
+summary: Avoid nested docs in painless execute api
+area: Infra/Scripting
+type: bug
+issues:
+ - 41004

From c7dd2f9cdef9f9be39d11c7e3f6b4e212714b0f2 Mon Sep 17 00:00:00 2001
From: elasticsearchmachine <infra-root+elasticsearchmachine@elastic.co>
Date: Fri, 9 May 2025 18:27:46 +0000
Subject: [PATCH 3/3] [CI] Auto commit changes from spotless

---
 .../painless/action/PainlessExecuteApiTests.java      | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java
index f38c69f31d63f..b6c1696ed55b4 100644
--- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java
+++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteApiTests.java
@@ -77,16 +77,7 @@ public void testNestedDocs() throws IOException {
         Request.ContextSetup contextSetup = new Request.ContextSetup("index", new BytesArray("""
             {"rank": 4.0, "nested": [{"text": "foo"}, {"text": "bar"}]}"""), new MatchAllQueryBuilder());
         contextSetup.setXContentType(XContentType.JSON);
-        Request request = new Request(
-            new Script(
-                ScriptType.INLINE,
-                "painless",
-                "doc['rank'].value",
-                Map.of()
-            ),
-            "score",
-            contextSetup
-        );
+        Request request = new Request(new Script(ScriptType.INLINE, "painless", "doc['rank'].value", Map.of()), "score", contextSetup);
         Response response = innerShardOperation(request, scriptService, indexService);
         assertThat(response.getResult(), equalTo(4.0D));
     }