Skip to content

Commit 38286f6

Browse files
authored
fix: searching json number property error (#1767)
1 parent 051276f commit 38286f6

File tree

3 files changed

+81
-10
lines changed

3 files changed

+81
-10
lines changed

.changeset/twelve-seahorses-do.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
fix: searching json number property error

packages/app/src/components/DBRowJsonViewer.test.tsx

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { fireEvent, screen, within } from '@testing-library/react';
33

4-
import { DBRowJsonViewer } from './DBRowJsonViewer';
4+
import { buildJSONExtractQuery, DBRowJsonViewer } from './DBRowJsonViewer';
55
import { RowSidePanelContext } from './DBRowSidePanel';
66

77
// Mock Next.js router
@@ -195,4 +195,57 @@ describe('DBRowJsonViewer', () => {
195195
);
196196
});
197197
});
198+
199+
describe('buildJSONExtractQuery', () => {
200+
it('returns null when keyPath equals parsedJsonRootPath (no nested path)', () => {
201+
expect(
202+
buildJSONExtractQuery(['LogAttributes'], ['LogAttributes']),
203+
).toBeNull();
204+
});
205+
206+
it('returns null when keyPath is shorter than parsedJsonRootPath', () => {
207+
expect(buildJSONExtractQuery([], ['LogAttributes'])).toBeNull();
208+
});
209+
210+
it('builds query for single-level path with default JSONExtractString', () => {
211+
expect(
212+
buildJSONExtractQuery(['LogAttributes', 'field1'], ['LogAttributes']),
213+
).toBe("JSONExtractString(LogAttributes, 'field1')");
214+
});
215+
216+
it('builds query for nested path', () => {
217+
expect(
218+
buildJSONExtractQuery(
219+
['LogAttributes', 'nested', 'field3'],
220+
['LogAttributes'],
221+
),
222+
).toBe("JSONExtractString(LogAttributes, 'nested', 'field3')");
223+
});
224+
225+
it('uses JSONExtractFloat when specified', () => {
226+
expect(
227+
buildJSONExtractQuery(
228+
['SpanAttributes', 'count'],
229+
['SpanAttributes'],
230+
'JSONExtractFloat',
231+
),
232+
).toBe("JSONExtractFloat(SpanAttributes, 'count')");
233+
});
234+
235+
it('uses JSONExtractBool when specified', () => {
236+
expect(
237+
buildJSONExtractQuery(
238+
['LogAttributes', 'enabled'],
239+
['LogAttributes'],
240+
'JSONExtractBool',
241+
),
242+
).toBe("JSONExtractBool(LogAttributes, 'enabled')");
243+
});
244+
245+
it('handles path segments that look like array indices', () => {
246+
expect(
247+
buildJSONExtractQuery(['LogAttributes', '0', 'id'], ['LogAttributes']),
248+
).toBe("JSONExtractString(LogAttributes, '0', 'id')");
249+
});
250+
});
198251
});

packages/app/src/components/DBRowJsonViewer.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@ import {
3030
import HyperJson, { GetLineActions, LineAction } from '@/components/HyperJson';
3131
import { mergePath } from '@/utils';
3232

33-
function buildJSONExtractStringQuery(
33+
type JSONExtractFn =
34+
| 'JSONExtractString'
35+
| 'JSONExtractFloat'
36+
| 'JSONExtractBool';
37+
38+
export function buildJSONExtractQuery(
3439
keyPath: string[],
3540
parsedJsonRootPath: string[],
41+
jsonExtractFn: JSONExtractFn = 'JSONExtractString',
3642
): string | null {
3743
const nestedPath = keyPath.slice(parsedJsonRootPath.length);
3844
if (nestedPath.length === 0) {
@@ -41,7 +47,7 @@ function buildJSONExtractStringQuery(
4147

4248
const baseColumn = parsedJsonRootPath[parsedJsonRootPath.length - 1];
4349
const jsonPathArgs = nestedPath.map(p => `'${p}'`).join(', ');
44-
return `JSONExtractString(${baseColumn}, ${jsonPathArgs})`;
50+
return `${jsonExtractFn}(${baseColumn}, ${jsonPathArgs})`;
4551
}
4652

4753
import { RowSidePanelContext } from './DBRowSidePanel';
@@ -258,7 +264,7 @@ export function DBRowJsonViewer({
258264

259265
// Handle parsed JSON from string columns using JSONExtractString
260266
if (isInParsedJson && parsedJsonRootPath) {
261-
const jsonQuery = buildJSONExtractStringQuery(
267+
const jsonQuery = buildJSONExtractQuery(
262268
keyPath,
263269
parsedJsonRootPath,
264270
);
@@ -301,10 +307,20 @@ export function DBRowJsonViewer({
301307

302308
// Handle parsed JSON from string columns using JSONExtractString
303309
if (isInParsedJson && parsedJsonRootPath) {
304-
const jsonQuery = buildJSONExtractStringQuery(
310+
let jsonExtractFn: JSONExtractFn = 'JSONExtractString';
311+
312+
if (typeof value === 'number') {
313+
jsonExtractFn = 'JSONExtractFloat';
314+
} else if (typeof value === 'boolean') {
315+
jsonExtractFn = 'JSONExtractBool';
316+
}
317+
318+
const jsonQuery = buildJSONExtractQuery(
305319
keyPath,
306320
parsedJsonRootPath,
321+
jsonExtractFn,
307322
);
323+
308324
if (jsonQuery) {
309325
searchFieldPath = jsonQuery;
310326
}
@@ -342,7 +358,7 @@ export function DBRowJsonViewer({
342358

343359
// Handle parsed JSON from string columns using JSONExtractString
344360
if (isInParsedJson && parsedJsonRootPath) {
345-
const jsonQuery = buildJSONExtractStringQuery(
361+
const jsonQuery = buildJSONExtractQuery(
346362
keyPath,
347363
parsedJsonRootPath,
348364
);
@@ -368,10 +384,7 @@ export function DBRowJsonViewer({
368384

369385
// Handle parsed JSON from string columns using JSONExtractString
370386
if (isInParsedJson && parsedJsonRootPath) {
371-
const jsonQuery = buildJSONExtractStringQuery(
372-
keyPath,
373-
parsedJsonRootPath,
374-
);
387+
const jsonQuery = buildJSONExtractQuery(keyPath, parsedJsonRootPath);
375388
if (jsonQuery) {
376389
columnFieldPath = jsonQuery;
377390
}

0 commit comments

Comments
 (0)