diff --git a/lattice/src/App/QueryBuilder/QueryBuilder.tsx b/lattice/src/App/QueryBuilder/QueryBuilder.tsx index fcad11f97..ef83a787b 100644 --- a/lattice/src/App/QueryBuilder/QueryBuilder.tsx +++ b/lattice/src/App/QueryBuilder/QueryBuilder.tsx @@ -30,7 +30,7 @@ import { ResultType } from 'App/Query/QueryContainer'; import { RowCall } from './RowCall'; import { SavedQueries } from './SavedQueries'; import { Select } from 'shared/Select'; -import { stringifyRowData } from './utils'; +import { stringifyRowData } from './stringifyRowData'; import css from './QueryBuilder.module.scss'; type QueryBuilderProps = { diff --git a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx index 266aa9858..0456943ec 100644 --- a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx +++ b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx @@ -13,7 +13,7 @@ import { ResultType } from 'App/Query/QueryContainer'; import { queryPQL } from 'services/grpcServices'; import { grpc } from '@improbable-eng/grpc-web'; import { RowResponse } from 'proto/pilosa_pb'; -import { stringifyRowData } from './utils'; +import { stringifyRowData } from './stringifyRowData'; import css from './QueryBuilderContainer.module.scss'; let streamingResults: ResultType = { diff --git a/lattice/src/App/QueryBuilder/RowCall/helpers.ts b/lattice/src/App/QueryBuilder/RowCall/helpers.ts index 4068057ef..dd55cad7c 100644 --- a/lattice/src/App/QueryBuilder/RowCall/helpers.ts +++ b/lattice/src/App/QueryBuilder/RowCall/helpers.ts @@ -10,6 +10,7 @@ export const operators = { set: [ { label: 'is', value: '=' }, { label: 'is not', value: '!=' }, + { label: 'like', value: 'like' }, { label: 'CIDR', value: 'cidr' } ], timestamp: [ diff --git a/lattice/src/App/QueryBuilder/stringifyRowData.test.ts b/lattice/src/App/QueryBuilder/stringifyRowData.test.ts new file mode 100644 index 000000000..4ace4d7a9 --- /dev/null +++ b/lattice/src/App/QueryBuilder/stringifyRowData.test.ts @@ -0,0 +1,204 @@ +import { stringifyRowData } from './stringifyRowData'; + +describe('int types', () => { + it('stringifies = operator', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "=", + "value": "0", + "type": "int" + }] + }])).toEqual({ error: false, query: 'Row(fieldName=0)' }) + }); + + it('stringifies != operator', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "!=", + "value": "0", + "type": "int" + }] + }])).toEqual({ error: false, query: 'Not(Row(fieldName=0))' }) + }); + + it('stringifies > operator', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": ">", + "value": "0", + "type": "int" + }] + }])).toEqual({ error: false, query: 'Row(fieldName>0)' }) + }); + + it('stringifies >= operator', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": ">=", + "value": "0", + "type": "int" + }] + }])).toEqual({ error: false, query: 'Row(fieldName>=0)' }) + }); + + it('stringifies negated operatorations', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": ">=", + "value": "0", + "type": "int" + }], + isNot: true + }])).toEqual({ error: false, query: 'Not(Row(fieldName>=0))' }) + }); +}) + +describe('set types', () => { + it('stringifies is operator', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "=", + "value": "value", + "type": "set" + }] + }])).toEqual({ error: false, query: 'Row(fieldName="value")' }) + }); + + it('stringifies is operator for multiple values', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "=", + "value": "one, two", + "type": "set" + }] + }])).toEqual({ error: false, query: 'Union(Row(fieldName="one"), Row(fieldName="two"))' }) + }); + + it('stringifies is not operator', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "!=", + "value": "value", + "type": "set" + }] + }])).toEqual({ error: false, query: 'Not(Row(fieldName="value"))' }) + }); + + it('stringifies is not operator for multiple values', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "!=", + "value": "one, two", + "type": "set" + }] + }])).toEqual({ error: false, query: 'Not(Union(Row(fieldName="one"), Row(fieldName="two")))' }) + }); + + it('stringifies like operator with a wildcard', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "like", + "value": "value%", + "type": "set" + }] + }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="value%"))' }) + }); + + it('stringifies like operator without a wildcard', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "like", + "value": "value", + "type": "set" + }] + }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="%value%"))' }) + }); + + it('stringifies CIDR operator', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "cidr", + "value": "10.164.124.33/30", + "type": "set" + }] + }])).toEqual({ error: false, query: 'Union(Row(fieldName="10.164.124.32"), Row(fieldName="10.164.124.33"), Row(fieldName="10.164.124.34"), Row(fieldName="10.164.124.35"))' }) + }); + + it('stringifies negated operatorations', () => { + expect(stringifyRowData([{ + row: [{ + "field": "fieldName", + "rowOperator": "like", + "value": "value", + "type": "set" + }], + isNot: true + }])).toEqual({ error: false, query: 'Not(UnionRows(Rows(field=fieldName, like="%value%")))' }) + }); +}); + +describe('mixed types', () => { + it('stringifies multiple and-ed rows', () => { + expect(stringifyRowData([{ + row: [{ + "field": "stringType", + "rowOperator": "like", + "value": "value", + "type": "set" + }, { + "field": "intType", + "rowOperator": "=", + "value": "0", + "type": "int" + }], + operator: "and" + }])).toEqual({ error: false, query: 'Intersect(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0))' }) + }); + + it('stringifies multiple or-ed rows', () => { + expect(stringifyRowData([{ + row: [{ + "field": "stringType", + "rowOperator": "like", + "value": "value", + "type": "set" + }, { + "field": "intType", + "rowOperator": "=", + "value": "0", + "type": "int" + }], + operator: "or" + }])).toEqual({ error: false, query: 'Union(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0))' }) + }); + + it('stringifies negated multiple rows', () => { + expect(stringifyRowData([{ + row: [{ + "field": "stringType", + "rowOperator": "like", + "value": "value", + "type": "set" + }, { + "field": "intType", + "rowOperator": "=", + "value": "0", + "type": "int" + }], + operator: "or", + isNot: true + }])).toEqual({ error: false, query: 'Not(Union(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0)))' }) + }); +}) diff --git a/lattice/src/App/QueryBuilder/utils.ts b/lattice/src/App/QueryBuilder/stringifyRowData.ts similarity index 89% rename from lattice/src/App/QueryBuilder/utils.ts rename to lattice/src/App/QueryBuilder/stringifyRowData.ts index a37bca045..c03ea9da4 100644 --- a/lattice/src/App/QueryBuilder/utils.ts +++ b/lattice/src/App/QueryBuilder/stringifyRowData.ts @@ -29,6 +29,12 @@ export const stringifyRowData = (rowData: RowGrouping[], operator?: Operator) => } catch (error) { return { error: true, query: error.message }; } + } else if (rowOperator === 'like') { + if (value.includes('%')) { + rowString = `UnionRows(Rows(field=${field}, like="${value}"))`; + } else { + rowString = `UnionRows(Rows(field=${field}, like="%${value}%"))`; + } } else { rowString = ['set', 'timestamp'].includes(type) ? `Row(${field}${operator}"${value}")` diff --git a/lattice/src/shared/utils/formatByes.test.ts b/lattice/src/shared/utils/formatBytes.test.ts similarity index 100% rename from lattice/src/shared/utils/formatByes.test.ts rename to lattice/src/shared/utils/formatBytes.test.ts