Lucene search on a numeric- or Bool-valued Map column double-escapes the map subscript
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 68/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Tranquilla
- Stack tecnologico
- clickhouse, typescript
- Ambito
- backend, databases, testing-qa
Direzione di ricerca
Il codice interessato si trova in packages/common-utils/src/queryParser.ts, in SQLSerializer.eq e CustomSchemaSQLSerializerV2.fieldSearch; inizia leggendo i tre punti di chiamata di ?? insieme ai rami di interpolazione corrispondenti. Estendi packages/common-utils/src/tests/queryParser.test.ts intorno al caso Map(String, Float64) con ricerche in map numeriche senza virgolette e negate, oltre a ricerche in map Bool. Il lavoro è completato quando i predicati generati non fanno più il doppio escaping dei pedici delle map e i test Jest pertinenti superano.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
What happens
Searching a Map column whose value type is numeric or Bool generates SQL in which the map subscript is wrapped in an extra layer of backticks, with the inner ones doubled. ClickHouse then reads it as a single identifier literally named `Measures`['latency_ms'] rather than as a map subscript, so the predicate cannot resolve.
The tell is that the same search behaves differently depending only on quoting:
| Search | Generated predicate |
|---|---|
Measures.latency_ms:250 |
Measures``['latency_ms']` = CAST('250', 'Float64') ``` ❌ |
Measures.latency_ms:"250" |
`Measures`['latency_ms'] = CAST('250', 'Float64') ✅ |
Measures.latency_ms:>250 |
`Measures`['latency_ms'] > '250' ✅ |
-Measures.latency_ms:250 |
Measures``['latency_ms']` != CAST('250', 'Float64') ``` ❌ |
Flags.cached:true |
Flags``['cached']` = 1 ``` ❌ |
Flags.cached:"true" |
Flags``['cached']` = 1 ``` ❌ |
For a Map(String, Bool) column both the quoted and unquoted forms are affected, so there is no working spelling of a Bool-map search. Map(String, String) columns are unaffected (Attributes.host:web1 → `Attributes`['host'] ILIKE '%web1%'), as are plain non-map columns.
Reproduced against main @ e73af381.
Cause
Three call sites pass the already-rendered column expression as a SqlString ?? placeholder, which applies escapeId to it a second time:
packages/common-utils/src/queryParser.ts:502—SQLSerializer.eq,Boolbranchpackages/common-utils/src/queryParser.ts:1384—CustomSchemaSQLSerializerV2.fieldSearch,Boolbranchpackages/common-utils/src/queryParser.ts:1396—CustomSchemaSQLSerializerV2.fieldSearch,Numberbranch
getColumnForField() returns column already rendered — `Measures`['latency_ms'] for a map key, and the bare name for a plain column. Every other branch interpolates it raw: eq/Number (:514), gte (:597), lte, lt, gt, range (:731) and the final ILIKE (:1554). Those three are the odd ones out.
git log -L shows :514 used ${column} and :1396 used ?? from the initial common-utils commit (6ee29abe) — the paths look copy-pasted and then diverged, rather than deliberately different.
Why the tests don't catch it
queryParser.test.ts:2735 (falls back for Map(String, Float64) value type) uses the quoted form NumericAttributes.count:"42", which routes to the correct eq/Number path, and asserts only not.toContain('has(') / toContain('CAST'). No test uses an unquoted numeric-map term, and no test uses a Map(String, Bool) column at all.
Repro
Drop this in packages/common-utils/src/__tests__/ and run yarn jest --ci zzprobe:
import { ClickhouseClient } from '@/clickhouse/node';
import { getMetadata } from '@/core/metadata';
import { CustomSchemaSQLSerializerV2, SearchQueryBuilder } from '@/queryParser';
describe('probe', () => {
function buildSerializer() {
const metadata = getMetadata(new ClickhouseClient({ host: 'http://localhost:8123' }));
metadata.getColumn = jest.fn().mockImplementation(async ({ column }) => {
if (column === 'Measures') return { name: 'Measures', type: 'Map(String, Float64)' };
if (column === 'Flags') return { name: 'Flags', type: 'Map(String, Bool)' };
if (column === 'Body') return { name: 'Body', type: 'String' };
return undefined;
});
metadata.getMaterializedColumnsLookupTable = jest.fn().mockImplementation(async () => new Map());
metadata.getColumns = jest.fn().mockImplementation(async () => [
{ name: 'Measures', type: 'Map(String, Float64)', default_type: '', default_expression: '' },
{ name: 'Flags', type: 'Map(String, Bool)', default_type: '', default_expression: '' },
]);
metadata.getSkipIndices = jest.fn().mockImplementation(async () => []);
metadata.getSetting = jest.fn().mockImplementation(async () => '0');
metadata.getServerVersion = jest.fn().mockImplementation(async () => [26, 5, 0, 0] as const);
return new CustomSchemaSQLSerializerV2({
metadata,
databaseName: 'default',
tableName: 'otel_logs',
connectionId: 'test',
implicitColumnExpression: 'Body',
});
}
it.each([
'Measures.latency_ms:250',
'Measures.latency_ms:"250"',
'Flags.cached:true',
])('probe %s', async q => {
process.stdout.write(`\n${q}\n ${await new SearchQueryBuilder(q, buildSerializer()).build()}\n`);
expect(1).toBe(1);
});
});
Two possible fixes — I'd rather you picked
I have a patch ready either way, but the choice is yours to make because they differ in an observable way:
A. Match the sibling branches — swap ?? for ${column} at the three sites. Smallest possible diff and makes all branches consistent. But for plain (non-map) columns those three branches currently get their backticks from escapeId, so this drops them: IsError:true would go from `IsError` = 1 to IsError = 1. That matches what the ILIKE, gt/lt and range branches already emit today (ServiceName ILIKE '%foo%', unquoted), so it is consistent — but it does remove quoting that is currently there, which would bite a column named e.g. order.
B. Escape only when it is a bare column name — keep escapeId for plain columns and interpolate raw only for a rendered map expression (mapKeyIndexExpression is already available in fieldSearch as the discriminator). Preserves today's quoting for plain columns; slightly larger diff, and leaves the serializer inconsistent about quoting overall.
There is also a C — have getColumnForField() return a uniformly-escaped expression and interpolate raw everywhere — which is the tidiest end state but a much wider change.
Happy to open a PR with whichever you prefer, with tests covering unquoted numeric-map, negated numeric-map, and Bool-map searches in both directions. Just say which.
Found while working on #2764. This report was AI-assisted, per the AI-Assisted Development section of CONTRIBUTING.md; every SQL string above was executed, not inferred.
- Lingua principale
- TypeScript
- Stelle
- 10k
- Fork
- 501
- Merge medio
- 2g 3h
- PR unite (30g)
- 152
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di hyperdxio/hyperdx
-
Metric chart with aggFn increase and a multi-column group-by fails: toString() gets several argumentsForse già presa @CG-5228 l’ha presa 1 giorno fa. Apertaexternal
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 2 giorni
-
external
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 2 giorni
-
Slack notifications for inline alerts fail when the chart-explorer link passes Slack's 3000-character block limitForse già presa @Tyagiquamar l’ha presa 2 giorni fa. Apertaalerting bug external
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 2 giorni
-
bug external Needs Feedback
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
hyperdxio/hyperdx#3308 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
external
Difficoltà 1/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di hyperdxio/hyperdx
Issue simili
-
area: server priority: P3 type: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 83/100
appandflow/stim#4538 ·
I maintainer di solito rispondono entro 1 giorno
-
[BUG] Published Docker images lack the redis extra: HEADROOM_COMPRESS_SESSION_STORE stops startupForse già presa @AlessandroSpallina l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
headroomlabs-ai/headroom#4513 ·
I maintainer di solito rispondono entro 1 giorno
-
bug extension extension: obsidian platform: Windows
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
raycast/extensions#32838 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
copy design-review mac P3
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
tosunlab/hisarapp-core#510 ·
-
bug priority-medium to-agent
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno