Skip to content

[BUG] Zero results returned for nested text-keyword field blends #5702

Description

@Swiddis

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?

  • mainline

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    PPLPiped processing languagebugSomething isn't workinguntriaged

    Type

    No type

    Projects

    Status
    Not Started

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions