Skip to content

Commit

Permalink
Support escaped quotes (#214)
Browse files Browse the repository at this point in the history
Previously if you wanted to search something like `message:"{\"json\": \"value\""`, you couldn't as we didn't support escaping double quotes in exact property searches.

I've checked and I couldn't see anyone depending on the prior `\` behavior so while I believe this is breaking, I don't expect anyone to actually get impacted by this practically.
  • Loading branch information
MikeShi42 committed Jan 11, 2024
1 parent 0824822 commit a0dc1b5
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .changeset/angry-seahorses-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@hyperdx/api': minor
'@hyperdx/app': minor
---

Breaking Search Syntax Change: Backslashes will be treated as an escape
character for a double quotes (ex. message:"\"" will search for the double quote
character). Two backslashes will be treated as a backslash literal (ex.
message:\\ will search for the backslash literal)
30 changes: 30 additions & 0 deletions packages/api/src/clickhouse/__tests__/searchQueryParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,36 @@ describe('searchQueryParser', () => {
).toEqual('(1 = 0)');
});

it('parses escaped quotes in quoted searches', async () => {
const ast = parse('foo:"b\\"ar"');
jest.spyOn(propertyTypesMappingsModel, 'get').mockReturnValue('string');
expect(
await genWhereSQL(
ast,
propertyTypesMappingsModel,
'TEAM_ID_UNIT_TESTS',
),
).toEqual(eq("_string_attributes['foo']", 'b\\"ar'));
});

it('parses backslash literals', async () => {
const ast = parse('foo:"b\\\\ar"');
jest.spyOn(propertyTypesMappingsModel, 'get').mockReturnValue('string');
expect(
await genWhereSQL(
ast,
propertyTypesMappingsModel,
'TEAM_ID_UNIT_TESTS',
),
).toEqual(eq("_string_attributes['foo']", 'b\\\\ar'));
});

it('does not escape quotes with backslash literals', async () => {
expect(() => parse('foo:"b\\\\"ar"')).toThrowErrorMatchingInlineSnapshot(
`"Expected \\"\\\\\\"\\", \\"\\\\\\\\\\", or any character but end of input found."`,
);
});

describe('negation', () => {
it('negates property values', async () => {
const ast = parse('-foo:bar');
Expand Down
3 changes: 3 additions & 0 deletions packages/api/src/clickhouse/searchQueryParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import { PropertyTypeMappingsModel } from './propertyTypeMappingsModel';

function encodeSpecialTokens(query: string): string {
return query
.replace(/\\\\/g, 'HDX_BACKSLASH_LITERAL')
.replace('http://', 'http_COLON_//')
.replace('https://', 'https_COLON_//')
.replace(/localhost:(\d{1,5})/, 'localhost_COLON_$1')
.replace(/\\:/g, 'HDX_COLON');
}
function decodeSpecialTokens(query: string): string {
return query
.replace(/\\"/g, '"')
.replace(/HDX_BACKSLASH_LITERAL/g, '\\')
.replace('http_COLON_//', 'http://')
.replace('https_COLON_//', 'https://')
.replace(/localhost_COLON_(\d{1,5})/, 'localhost:$1')
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/DBQuerySidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function DBQuerySidePanel() {
const scopeWhereQuery = React.useCallback(
(where: string) => {
const spanNameQuery = dbQuery
? `${DB_STATEMENT_PROPERTY}:"${dbQuery}" `
? `${DB_STATEMENT_PROPERTY}:"${dbQuery.replace(/"/g, '\\"')}" `
: '';
const whereQuery = where ? `(${where})` : '';
const serviceQuery = service ? `service:"${service}" ` : '';
Expand Down
3 changes: 3 additions & 0 deletions packages/app/src/queryv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import lucene from '@hyperdx/lucene';

function encodeSpecialTokens(query: string): string {
return query
.replace(/\\\\/g, 'HDX_BACKSLASH_LITERAL')
.replace('http://', 'http_COLON_//')
.replace('https://', 'https_COLON_//')
.replace(/localhost:(\d{1,5})/, 'localhost_COLON_$1')
.replace(/\\:/g, 'HDX_COLON');
}
function decodeSpecialTokens(query: string): string {
return query
.replace(/\\"/g, '"')
.replace(/HDX_BACKSLASH_LITERAL/g, '\\')
.replace('http_COLON_//', 'http://')
.replace('https_COLON_//', 'https://')
.replace(/localhost_COLON_(\d{1,5})/, 'localhost:$1')
Expand Down

0 comments on commit a0dc1b5

Please sign in to comment.