Skip to content

Commit

Permalink
SearchBox: Add a != operator to the query wizard (#850)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dlurak authored Dec 22, 2024
1 parent 91ac420 commit 1c60674
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ describe('userInput -> query works', () => {
expect(generateQuery(getAST(' diet:vegan=no '))).toBe(
'nwr["diet:vegan"="no"]',
);
expect(generateQuery(getAST('tourism!=hotel'))).toBe(
'nwr["tourism"!="hotel"]',
);
expect(generateQuery(getAST('cuisine!=*'))).toBe('nwr["cuisine"!~".*"]');
});

it('should work for more complex queries', () => {
Expand All @@ -32,6 +36,10 @@ describe('userInput -> query works', () => {
getAST('amenity=restaurant or amenity = cafe or historic=*'),
),
).toBe('nwr["amenity"="restaurant"];nwr["amenity"="cafe"];nwr["historic"]');

expect(generateQuery(getAST('tourism=* and tourism!=hotel'))).toBe(
'nwr["tourism"]["tourism"!="hotel"]',
);
});

it('should work with groups', () => {
Expand Down Expand Up @@ -70,6 +78,7 @@ describe('userInput -> query works', () => {
test('userInput (ast) -> label works', () => {
expect(queryWizardLabel(getAST('amenity=*'))).toBe('amenity=*');
expect(queryWizardLabel(getAST('amenity=bench'))).toBe('amenity=bench');
expect(queryWizardLabel(getAST('amenity!=bench'))).toBe('amenity!=bench');
expect(queryWizardLabel(getAST('amenity=bench or speed_limit=30'))).toBe(
'amenity=bench or 1 other',
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/SearchBox/queryWizard/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type ASTNodeComparison = {
type: 'comparison';
key: string;
value: SpecialValue | string;
operator: '=';
operator: '=' | '!=';
};

export type ASTNodeGroup = { type: 'group'; expression: ASTNode };
Expand Down
6 changes: 5 additions & 1 deletion src/components/SearchBox/queryWizard/generateQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ASTNode, ASTNodeComparison, ASTNodeExpression } from './ast';
import { isSpecialComparisonValue } from './isAst';

const processNode = (node: ASTNode): string[][] => {
switch (node.type) {
Expand All @@ -12,11 +13,14 @@ const processNode = (node: ASTNode): string[][] => {
};

const generateComparison = ({ key, value, operator }: ASTNodeComparison) => {
// Using switch to simplify adding more operators in the future
switch (operator) {
case '=':
// Only { type=anything } isn't a string
return typeof value === 'string' ? `["${key}"="${value}"]` : `["${key}"]`;
case '!=':
return typeof value === 'string'
? `["${key}"!="${value}"]`
: `["${key}"!~".*"]`;
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/SearchBox/queryWizard/isAst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const isComparisonAstNode = (obj: any): obj is ASTNodeComparison => {
type === 'comparison' &&
typeof key === 'string' &&
(typeof value === 'string' || isSpecialComparisonValue(value)) &&
operator === '='
(operator === '=' || operator === '!=')
);
} catch {
return false;
Expand Down
5 changes: 3 additions & 2 deletions src/components/SearchBox/queryWizard/queryWizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ export const getAST = (inputValue: string) => {
export const queryWizardLabel = (ast: ASTNode): string => {
switch (ast.type) {
case 'comparison':
const { operator } = ast;
if (!isSpecialComparisonValue(ast.value)) {
return `${ast.key}=${ast.value}`;
return `${ast.key}${operator}${ast.value}`;
}

if (ast.value.type === 'anything') {
return `${ast.key}=*`;
return `${ast.key}${operator}*`;
}
break;
case 'expression':
Expand Down
6 changes: 4 additions & 2 deletions src/components/SearchBox/queryWizard/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ export type Operator = 'and' | 'or';
export type Token =
| { type: 'string'; value: string }
| { type: 'logical'; value: Operator }
| { type: 'operator'; value: '=' }
| { type: 'operator'; value: '=' | '!=' }
| { type: 'bracket'; value: 'opening' | 'closing' }
| { type: 'anything' };

const tokenRegex = /\s+and\s+|\s+or\s+|\*|=|\(|\)|[\w:]+|"[^"]*"/g;
const tokenRegex = /\s+and\s+|\s+or\s+|\*|!=|=|\(|\)|[\w:]+|"[^"]*"/g;

const validateMatches = (matches: RegExpExecArray[], inputValue: string) => {
const [lastMatchedIndex, onlyWhitespaceGaps] = matches.reduce<
Expand Down Expand Up @@ -47,6 +47,8 @@ export function tokenize(inputValue: string) {
return { type: 'logical', value };
case '=':
return { type: 'operator', value };
case '!=':
return { type: 'operator', value };
case '*':
return { type: 'anything' };
case '(':
Expand Down

0 comments on commit 1c60674

Please sign in to comment.