What is the bug?
Field merging causes zero matches to happen when there's a script pushdown involving text/keyword mixed-type docs on nested fields.
This was fixed in #5358 for non-nested fields, but not for nested ones, as DeepMergeRule follows different rules. The pushed down script attempts to fetch doc_values for the text, which doesn't exist for this field type.
How can one reproduce the bug?
Here's a repro script. The tldr is that we create two documents matching {"log":{"user_agent": "requests/2.32.3"}} with a keyword/text mismatch and attempt to search for both with where log.user_agent = "requests/2.32.3". Output at the end.
#!/bin/bash
set -e
OS_URL="http://localhost:9200"
echo "=== OpenSearch Version ==="
curl -s "$OS_URL" | jq -r '.version.number'
echo -e "\n=== Cleanup ==="
curl -s -X DELETE "$OS_URL/test_*" > /dev/null 2>&1 || true
echo -e "\n=== Test 1: Top-level field conflict ==="
echo "Creating test_flat_1 with text+keyword subfield..."
curl -s -X PUT "$OS_URL/test_flat_1" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"user_agent": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}' | jq -r '.acknowledged'
echo "Creating test_flat_2 with keyword only..."
curl -s -X PUT "$OS_URL/test_flat_2" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"user_agent": {
"type": "keyword"
}
}
}
}' | jq -r '.acknowledged'
echo "Indexing documents..."
curl -s -X POST "$OS_URL/test_flat_1/_doc" -H 'Content-Type: application/json' -d'
{"user_agent": "requests/2.32.3"}
' > /dev/null
curl -s -X POST "$OS_URL/test_flat_2/_doc" -H 'Content-Type: application/json' -d'
{"user_agent": "requests/2.32.3"}
' > /dev/null
curl -s -X POST "$OS_URL/_refresh" > /dev/null
echo "Query: source=test_flat_* | where user_agent = \"requests/2.32.3\""
RESULT=$(curl -s -X POST "$OS_URL/_plugins/_ppl" -H 'Content-Type: application/json' -d'
{
"query": "source=test_flat_* | where user_agent = \"requests/2.32.3\""
}
' | jq -r '.total')
echo "Result: $RESULT rows (expected: 2)"
if [ "$RESULT" -eq 2 ]; then
echo "PASS: Top-level field conflict handled correctly"
else
echo "FAIL: Expected 2 rows, got $RESULT"
fi
echo -e "\n=== Test 2: Nested field conflict ==="
echo "Creating test_nested_1 with nested text+keyword subfield..."
curl -s -X PUT "$OS_URL/test_nested_1" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"log": {
"properties": {
"user_agent": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}' | jq -r '.acknowledged'
echo "Creating test_nested_2 with nested keyword only..."
curl -s -X PUT "$OS_URL/test_nested_2" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"log": {
"properties": {
"user_agent": {
"type": "keyword"
}
}
}
}
}
}' | jq -r '.acknowledged'
echo "Indexing documents..."
curl -s -X POST "$OS_URL/test_nested_1/_doc" -H 'Content-Type: application/json' -d'
{"log": {"user_agent": "requests/2.32.3"}}
' > /dev/null
curl -s -X POST "$OS_URL/test_nested_2/_doc" -H 'Content-Type: application/json' -d'
{"log": {"user_agent": "requests/2.32.3"}}
' > /dev/null
curl -s -X POST "$OS_URL/_refresh" > /dev/null
echo "Query: source=test_nested_* | where log.user_agent = \"requests/2.32.3\""
RESULT=$(curl -s -X POST "$OS_URL/_plugins/_ppl" -H 'Content-Type: application/json' -d'
{
"query": "source=test_nested_* | where log.user_agent = \"requests/2.32.3\""
}
' | jq -r '.total')
echo "Result: $RESULT rows (expected: 2)"
if [ "$RESULT" -eq 2 ]; then
echo "PASS: Nested field conflict handled correctly"
else
echo "FAIL: Expected 2 rows, got $RESULT"
fi
echo -e "\n=== Field Capabilities ==="
echo "Top-level field:"
curl -s "$OS_URL/test_flat_*/_field_caps?fields=user_agent" | jq '.fields.user_agent'
echo -e "\nNested field:"
curl -s "$OS_URL/test_nested_*/_field_caps?fields=log.user_agent" | jq '.fields["log.user_agent"]'
echo -e "\n=== Cleanup ==="
curl -s -X DELETE "$OS_URL/test_*" > /dev/null 2>&1
echo "Test indices deleted"
What is the expected behavior?
Should correctly resolve the field & return 2 results.
What is your host/environment?
Do you have any screenshots?
N/A
Do you have any additional context?
Related: #4659
=== OpenSearch Version ===
3.9.0-SNAPSHOT
=== Cleanup ===
=== Test 1: Top-level field conflict ===
Creating test_flat_1 with text+keyword subfield...
true
Creating test_flat_2 with keyword only...
true
Indexing documents...
Query: source=test_flat_* | where user_agent = "requests/2.32.3"
Result: 2 rows (expected: 2)
PASS: Top-level field conflict handled correctly
=== Test 2: Nested field conflict ===
Creating test_nested_1 with nested text+keyword subfield...
true
Creating test_nested_2 with nested keyword only...
true
Indexing documents...
Query: source=test_nested_* | where log.user_agent = "requests/2.32.3"
Result: 0 rows (expected: 2)
FAIL: Expected 2 rows, got 0
=== Field Capabilities ===
Top-level field:
{
"text": {
"type": "text",
"searchable": true,
"aggregatable": false,
"indices": [
"test_flat_1"
]
},
"keyword": {
"type": "keyword",
"searchable": true,
"aggregatable": true,
"indices": [
"test_flat_2"
]
}
}
Nested field:
{
"text": {
"type": "text",
"searchable": true,
"aggregatable": false,
"indices": [
"test_nested_1"
]
},
"keyword": {
"type": "keyword",
"searchable": true,
"aggregatable": true,
"indices": [
"test_nested_2"
]
}
}
=== Cleanup ===
Test indices deleted
What is the bug?
Field merging causes zero matches to happen when there's a script pushdown involving text/keyword mixed-type docs on nested fields.
This was fixed in #5358 for non-nested fields, but not for nested ones, as DeepMergeRule follows different rules. The pushed down script attempts to fetch
doc_values for the text, which doesn't exist for this field type.How can one reproduce the bug?
Here's a repro script. The tldr is that we create two documents matching
{"log":{"user_agent": "requests/2.32.3"}}with a keyword/text mismatch and attempt to search for both withwhere log.user_agent = "requests/2.32.3". Output at the end.What is the expected behavior?
Should correctly resolve the field & return 2 results.
What is your host/environment?
Do you have any screenshots?
N/A
Do you have any additional context?
Related: #4659