Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions integ-test/src/test/java/org/opensearch/sql/ppl/DataTypeIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,47 @@ public void testNumericFieldFromString() throws Exception {
client().performRequest(deleteRequest);
}

@Test
public void testBooleanFieldFromNumberAcrossWildcardIndices() throws Exception {
// Reproduce issue #5269: querying across indices where same field has conflicting types
// (boolean vs text) and the text-typed index stores a numeric value like 0.
String indexBool = "repro_bool_test_bb";
String indexText = "repro_bool_test_aa";

try {
// Create index with boolean mapping
Request createBool = new Request("PUT", "/" + indexBool);
createBool.setJsonEntity(
"{\"mappings\":{\"properties\":{\"flag\":{\"type\":\"boolean\"},"
+ "\"startTime\":{\"type\":\"date_nanos\"}}}}");
client().performRequest(createBool);

// Create index with text mapping
Request createText = new Request("PUT", "/" + indexText);
createText.setJsonEntity(
"{\"mappings\":{\"properties\":{\"flag\":{\"type\":\"text\"},"
+ "\"startTime\":{\"type\":\"date_nanos\"}}}}");
client().performRequest(createText);

// Insert boolean value into boolean-typed index
Request insertBool = new Request("PUT", "/" + indexBool + "/_doc/1?refresh=true");
insertBool.setJsonEntity("{\"startTime\":\"2026-03-25T20:25:00.000Z\",\"flag\":false}");
client().performRequest(insertBool);

// Insert numeric value into text-typed index
Request insertText = new Request("PUT", "/" + indexText + "/_doc/1?refresh=true");
insertText.setJsonEntity("{\"startTime\":\"2026-03-24T20:25:00.000Z\",\"flag\":0}");
client().performRequest(insertText);

// Query across both indices with wildcard — should not throw an error
JSONObject result = executeQuery("source=repro_bool_test_* | fields flag");
assertEquals(2, result.getJSONArray("datarows").length());
} finally {
client().performRequest(new Request("DELETE", "/" + indexBool));
client().performRequest(new Request("DELETE", "/" + indexText));
}
}

@Test
public void testBooleanFieldFromString() throws Exception {
final int docId = 2;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
setup:
- do:
indices.create:
index: issue5269_bool
body:
settings:
number_of_shards: 1
number_of_replicas: 0
mappings:
properties:
flag:
type: boolean
startTime:
type: date_nanos

- do:
indices.create:
index: issue5269_text
body:
settings:
number_of_shards: 1
number_of_replicas: 0
mappings:
properties:
flag:
type: text
startTime:
type: date_nanos

- do:
bulk:
refresh: true
body:
- '{"index": {"_index": "issue5269_bool", "_id": "1"}}'
- '{"startTime": "2026-03-25T20:25:00.000Z", "flag": false}'
- '{"index": {"_index": "issue5269_text", "_id": "1"}}'
- '{"startTime": "2026-03-24T20:25:00.000Z", "flag": 0}'

---
teardown:
- do:
indices.delete:
index: issue5269_bool
ignore_unavailable: true
- do:
indices.delete:
index: issue5269_text
ignore_unavailable: true

---
"Issue 5269: PPL wildcard query across indices with boolean/text mapping conflict should not error":
- skip:
features:
- headers
- do:
headers:
Content-Type: 'application/json'
ppl:
body:
query: source=issue5269_* | fields flag

- match: { total: 2 }
- length: { datarows: 2 }
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ private boolean parseBooleanValue(JsonNode node) {
return node.booleanValue();
} else if (node.isTextual()) {
return Boolean.parseBoolean(node.textValue());
} else if (node.isNumber()) {
return node.intValue() != 0;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("node '{}' must be a boolean", node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ public void constructIp() {
public void constructBoolean() {
assertAll(
() -> assertEquals(booleanValue(true), tupleValue("{\"boolV\":true}").get("boolV")),
() -> assertEquals(booleanValue(false), tupleValue("{\"boolV\":false}").get("boolV")),
() -> assertEquals(booleanValue(true), tupleValue("{\"boolV\":1}").get("boolV")),
() -> assertEquals(booleanValue(false), tupleValue("{\"boolV\":0}").get("boolV")),
() -> assertEquals(booleanValue(true), constructFromObject("boolV", true)),
() -> assertEquals(booleanValue(true), constructFromObject("boolV", "true")),
() -> assertEquals(booleanValue(true), constructFromObject("boolV", 1)),
Expand Down
Loading