Support escaped quotes (#214)

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.
This commit is contained in:
Mike Shi 2024-01-10 17:56:43 -08:00 committed by GitHub
parent 0824822a59
commit a0dc1b5f25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 1 deletions

View file

@ -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)

View file

@ -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');

View file

@ -8,6 +8,7 @@ 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')
@ -15,6 +16,8 @@ function encodeSpecialTokens(query: string): string {
}
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')

View file

@ -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}" ` : '';

View file

@ -2,6 +2,7 @@ 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')
@ -9,6 +10,8 @@ function encodeSpecialTokens(query: string): string {
}
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')