From 7045bf575e54c315aa616e98dd31acf27751edf9 Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 03:28:57 -0700 Subject: [PATCH 1/2] fix: decode escaped characters in Lucene range bounds Range bounds skipped decodeSpecialTokens, so an escaped colon or backslash reached ClickHouse as the HDX_COLON / HDX_BACKSLASH_LITERAL placeholder. Decode them like other terms. --- .changeset/range-bound-escapes.md | 12 ++++++++++++ .../common-utils/src/__tests__/queryParser.test.ts | 4 ++++ packages/common-utils/src/queryParser.ts | 4 ++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .changeset/range-bound-escapes.md diff --git a/.changeset/range-bound-escapes.md b/.changeset/range-bound-escapes.md new file mode 100644 index 0000000000..39b9e79cec --- /dev/null +++ b/.changeset/range-bound-escapes.md @@ -0,0 +1,12 @@ +--- +'@hyperdx/common-utils': patch +'@hyperdx/api': patch +'@hyperdx/app': patch +--- + +fix: decode escaped characters in Lucene range bounds + +A range bound with an escaped colon, such as +`Timestamp:[2024-01-01T10\:00\:00 TO *]`, sent the internal placeholder to +ClickHouse (`'2024-01-01T10HDX_COLON00HDX_COLON00'`) and the query failed. Range +bounds are now decoded the same way as other search terms. diff --git a/packages/common-utils/src/__tests__/queryParser.test.ts b/packages/common-utils/src/__tests__/queryParser.test.ts index c3f897b3db..d2f256d4a3 100644 --- a/packages/common-utils/src/__tests__/queryParser.test.ts +++ b/packages/common-utils/src/__tests__/queryParser.test.ts @@ -842,6 +842,10 @@ describe('CustomSchemaSQLSerializerV2 - range bounds', () => { lucene: 'Timestamp:{2024-01-01 TO 2024-06-01}', sql: "((Timestamp > '2024-01-01' AND Timestamp < '2024-06-01'))", }, + { + lucene: 'Timestamp:[2024-01-01T10\\:00\\:00 TO *]', + sql: "((Timestamp >= '2024-01-01T10:00:00'))", + }, { lucene: 'LogAttributes.duration_ms:{100 TO 500}', sql: "((`LogAttributes`['duration_ms'] > 100 AND `LogAttributes`['duration_ms'] < 500 AND indexHint(mapContains(`LogAttributes`, 'duration_ms'))))", diff --git a/packages/common-utils/src/queryParser.ts b/packages/common-utils/src/queryParser.ts index 61326a599a..37cc098afa 100644 --- a/packages/common-utils/src/queryParser.ts +++ b/packages/common-utils/src/queryParser.ts @@ -2083,8 +2083,8 @@ async function nodeTerm( const rangedTerm = node; return serializer.range( field, - rangedTerm.term_min, - rangedTerm.term_max, + decodeSpecialTokens(rangedTerm.term_min), + decodeSpecialTokens(rangedTerm.term_max), isNegatedField, context, rangedTerm.inclusive, From 1a859cd121f3052671ec5746c04548308491fcfe Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 03:34:00 -0700 Subject: [PATCH 2/2] test: cover an unescaped localhost:port range bound --- packages/common-utils/src/__tests__/queryParser.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/common-utils/src/__tests__/queryParser.test.ts b/packages/common-utils/src/__tests__/queryParser.test.ts index d2f256d4a3..b6bb091786 100644 --- a/packages/common-utils/src/__tests__/queryParser.test.ts +++ b/packages/common-utils/src/__tests__/queryParser.test.ts @@ -846,6 +846,10 @@ describe('CustomSchemaSQLSerializerV2 - range bounds', () => { lucene: 'Timestamp:[2024-01-01T10\\:00\\:00 TO *]', sql: "((Timestamp >= '2024-01-01T10:00:00'))", }, + { + lucene: 'Host:[localhost:3000 TO localhost:3999]', + sql: "((Host BETWEEN 'localhost:3000' AND 'localhost:3999'))", + }, { lucene: 'LogAttributes.duration_ms:{100 TO 500}', sql: "((`LogAttributes`['duration_ms'] > 100 AND `LogAttributes`['duration_ms'] < 500 AND indexHint(mapContains(`LogAttributes`, 'duration_ms'))))",