Skip to content
This repository was archived by the owner on Nov 7, 2025. It is now read-only.

Commit c689854

Browse files
trzysiektrzysiek
andauthored
[Ingest] Accept strings with numeric values (within range) for numeric columns (#1358)
Before: dots weren't shown on 2 maps, and we had only nulls in the Clickhouse table (as we somehow get `"35.5"`, not `35.5` in ingest for geopoints, and previous logic put all of that to the attributes, leaving columns as `NULLs`) <img width="1726" alt="Screenshot 2025-03-09 at 13 44 32" src="https://github.com/user-attachments/assets/967db4dc-d946-4379-a4db-a46a45927e1d" /> After: <img width="1723" alt="Screenshot 2025-03-08 at 14 39 38" src="https://github.com/user-attachments/assets/8ca15ee9-9033-46ee-b2b5-5b05891a11d4" /> <img width="1728" alt="Screenshot 2025-03-08 at 14 42 08" src="https://github.com/user-attachments/assets/221cdcf7-a38f-4a30-abcf-cba7454744c7" /> <img width="1728" alt="Screenshot 2025-03-08 at 14 42 50" src="https://github.com/user-attachments/assets/cdd2be7b-e9ee-4aac-9d83-98b90d7b6925" /> --------- Co-authored-by: trzysiek <[email protected]>
1 parent 6c80a67 commit c689854

File tree

4 files changed

+50
-3
lines changed

4 files changed

+50
-3
lines changed

ci/it/testcases/test_ingest_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func (a *IngestTypesTestcase) testSupportedTypesInDefaultSetup(ctx context.Conte
309309
resp, bytes := a.RequestToQuesma(ctx, t, "GET", "/"+indexName+"/_search", []byte(`
310310
{ "query": { "match_all": {} } }
311311
`))
312-
assert.Equal(t, http.StatusOK, resp.StatusCode)
312+
assert.Equal(t, http.StatusOK, resp.StatusCode, "response status code: %d, response body: %s", resp.StatusCode, string(bytes))
313313

314314
r.querySuccess = true
315315

platform/ingest/ingest_validator.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/QuesmaOrg/quesma/platform/clickhouse"
99
"github.com/QuesmaOrg/quesma/platform/logger"
1010
"github.com/QuesmaOrg/quesma/platform/types"
11+
"github.com/QuesmaOrg/quesma/platform/util"
1112
"math"
1213
)
1314

@@ -94,6 +95,28 @@ func validateNumericType(columnType string, incomingValueType string, value inte
9495
if isIntegerType(columnType) && isIntegerType(incomingValueType) {
9596
return validateNumericRange(columnType, value)
9697
}
98+
99+
// numbers incoming as strings
100+
if isFloatingPointType(columnType) && incomingValueType == "String" {
101+
if valueAsStr, ok := value.(string); ok {
102+
return util.IsFloat(valueAsStr)
103+
} else {
104+
logger.Error().Msgf("Invalid value type for column of type %s: %T, value: %v", columnType, value, value)
105+
}
106+
}
107+
if isIntegerType(columnType) && incomingValueType == "String" {
108+
if valueAsStr, ok := value.(string); ok && util.IsInt(valueAsStr) {
109+
valueAsInt, err := util.ToInt64(valueAsStr)
110+
if err != nil {
111+
logger.Error().Msgf("Failed to convert value to int: %v", valueAsStr)
112+
return false
113+
}
114+
return validateNumericRange(columnType, valueAsInt)
115+
} else {
116+
logger.Error().Msgf("Invalid value type for column of type %s: %T, value: %v", columnType, value, value)
117+
}
118+
}
119+
97120
return false
98121
}
99122

platform/ingest/ingest_validator_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ func TestIngestValidation(t *testing.T) {
6969
`{"int32_field":15}`,
7070
`{"float_field":7.5}`,
7171
`{"float_field":15}`,
72+
`{"float_field":"15"}`,
73+
`{"float_field":"15.55"}`,
7274

7375
`{"int32_field":2147483647}`,
7476
`{"int32_field":2147483648}`,
@@ -126,7 +128,7 @@ func TestIngestValidation(t *testing.T) {
126128
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"int_field":15}`, tableName),
127129
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"int_field":15}`, tableName),
128130

129-
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"attributes_values":{"int_field":"15"},"attributes_metadata":{"int_field":"v1;String"}}`, tableName),
131+
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"int_field":"15"}`, tableName),
130132
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"attributes_values":{"int_field":"1.5"},"attributes_metadata":{"int_field":"v1;String"}}`, tableName),
131133
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"attributes_values":{"string_field":"15"},"attributes_metadata":{"string_field":"v1;Int64"}}`, tableName),
132134
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"attributes_values":{"string_field":"1.5"},"attributes_metadata":{"string_field":"v1;Float64"}}`, tableName),
@@ -138,6 +140,8 @@ func TestIngestValidation(t *testing.T) {
138140
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"int32_field":15}`, tableName),
139141
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"float_field":7.5}`, tableName),
140142
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"float_field":15}`, tableName),
143+
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"float_field":"15"}`, tableName),
144+
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"float_field":"15.55"}`, tableName),
141145

142146
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"int32_field":2147483647}`, tableName),
143147
fmt.Sprintf(`INSERT INTO "%s" FORMAT JSONEachRow {"attributes_values":{"int32_field":"2147483648"},"attributes_metadata":{"int32_field":"v1;Int64"}}`, tableName),

platform/util/maths.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
// SPDX-License-Identifier: Elastic-2.0
33
package util
44

5-
import "math"
5+
import (
6+
"math"
7+
"strconv"
8+
)
69

710
// IsSmaller checks if a is smaller than b (with a small epsilon, due to float inaccuracies)
811
func IsSmaller(a, b float64) bool {
@@ -14,3 +17,20 @@ func IsSmaller(a, b float64) bool {
1417
func IsFloat64AnInt64(f float64) bool {
1518
return f == math.Ceil(f) && f <= float64(math.MaxInt64)
1619
}
20+
21+
// IsFloat checks if a string is a float
22+
func IsFloat(s string) bool {
23+
_, err := strconv.ParseFloat(s, 64)
24+
return err == nil
25+
}
26+
27+
// IsInt checks if a string is an integer (in range of int64)
28+
func IsInt(s string) bool {
29+
_, err := strconv.ParseInt(s, 10, 64)
30+
return err == nil
31+
}
32+
33+
// ToInt64 converts a string to int64
34+
func ToInt64(s string) (int64, error) {
35+
return strconv.ParseInt(s, 10, 64)
36+
}

0 commit comments

Comments
 (0)