-
setShowBuilder(false)} size="small">
-
-
+
+
+ {editMode ? (
+
+
+ Back to new query
+
+ ) : null}
+
+ {editMode ? 'Edit Saved Query' : 'New Query'}
+
+
+ {editMode ? (
+
+ = 0 && queryNameIdx !== savedQuery)
+ }
+ helperText={
+ queryNameIdx >= 0 && queryNameIdx !== savedQuery
+ ? 'Query name must be unique'
+ : ''
+ }
+ onChange={(event) =>
+ setQuery({ ...query, name: event.target.value })
+ }
+ margin="normal"
+ size="small"
+ required
+ fullWidth
+ />
+
+
+ setQuery({ ...query, description: event.target.value })
+ }
+ margin="normal"
+ size="small"
+ fullWidth
+ />
-
- {inEditMode ? (
-
-
- Back to new query
-
- ) : null}
-
- {inEditMode ? 'Edit Saved Query' : 'New Query'}
-
-
-
- {inEditMode ? (
-
- = 0 && queryNameIdx !== editSavedIdx)
- }
- helperText={
- queryNameIdx >= 0 && queryNameIdx !== editSavedIdx
- ? 'Query name must be unique'
- : ''
- }
- onChange={(event) => setQueryName(event.target.value)}
- margin="normal"
- size="small"
- required
- fullWidth
- />
+ ) : null}
- setQueryDescription(event.target.value)}
- margin="normal"
- size="small"
- fullWidth
- />
-
- ) : null}
-
- {
- return { label: table.name, value: table.name };
- })}
- onChange={(value) => {
- const table = tables.find((t) => t.name === value);
- setSelectedTable(table);
- setSelectedColumns(
- table.fields.map((field) => ({
- name: field.name,
- show: true
- }))
- );
- setRowCalls([]);
- setOperator(undefined);
- setGroupByCall({
- primary: '',
- secondary: ''
- });
- }}
- />
-
+
{
+ return { label: table.name, value: table.name };
+ })}
+ onChange={(value) => {
+ const table = tables.find((t) => t.name === value);
+ setSelectedTable(table);
+ setShowInvalid(false);
+ setQuery({
+ table: table.name,
+ columns: table.fields.map((field) => field.name),
+ operation: query.operation,
+ isInvalid: false
+ });
+ }}
+ />
-
-
setOperation(value)}
- />
-
- {operation === 'Extract' ? (
-
- setShowColumnSelector(true)}
- >
- Configure result fields
-
-
-
-
-
- ) : null}
-
- setShowColumnSelector(false)}
- />
-
-
- {selectedTable && operation !== 'GroupBy'
- ? rowCalls.map((rowCall, idx) => (
-
- {idx > 0 ? (
-
-
-
-
- setOperatorEl(event.currentTarget)
- }
- >
- {operator}
-
-
-
- setOperatorEl(null)}
- >
- {groupOperators.map((op: Operator) => (
- {
- setOperator(op);
- setOperatorEl(null);
- }}
- >
- {op}
-
- ))}
-
-
- ) : null}
-
- onUpdateRow(idx, updatedRow, operator, isNot)
- }
- onRemoveGroup={() => onRemoveGroup(idx)}
- />
-
- ))
- : null}
-
- {selectedTable && operation === 'GroupBy' ? (
-
-
-
- [
- 'set',
- 'time',
- 'mutex',
- 'bool',
- 'int',
- 'timestamp'
- ].includes(f.options.type)
- )
- .map((field) => {
- return {
- label: `${field.name} (${field.options.type})`,
- value: field.name
- };
- })}
- onChange={(value) =>
- setGroupByCall({
- ...groupByCall,
- primary: value.toString()
- })
- }
- error={hasInvalid && !groupByCall.primary}
- />
-
- [
- 'set',
- 'time',
- 'mutex',
- 'bool',
- 'int',
- 'timestamp'
- ].includes(f.options.type)
- )
- .map((field) => {
- return {
- label: `${field.name} (${field.options.type})`,
- value: field.name
- };
- })}
- onChange={(value) =>
- setGroupByCall({
- ...groupByCall,
- secondary: value.toString()
- })
- }
- />
-
-
-
- Filter (optional)
- {groupByFilters.length > 0 && (
-
-
-
- )}
-
- {filter ? (
- setFilter(undefined)}
- >
- Clear Filter
-
- ) : null}
-
- {filter ? (
- {filter}
- ) : groupByFilters.length > 0 ? (
- {
- const { rowCalls, operator } = groupByFilters[queryIdx];
- const res = stringifyRowData(rowCalls, operator);
- if (!res.error) {
- setFilter(res.query);
- }
- }}
- />
- ) : (
-
- No available filters. To use filters, save an Extract query
- for {selectedTable.name} with at least one field constraint.
-
- )}
-
-
-
- Sort (optional)
-
- field.options.type === 'int')
- .map((field) => {
- return { label: field.name, value: field.name };
- })}
- showErrors={hasInvalid}
- />
-
-
- ) : (
-
-
-
{
- if (rowCalls.length === 0 || !!operator) {
- onNewGroup();
- } else {
- setNewOperatorEl(event.currentTarget);
- }
- }}
- >
-
-
-
-
- )}
- setNewOperatorEl(null)}
- >
- {groupOperators.map((op: Operator) => (
- {
- setOperator(op);
- onNewGroup();
- setNewOperatorEl(null);
- }}
- >
- {op}
-
- ))}
-
-
-
-
-
- Run
-
- {inEditMode ? (
-
= 0 && queryNameIdx !== editSavedIdx)
- }
- >
- {updating ? : 'Update'}
-
- ) : (
-
- {
- setSaveButtonEl(event.currentTarget);
- setQueryName('');
- setQueryDescription('');
- }}
- >
- Save
-
- setSaveButtonEl(null)}
- anchorOrigin={{
- vertical: 'top',
- horizontal: 'left'
- }}
- transformOrigin={{
- vertical: 'bottom',
- horizontal: 'left'
- }}
- >
-
-
- Give your query a unique name and a short description to
- help you identify it for future use.
-
-
-
= 0}
- helperText={
- queryNameIdx >= 0 ? 'Query name must be unique' : ''
- }
- onChange={(event) => setQueryName(event.target.value)}
- margin="dense"
- required
- fullWidth
- />
-
-
- setQueryDescription(event.target.value)
- }
- margin="dense"
- fullWidth
- />
-
-
-
- {
- onSave();
- setSaveButtonEl(null);
- }}
- disabled={!queryName || queryNameIdx >= 0}
- >
- Ok
-
- setSaveButtonEl(null)}
- >
- Cancel
-
-
-
-
-
-
- )}
-
-
{
- setRowCalls([]);
- setHasInvalid(false);
- setEditSavedIdx(-1);
- setSelectedColumns(
- selectedTable.fields.map((field) => ({
- name: field.name,
- show: true
- }))
- );
- setGroupByCall({
- primary: '',
- secondary: ''
- });
- setSort([]);
- setFilter(undefined);
- onClear();
- }}
- >
- Clear
-
-
-
- ) : (
-
-
setShowBuilder(true)} size="small">
-
-
-
- )}
-
-
-
-
- Results{' '}
-
- {results?.query.includes('Extract(') ? (
-
-
-
-
- Download
-
- ) : null}
-
- {loading ? Loading...
: null}
- {results && !error && !loading ? (
-
- {fullResultsCount && fullRecordsCount ? (
-
- {results.duration ? (
-
- {fullRecordsCount.toLocaleString()} records scanned in{' '}
- {formatDuration(results.duration, true)}.
-
- ) : null}
-
- Showing{' '}
- {fullResultsCount > 1000 ? 'first 1,000 rows of' : 'all'}{' '}
- {fullResultsCount.toLocaleString()} results.
-
-
- ) : null}
-
-
- ) : null}
- {error && !loading ? {error.error}
: null}
- {!loading && !results && !error ? (
-
-
- Build a query to see results
-
- {queriesList.length > 0 ? (
-
-
- Saved Queries
-
-
-
- ) : null}
-
- ) : null}
-
+
{
+ setOperation(value);
+ setShowInvalid(false);
+ setQuery({
+ table: selectedTable.name,
+ operation: value,
+ isInvalid: false
+ });
+ }}
+ />
+
-
+
{renderBuilder()}
+
+
+
+ Run
+
+ {editMode ? (
+
= 0 && queryNameIdx !== savedQuery)
+ }
+ >
+ {updating ? : 'Update'}
+
+ ) : (
+
+ setSaveButtonEl(event.currentTarget)}
+ >
+ Save
+
+ setSaveButtonEl(null)}
+ anchorOrigin={{
+ vertical: 'top',
+ horizontal: 'left'
+ }}
+ transformOrigin={{
+ vertical: 'bottom',
+ horizontal: 'left'
+ }}
+ >
+
+
+ Give your query a unique name and a short description to
+ help you identify it for future use.
+
+
+
= 0}
+ helperText={
+ queryNameIdx >= 0 ? 'Query name must be unique' : ''
+ }
+ onChange={(event) =>
+ setQuery({ ...query, name: event.target.value })
+ }
+ margin="dense"
+ required
+ fullWidth
+ />
+
+
+ setQuery({ ...query, description: event.target.value })
+ }
+ margin="dense"
+ fullWidth
+ />
+
+
+
+ {
+ onSave();
+ setSaveButtonEl(null);
+ }}
+ disabled={!query?.name || queryNameIdx >= 0}
+ >
+ Ok
+
+ setSaveButtonEl(null)}
+ >
+ Cancel
+
+
+
+
+
+
+ )}
+
+
Clear
+
+
);
};
diff --git a/lattice/src/App/QueryBuilder/QueryBuilderContainer.module.scss b/lattice/src/App/QueryBuilder/QueryBuilderContainer.module.scss
index cd406d4e8..a5cd6c77d 100644
--- a/lattice/src/App/QueryBuilder/QueryBuilderContainer.module.scss
+++ b/lattice/src/App/QueryBuilder/QueryBuilderContainer.module.scss
@@ -1,3 +1,65 @@
+.builderColumn,
+.results {
+ height: calc(100vh - 64px);
+ overflow: scroll;
+ position: relative;
+
+ .openClose {
+ position: absolute;
+ right: 10px;
+ top: 10px;
+ }
+}
+
+.split {
+ display: flex;
+}
+
+.collapsedBuilder {
+ border-right: 1px solid rgba(var(--contrast-rgb), 0.1);
+ text-align: center;
+ padding-top: 10px;
+}
+
+.resultsBlock {
+ height: 100%;
+}
+
+.resultsHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .download {
+ display: flex;
+ align-items: center;
+ }
+
+ .downloadInfo {
+ margin-right: 8px;
+ }
+}
+
+.infoMessage {
+ padding: 16px;
+ border: 1px solid rgba(var(--primary-rgb), 0.5);
+ background: rgba(var(--primary-rgb), 0.1);
+ border-radius: 4px;
+ margin: 4px 0 16px;
+}
+
+.spacer{
+ padding-bottom: 16px;
+}
+
+.resultsLoading {
+ margin-left: 8px;
+}
+
+.savedQueries {
+ margin-top: 48px;
+}
+
.noTables {
margin-top: 20px;
padding: 12px 16px;
diff --git a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx
index 40e42fb8c..ef9f9062a 100644
--- a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx
+++ b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx
@@ -1,20 +1,28 @@
import React, { Fragment, useState } from 'react';
-import moment, { Moment } from 'moment';
import Alert from '@material-ui/lab/Alert';
+import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
+import Button from '@material-ui/core/Button';
+import classNames from 'classnames';
+import CloseIcon from '@material-ui/icons/Close';
+import IconButton from '@material-ui/core/IconButton';
+import InfoIcon from '@material-ui/icons/Info';
+import moment, { Moment } from 'moment';
import Paper from '@material-ui/core/Paper';
+import Split from 'react-split';
import Snackbar from '@material-ui/core/Snackbar';
+import Tooltip from '@material-ui/core/Tooltip';
import Typography from '@material-ui/core/Typography';
import { Block } from 'shared/Block';
-import { Operator, RowGrouping, RowsCallType } from './rowTypes';
+import { formatDuration } from 'shared/utils/formatDuration';
+import { grpc } from '@improbable-eng/grpc-web';
import { pilosa } from 'services/eventServices';
import { QueryBuilder } from './QueryBuilder';
-import { useEffectOnce } from 'react-use';
-import { ResultType } from 'App/Query/QueryContainer';
import { queryPQL } from 'services/grpcServices';
-import { grpc } from '@improbable-eng/grpc-web';
+import { QueryResults } from 'App/Query/QueryResults';
+import { ResultType } from 'App/Query/QueryContainer';
import { RowResponse } from 'proto/pilosa_pb';
-import { SortOption } from './GroupBySort';
-import { stringifyRowData } from './stringifyRowData';
+import { SavedQueries } from 'App/QueryBuilder/SavedQueries';
+import { useEffectOnce } from 'react-use';
import css from './QueryBuilderContainer.module.scss';
let streamingResults: ResultType = {
@@ -30,13 +38,22 @@ let streamingResults: ResultType = {
export const QueryBuilderContainer = () => {
let startTime: Moment;
let exportRows: any[] = [];
- const [tables, setTables] = useState
();
+ const colSizes = JSON.parse(
+ localStorage.getItem('builderColSizes') || '[25, 75]'
+ );
+ const [queriesList, setQueriesList] = useState(
+ JSON.parse(localStorage.getItem('saved-queries') || '[]')
+ );
+
+ const [tables, setTables] = useState([]);
+ const [showBuilder, setShowBuilder] = useState(true);
const [results, setResults] = useState();
const [fullCount, setFullCount] = useState();
const [recordsCount, setRecordsCount] = useState();
const [errorResult, setErrorResult] = useState();
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
+ const [savedQuery, setSavedQuery] = useState(-1);
useEffectOnce(() => {
pilosa.get.schema().then((res) => {
@@ -67,62 +84,6 @@ export const QueryBuilderContainer = () => {
setLoading(false);
};
- const onRunQuery = (
- table: any,
- operation: string,
- rowData: RowGrouping[],
- columnsList: string[],
- operator?: Operator
- ) => {
- streamingResults = {
- query: '',
- operation,
- type: 'PQL',
- headers: [],
- rows: [],
- index: table.name,
- roundtrip: 0,
- error: ''
- };
- startTime = moment();
- setLoading(true);
- let query: string = '';
- if (rowData.length === 0) {
- query = 'All()';
- } else {
- const res = stringifyRowData(rowData, operator);
- if (res.error) {
- streamingResults.error = res.query;
- } else {
- query = res.query;
- }
- }
-
- if (operation !== 'Count') {
- const countQuery = `Count(${query})`;
- pilosa.post
- .query(table.name, countQuery)
- .then((res) => setFullCount(res.data.results[0]));
-
- pilosa.post
- .query(table.name, `Count(All())`)
- .then((res) => setRecordsCount(res.data.results[0]));
- } else {
- setFullCount(undefined);
- }
-
- if (operation === 'Extract') {
- const fields = columnsList.map((field) => `Rows(${field})`);
- const allRows = fields.join(', ');
- query = `${operation}(Limit(${query}, limit=1000), ${allRows})`;
- } else {
- query = `${operation}(${query})`;
- }
-
- streamingResults.query = query;
- queryPQL(table.name, query, handleQueryMessages, handleQueryEnd);
- };
-
const handleExternalLookup = (message: RowResponse) => {
const response = message.toObject();
let rowStr: string[] = [];
@@ -158,46 +119,71 @@ export const QueryBuilderContainer = () => {
}
};
- const onRunGroupBy = (
- table: any,
- rowsData: RowsCallType,
- sort: SortOption[],
- filter?: string
+ const onRunQuery = (
+ table: string,
+ operation: string,
+ query: string,
+ countQuery?: string
) => {
streamingResults = {
- query: '',
- operation: 'GroupBy',
+ query,
+ operation,
type: 'PQL',
headers: [],
rows: [],
- index: table.name,
+ index: table,
roundtrip: 0,
error: ''
};
startTime = moment();
setLoading(true);
- let sortString = '';
- let aggregateString = '';
- if (sort.length > 0) {
- sortString = sort[0].sortValue;
- if (sort[0].sortValue.includes('sum')) {
- aggregateString = `, aggregate=Sum(field=${sort[0].field})`;
+
+ if (operation !== 'Count') {
+ if (countQuery) {
+ pilosa.post.query(table, countQuery).then((res) => {
+ setFullCount(res.data.results[0]);
+ });
}
- if (sort.length > 1) {
- sortString = `${sortString}, ${sort[1].sortValue}`;
- if (sort[1].sortValue.includes('sum')) {
- aggregateString = `, aggregate=Sum(field=${sort[1].field})`;
- }
- }
- sortString = `, sort="${sortString}"`;
+
+ pilosa.post
+ .query(table, `Count(All())`)
+ .then((res) => setRecordsCount(res.data.results[0]));
+ } else {
+ setFullCount(undefined);
+ }
+
+ queryPQL(table, query, handleQueryMessages, handleQueryEnd);
+ };
+
+ const onClear = () => {
+ setResults(undefined);
+ setFullCount(undefined);
+ setErrorResult(undefined);
+ };
+
+ const onRemoveQuery = (queryIdx: number) => {
+ let queries = [...queriesList];
+ queries.splice(queryIdx, 1);
+ localStorage.setItem('saved-queries', JSON.stringify(queries));
+ setQueriesList(queries);
+ };
+
+ const onSaveQuery = () => {
+ const updatedList = JSON.parse(
+ localStorage.getItem('saved-queries') || '[]'
+ );
+ if (savedQuery < 0) {
+ setSavedQuery(updatedList.length - 1);
+ }
+ setQueriesList(updatedList);
+ };
+
+ const onExportLogs = () => {
+ if (results) {
+ const columns = results.rows.map((row) => row[0].uint64val);
+ const table = results.index ? results.index : '';
+ onExternalLookup(table, columns);
}
- const filterString =
- filter && filter.length > 0 ? `, filter=${filter}` : '';
- const query = rowsData.secondary
- ? `GroupBy(Rows(${rowsData.primary}), Rows(${rowsData.secondary})${filterString}${sortString}${aggregateString})`
- : `GroupBy(Rows(${rowsData.primary})${filterString}${sortString}${aggregateString})`;
- streamingResults.query = query;
- queryPQL(table.name, query, handleQueryMessages, handleQueryEnd);
};
const onExternalLookup = (table: string, columns: number[]) => {
@@ -206,40 +192,132 @@ export const QueryBuilderContainer = () => {
queryPQL(table, query, handleExternalLookup, handleExternalLookupEnd);
};
- return tables && tables.length > 0 ? (
+ return (
- {
- setResults(undefined);
- setFullCount(undefined);
- setErrorResult(undefined);
- }}
- />
-
- setError('')}>
- {error}
-
-
+ {tables.length > 0 ? (
+
+ localStorage.setItem('builderColSizes', JSON.stringify(sizes))
+ }
+ gutter={(_index, direction) => {
+ const gutter = document.createElement('div');
+ gutter.className = `gutter gutter-${direction}`;
+ const dragbars = document.createElement('div');
+ dragbars.className = 'dragBar';
+ gutter.appendChild(dragbars);
+ return gutter;
+ }}
+ className={classNames(css.split, !showBuilder ? 'hide-gutter' : '')}
+ >
+ {showBuilder ? (
+
+
+ setShowBuilder(false)} size="small">
+
+
+
+
setSavedQuery(-1)}
+ onSaveQuery={onSaveQuery}
+ />
+
+ ) : (
+
+
setShowBuilder(true)} size="small">
+
+
+
+ )}
+
+
+
+
+ Results{' '}
+
+ {results?.query.includes('Extract(') ? (
+
+
+
+
+ Download
+
+ ) : null}
+
+ {loading ? Loading...
: null}
+ {results && !loading ? (
+
+ {fullCount && recordsCount ? (
+
+ {results.duration ? (
+
+ {recordsCount.toLocaleString()} records scanned in{' '}
+ {formatDuration(results.duration, true)}.
+
+ ) : null}
+
+ Showing{' '}
+ {fullCount > 1000 ? 'first 1,000 rows of' : 'all'}{' '}
+ {fullCount.toLocaleString()} results.
+
+
+ ) : null}
+
+
+
+ ) : null}
+ {errorResult && !loading ? {errorResult.error}
: null}
+ {!loading && !results && !error ? (
+
+
+ Build a query to see results
+
+ {queriesList.length > 0 ? (
+
+
+ Saved Queries
+
+ setSavedQuery(queryIdx)}
+ onRemoveQuery={onRemoveQuery}
+ />
+
+ ) : null}
+
+ ) : null}
+
+
+
+ setError('')}>
+ {error}
+
+
+
+ ) : (
+
+
+ Query Builder
+
+
+
+ There are no tables to query.
+
+
+
+ )}
- ) : (
-
-
- Query Builder
-
-
-
- There are no tables to query.
-
-
-
);
};
diff --git a/lattice/src/App/QueryBuilder/RowCall/helpers.ts b/lattice/src/App/QueryBuilder/RowCall/helpers.ts
deleted file mode 100644
index ebbd97470..000000000
--- a/lattice/src/App/QueryBuilder/RowCall/helpers.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-export const operators = {
- decimal: [
- { label: '>', value: '>' },
- { label: '<', value: '<' },
- { label: '>=', value: '>=' },
- { label: '<=', value: '<=' },
- { label: '==', value: '=' },
- { label: '!=', value: '!=' },
- ],
- int: [
- { label: '>', value: '>' },
- { label: '<', value: '<' },
- { label: '>=', value: '>=' },
- { label: '<=', value: '<=' },
- { label: '==', value: '=' },
- { label: '!=', value: '!=' },
- ],
- 'mutex-id': [
- { label: 'is', value: '=' },
- { label: 'is not', value: '!=' }
- ],
- 'mutex-keys': [
- { label: 'is', value: '=' },
- { label: 'is not', value: '!=' },
- { label: 'like', value: 'like' },
- { label: 'CIDR', value: 'cidr' }
- ],
- "set-id": [
- { label: 'is', value: '=' },
- { label: 'is not', value: '!=' }
- ],
- "set-keys": [
- { label: 'is', value: '=' },
- { label: 'is not', value: '!=' },
- { label: 'like', value: 'like' },
- { label: 'CIDR', value: 'cidr' }
- ],
- "time-id": [
- { label: 'is', value: '=' },
- { label: 'is not', value: '!=' }
- ],
- "time-keys": [
- { label: 'is', value: '=' },
- { label: 'is not', value: '!=' },
- { label: 'like', value: 'like' }
- ],
- timestamp: [
- { label: 'is before', value: '<' },
- { label: 'is after', value: '>' },
- { label: 'is', value: '=' },
- ]
-}
diff --git a/lattice/src/App/QueryBuilder/RowCall/RowCall.module.scss b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/RowCall.module.scss
similarity index 100%
rename from lattice/src/App/QueryBuilder/RowCall/RowCall.module.scss
rename to lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/RowCall.module.scss
diff --git a/lattice/src/App/QueryBuilder/RowCall/RowCall.tsx b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/RowCall.tsx
similarity index 99%
rename from lattice/src/App/QueryBuilder/RowCall/RowCall.tsx
rename to lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/RowCall.tsx
index cd0501060..a5f35f971 100644
--- a/lattice/src/App/QueryBuilder/RowCall/RowCall.tsx
+++ b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/RowCall.tsx
@@ -10,7 +10,7 @@ import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import { getIPRange } from 'get-ip-range';
import { operators } from './helpers';
-import { groupOperators, Operator, RowCallType } from '../rowTypes';
+import { groupOperators, Operator, RowCallType } from 'App/QueryBuilder/rowTypes';
import { Select } from 'shared/Select';
import MomentUtils from '@date-io/moment';
import { MuiPickersUtilsProvider, DateTimePicker } from '@material-ui/pickers';
diff --git a/lattice/src/App/QBuilder/RowCallBuilder/RowCall/helpers.ts b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/helpers.ts
similarity index 100%
rename from lattice/src/App/QBuilder/RowCallBuilder/RowCall/helpers.ts
rename to lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/helpers.ts
diff --git a/lattice/src/App/QueryBuilder/RowCall/index.ts b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/index.ts
similarity index 100%
rename from lattice/src/App/QueryBuilder/RowCall/index.ts
rename to lattice/src/App/QueryBuilder/RowCallBuilder/RowCall/index.ts
diff --git a/lattice/src/App/QBuilder/RowCallBuilder/RowCallBuilder.module.scss b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCallBuilder.module.scss
similarity index 100%
rename from lattice/src/App/QBuilder/RowCallBuilder/RowCallBuilder.module.scss
rename to lattice/src/App/QueryBuilder/RowCallBuilder/RowCallBuilder.module.scss
diff --git a/lattice/src/App/QBuilder/RowCallBuilder/RowCallBuilder.tsx b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCallBuilder.tsx
similarity index 88%
rename from lattice/src/App/QBuilder/RowCallBuilder/RowCallBuilder.tsx
rename to lattice/src/App/QueryBuilder/RowCallBuilder/RowCallBuilder.tsx
index 48f8e81ea..ba8a1ad8f 100644
--- a/lattice/src/App/QBuilder/RowCallBuilder/RowCallBuilder.tsx
+++ b/lattice/src/App/QueryBuilder/RowCallBuilder/RowCallBuilder.tsx
@@ -10,18 +10,15 @@ import {
RowCallType,
RowGrouping
} from 'App/QueryBuilder/rowTypes';
-import { RowCall } from 'App/QueryBuilder/RowCall';
+import { RowCall } from './RowCall';
import css from './RowCallBuilder.module.scss';
+import { useEffect } from 'react';
type RowCallBuilderProps = {
rowCalls: RowGrouping[];
fields: any[];
showInvalid: boolean;
- onChange: (
- rows: RowGrouping[],
- operator?: Operator,
- hasInvalid?: boolean
- ) => void;
+ onChange: (rows: RowGrouping[], operator?: Operator) => void;
};
export const RowCallBuilder: FC = ({
@@ -34,7 +31,7 @@ export const RowCallBuilder: FC = ({
const [operatorEl, setOperatorEl] = useState(null);
const [operator, setOperator] = useState();
- const onNewGroup = () => {
+ const onNewGroup = (op?: Operator) => {
const newRow: RowCallType[] = [
{
field: '',
@@ -45,19 +42,20 @@ export const RowCallBuilder: FC = ({
}
];
- onChange([...rowCalls, { row: newRow }]);
+ onChange([...rowCalls, { row: newRow }], op ? op : operator);
};
const onRemoveGroup = (groupIdx: number) => {
if (groupIdx === 0) {
- onChange(rowCalls.slice(1));
+ onChange(rowCalls.slice(1), operator);
} else {
const updatedRowCalls = [...rowCalls];
updatedRowCalls.splice(groupIdx, 1);
- onChange(updatedRowCalls);
+ onChange(updatedRowCalls, operator);
if (updatedRowCalls.length < 1) {
setOperator(undefined);
+ onChange(updatedRowCalls);
}
}
};
@@ -65,7 +63,7 @@ export const RowCallBuilder: FC = ({
const onUpdateRow = (
groupIdx: number,
newRowData?: RowCallType[],
- operator?: Operator,
+ rowGroupOperator?: Operator,
isNot?: boolean
) => {
const updatedRowCalls = [...rowCalls];
@@ -77,14 +75,14 @@ export const RowCallBuilder: FC = ({
};
if (newRowData.length <= 1) {
delete clone.operator;
- } else if (operator) {
- clone.operator = operator;
+ } else if (rowGroupOperator) {
+ clone.operator = rowGroupOperator;
}
updatedRowCalls.splice(groupIdx, 1, clone);
} else {
updatedRowCalls.splice(groupIdx, 1);
}
- onChange(updatedRowCalls);
+ onChange(updatedRowCalls, operator);
};
return (
@@ -167,7 +165,7 @@ export const RowCallBuilder: FC = ({
key={`operator-${op}`}
onClick={() => {
setOperator(op);
- onNewGroup();
+ onNewGroup(op);
setNewOperatorEl(null);
}}
>
diff --git a/lattice/src/App/QBuilder/RowCallBuilder/index.ts b/lattice/src/App/QueryBuilder/RowCallBuilder/index.ts
similarity index 100%
rename from lattice/src/App/QBuilder/RowCallBuilder/index.ts
rename to lattice/src/App/QueryBuilder/RowCallBuilder/index.ts
diff --git a/lattice/src/App/QueryBuilder/stringifyRowData.ts b/lattice/src/App/QueryBuilder/stringifyRowData.ts
deleted file mode 100644
index 0faa642c5..000000000
--- a/lattice/src/App/QueryBuilder/stringifyRowData.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { Operator, RowGrouping } from './rowTypes';
-import { getIPRange } from 'get-ip-range';
-
-export const stringifyRowData = (rowData: RowGrouping[], operator?: Operator) => {
- let query = '';
- let rowsMap: string[][] = [];
- rowData.forEach((group, groupIdx) => {
- rowsMap.push([]);
- group.row.forEach((row) => {
- let rowString = '';
- const { field, rowOperator, value, type, keys } = row;
- const isNegatory = rowOperator === '!=';
- const isUnion =
- ['=', '!='].includes(rowOperator) && value.split(',').length > 1;
- const operator = isNegatory ? '=' : rowOperator;
- if (isUnion) {
- const values = value.split(',');
- const unionRows = values
- .map((v) => keys ? `Row(${field}="${v.trim()}")` : `Row(${field}=${v.trim()})`)
- .join(', ');
- rowString = `Union(${unionRows})`;
- } else if (rowOperator === 'cidr') {
- try {
- const ipRange = getIPRange(value);
- const ipRows = ipRange
- .map((ip) => `Row(${field}="${ip}")`)
- .join(', ');
- rowString = `Union(${ipRows})`;
- } catch (error) {
- return { error: true, query: error.message };
- }
- } else if (rowOperator === 'like') {
- if (value.includes('%') || value.includes('_')) {
- rowString = `UnionRows(Rows(field=${field}, like="${value}"))`;
- } else {
- rowString = `UnionRows(Rows(field=${field}, like="%${value}%"))`;
- }
- } else {
- rowString = keys || type === 'timestamp'
- ? `Row(${field}${operator}"${value}")`
- : `Row(${field}${operator}${value})`;
- }
-
- if (isNegatory) {
- rowsMap[groupIdx].push(`Not(${rowString})`);
- } else {
- rowsMap[groupIdx].push(rowString);
- }
- });
- });
-
- query = rowsMap
- .map((group, idx) => {
- let joined = '';
- if (group.length > 1) {
- joined = group.map((r) => r).join(', ');
- const operator = rowData[idx].operator;
- if (operator === 'and') {
- joined = `Intersect(${joined})`;
- } else if (operator === 'or') {
- joined = `Union(${joined})`;
- }
- } else {
- joined = group[0];
- }
- return rowData[idx].isNot ? `Not(${joined})` : joined;
- })
- .join(', ');
-
- if (rowData.length > 1 && operator) {
- if (operator === 'and') {
- query = `Intersect(${query})`;
- } else if (operator === 'or') {
- query = `Union(${query})`;
- }
- }
-
- return { error: false, query };
-}
diff --git a/lattice/src/App/QueryBuilder/stringifyRowData.test.ts b/lattice/src/App/QueryBuilder/utils.test.ts
similarity index 61%
rename from lattice/src/App/QueryBuilder/stringifyRowData.test.ts
rename to lattice/src/App/QueryBuilder/utils.test.ts
index 22f439e82..d34e15ef9 100644
--- a/lattice/src/App/QueryBuilder/stringifyRowData.test.ts
+++ b/lattice/src/App/QueryBuilder/utils.test.ts
@@ -1,4 +1,9 @@
-import { stringifyRowData } from './stringifyRowData';
+import {
+ stringifyRowData,
+ stringifyExtract,
+ stringifyCount,
+ stringifyGroupBy
+} from './utils';
describe('decimal types', () => {
it('stringifies = operator', () => {
@@ -10,7 +15,7 @@ describe('decimal types', () => {
"type": "decimal",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName=0)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName=0)' })
});
it('stringifies != operator', () => {
@@ -22,7 +27,7 @@ describe('decimal types', () => {
"type": "decimal",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName=0))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName=0))' })
});
it('stringifies > operator', () => {
@@ -34,7 +39,7 @@ describe('decimal types', () => {
"type": "decimal",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName>0)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName>0)' })
});
it('stringifies >= operator', () => {
@@ -46,7 +51,7 @@ describe('decimal types', () => {
"type": "decimal",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName>=0)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName>=0)' })
});
it('stringifies negated operations', () => {
@@ -59,7 +64,7 @@ describe('decimal types', () => {
"keys": undefined
}],
isNot: true
- }])).toEqual({ error: false, query: 'Not(Row(fieldName>=0))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName>=0))' })
});
});
@@ -73,7 +78,7 @@ describe('int types', () => {
"type": "int",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName=0)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName=0)' })
});
it('stringifies != operator', () => {
@@ -85,7 +90,7 @@ describe('int types', () => {
"type": "int",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName=0))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName=0))' })
});
it('stringifies > operator', () => {
@@ -97,7 +102,7 @@ describe('int types', () => {
"type": "int",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName>0)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName>0)' })
});
it('stringifies >= operator', () => {
@@ -109,7 +114,7 @@ describe('int types', () => {
"type": "int",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName>=0)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName>=0)' })
});
it('stringifies negated operations', () => {
@@ -122,7 +127,7 @@ describe('int types', () => {
"keys": undefined
}],
isNot: true
- }])).toEqual({ error: false, query: 'Not(Row(fieldName>=0))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName>=0))' })
});
});
@@ -136,7 +141,7 @@ describe('mutex (id) types', () => {
"type": "mutex",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName=1234)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName=1234)' })
});
it('stringifies is operator for multiple values', () => {
@@ -148,7 +153,7 @@ describe('mutex (id) types', () => {
"type": "mutex",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Union(Row(fieldName=1234), Row(fieldName=5678))' })
+ }])).toEqual({ error: false, queryString: 'Union(Row(fieldName=1234), Row(fieldName=5678))' })
});
it('stringifies is not operator', () => {
@@ -160,7 +165,7 @@ describe('mutex (id) types', () => {
"type": "mutex",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName=1234))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName=1234))' })
});
it('stringifies is not operator for multiple values', () => {
@@ -172,7 +177,7 @@ describe('mutex (id) types', () => {
"type": "mutex",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Not(Union(Row(fieldName=1234), Row(fieldName=5678)))' })
+ }])).toEqual({ error: false, queryString: 'Not(Union(Row(fieldName=1234), Row(fieldName=5678)))' })
});
});
@@ -186,7 +191,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Row(fieldName="value")' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName="value")' })
});
it('stringifies is operator for multiple values', () => {
@@ -198,7 +203,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Union(Row(fieldName="one"), Row(fieldName="two"))' })
+ }])).toEqual({ error: false, queryString: 'Union(Row(fieldName="one"), Row(fieldName="two"))' })
});
it('stringifies is not operator', () => {
@@ -210,7 +215,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName="value"))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName="value"))' })
});
it('stringifies is not operator for multiple values', () => {
@@ -222,7 +227,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Not(Union(Row(fieldName="one"), Row(fieldName="two")))' })
+ }])).toEqual({ error: false, queryString: 'Not(Union(Row(fieldName="one"), Row(fieldName="two")))' })
});
it('stringifies like operator with a single-codepoint key', () => {
@@ -234,7 +239,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="_alue"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="_alue"))' })
});
it('stringifies like operator with a wildcard', () => {
@@ -246,7 +251,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="value%"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="value%"))' })
});
it('stringifies like operator without a single-codepoint key or wildcard', () => {
@@ -258,7 +263,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="%value%"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="%value%"))' })
});
it('stringifies CIDR operator', () => {
@@ -270,7 +275,7 @@ describe('mutex (keys) types', () => {
"type": "mutex",
"keys": true
}]
- }])).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"))' })
+ }])).toEqual({ error: false, queryString: '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 operations', () => {
@@ -283,7 +288,7 @@ describe('mutex (keys) types', () => {
"keys": true
}],
isNot: true
- }])).toEqual({ error: false, query: 'Not(UnionRows(Rows(field=fieldName, like="%value%")))' })
+ }])).toEqual({ error: false, queryString: 'Not(UnionRows(Rows(field=fieldName, like="%value%")))' })
});
});
@@ -297,7 +302,7 @@ describe('set (id) types', () => {
"type": "set",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName=1234)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName=1234)' })
});
it('stringifies is operator for multiple values', () => {
@@ -309,7 +314,7 @@ describe('set (id) types', () => {
"type": "set",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Union(Row(fieldName=1234), Row(fieldName=5678))' })
+ }])).toEqual({ error: false, queryString: 'Union(Row(fieldName=1234), Row(fieldName=5678))' })
});
it('stringifies is not operator', () => {
@@ -321,7 +326,7 @@ describe('set (id) types', () => {
"type": "set",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName=1234))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName=1234))' })
});
it('stringifies is not operator for multiple values', () => {
@@ -333,7 +338,7 @@ describe('set (id) types', () => {
"type": "set",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Not(Union(Row(fieldName=1234), Row(fieldName=5678)))' })
+ }])).toEqual({ error: false, queryString: 'Not(Union(Row(fieldName=1234), Row(fieldName=5678)))' })
});
});
@@ -347,7 +352,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Row(fieldName="value")' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName="value")' })
});
it('stringifies is operator for multiple values', () => {
@@ -359,7 +364,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Union(Row(fieldName="one"), Row(fieldName="two"))' })
+ }])).toEqual({ error: false, queryString: 'Union(Row(fieldName="one"), Row(fieldName="two"))' })
});
it('stringifies is not operator', () => {
@@ -371,7 +376,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName="value"))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName="value"))' })
});
it('stringifies is not operator for multiple values', () => {
@@ -383,7 +388,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Not(Union(Row(fieldName="one"), Row(fieldName="two")))' })
+ }])).toEqual({ error: false, queryString: 'Not(Union(Row(fieldName="one"), Row(fieldName="two")))' })
});
it('stringifies like operator with a single-codepoint key', () => {
@@ -395,7 +400,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="_alue"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="_alue"))' })
});
it('stringifies like operator with a wildcard', () => {
@@ -407,7 +412,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="value%"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="value%"))' })
});
it('stringifies like operator without a single-codepoint key or wildcard', () => {
@@ -419,7 +424,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="%value%"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="%value%"))' })
});
it('stringifies CIDR operator', () => {
@@ -431,7 +436,7 @@ describe('set (keys) types', () => {
"type": "set",
"keys": true
}]
- }])).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"))' })
+ }])).toEqual({ error: false, queryString: '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 operations', () => {
@@ -444,7 +449,7 @@ describe('set (keys) types', () => {
"keys": true
}],
isNot: true
- }])).toEqual({ error: false, query: 'Not(UnionRows(Rows(field=fieldName, like="%value%")))' })
+ }])).toEqual({ error: false, queryString: 'Not(UnionRows(Rows(field=fieldName, like="%value%")))' })
});
});
@@ -458,7 +463,7 @@ describe('time (ID) types', () => {
"type": "time",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Row(fieldName=1234)' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName=1234)' })
});
it('stringifies is not operators', () => {
@@ -470,7 +475,7 @@ describe('time (ID) types', () => {
"type": "time",
"keys": undefined
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName=1234))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName=1234))' })
});
it('stringifies negated operators', () => {
@@ -483,7 +488,7 @@ describe('time (ID) types', () => {
"keys": undefined
}],
isNot: true
- }])).toEqual({ error: false, query: 'Not(Row(fieldName=1234))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName=1234))' })
});
});
@@ -497,7 +502,7 @@ describe('time (keys) types', () => {
"type": "time",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Row(fieldName="1234")' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName="1234")' })
});
it('stringifies is not operators', () => {
@@ -509,7 +514,7 @@ describe('time (keys) types', () => {
"type": "time",
"keys": true
}]
- }])).toEqual({ error: false, query: 'Not(Row(fieldName="1234"))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName="1234"))' })
});
it('stringifies like operators with a single-codepoint key', () => {
@@ -521,7 +526,7 @@ describe('time (keys) types', () => {
"type": "time",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="123_"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="123_"))' })
});
it('stringifies like operators with a wildcard', () => {
@@ -533,7 +538,7 @@ describe('time (keys) types', () => {
"type": "time",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="%1234"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="%1234"))' })
});
it('stringifies like operators without a single-codepoint key or wildcard', () => {
@@ -545,7 +550,7 @@ describe('time (keys) types', () => {
"type": "time",
"keys": true
}]
- }])).toEqual({ error: false, query: 'UnionRows(Rows(field=fieldName, like="%1234%"))' })
+ }])).toEqual({ error: false, queryString: 'UnionRows(Rows(field=fieldName, like="%1234%"))' })
});
it('stringifies negated operators', () => {
@@ -558,7 +563,7 @@ describe('time (keys) types', () => {
"keys": true
}],
isNot: true
- }])).toEqual({ error: false, query: 'Not(Row(fieldName="1234"))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName="1234"))' })
});
});
@@ -572,7 +577,7 @@ describe('timestamp types', () => {
"type": "timestamp",
"keys": undefined
}],
- }])).toEqual({ error: false, query: 'Row(fieldName="2021-06-04T12:00:00Z")' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName="2021-06-04T12:00:00Z")' })
});
it('stringifies is before operators', () => {
@@ -584,7 +589,7 @@ describe('timestamp types', () => {
"type": "timestamp",
"keys": undefined
}],
- }])).toEqual({ error: false, query: 'Row(fieldName<"2021-06-04T12:00:00Z")' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName<"2021-06-04T12:00:00Z")' })
});
it('stringifies is after operators', () => {
@@ -596,7 +601,7 @@ describe('timestamp types', () => {
"type": "timestamp",
"keys": undefined
}],
- }])).toEqual({ error: false, query: 'Row(fieldName>"2021-06-04T12:00:00Z")' })
+ }])).toEqual({ error: false, queryString: 'Row(fieldName>"2021-06-04T12:00:00Z")' })
});
it('stringifies negated operators', () => {
@@ -609,7 +614,7 @@ describe('timestamp types', () => {
"keys": undefined
}],
isNot: true
- }])).toEqual({ error: false, query: 'Not(Row(fieldName="2021-06-04T12:00:00Z"))' })
+ }])).toEqual({ error: false, queryString: 'Not(Row(fieldName="2021-06-04T12:00:00Z"))' })
});
});
@@ -630,7 +635,7 @@ describe('mixed types', () => {
"keys": undefined
}],
operator: "and"
- }])).toEqual({ error: false, query: 'Intersect(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0))' })
+ }])).toEqual({ error: false, queryString: 'Intersect(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0))' })
});
it('stringifies multiple or-ed rows', () => {
@@ -649,7 +654,7 @@ describe('mixed types', () => {
"keys": undefined
}],
operator: "or"
- }])).toEqual({ error: false, query: 'Union(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0))' })
+ }])).toEqual({ error: false, queryString: 'Union(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0))' })
});
it('stringifies negated multiple rows', () => {
@@ -669,6 +674,129 @@ describe('mixed types', () => {
}],
operator: "or",
isNot: true
- }])).toEqual({ error: false, query: 'Not(Union(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0)))' })
+ }])).toEqual({ error: false, queryString: 'Not(Union(UnionRows(Rows(field=stringType, like="%value%")), Row(intType=0)))' })
});
})
+
+describe('Query types', () => {
+ it('stringifies Extract queries', () => {
+ expect(stringifyExtract({
+ columns: ['one', 'two', 'three'],
+ rowCalls: [{
+ row: [{
+ "field": "stringType",
+ "rowOperator": "like",
+ "value": "value",
+ "type": "set",
+ "keys": true
+ }, {
+ "field": "stringType2",
+ "rowOperator": "is",
+ "value": "value2",
+ "type": "set",
+ "keys": true
+ }],
+ operator: 'or'
+ }, {
+ row: [{
+ "field": "intType",
+ "rowOperator": "=",
+ "value": "0",
+ "type": "int",
+ "keys": undefined
+ }]
+ }],
+ operator: "and"
+ })).toEqual({ error: false, queryString: 'Extract(Limit(Intersect(Union(UnionRows(Rows(field=stringType, like=\"%value%\")), Row(stringType2is\"value2\")), Row(intType=0)), limit=1000), Rows(one), Rows(two), Rows(three))', countQuery: 'Count(Intersect(Union(UnionRows(Rows(field=stringType, like=\"%value%\")), Row(stringType2is\"value2\")), Row(intType=0)))' })
+ });
+
+ it('stringifies Count queries', () => {
+ expect(stringifyCount({
+ columns: ['one', 'two', 'three'],
+ rowCalls: [{
+ row: [{
+ "field": "stringType",
+ "rowOperator": "like",
+ "value": "value",
+ "type": "set",
+ "keys": true
+ }, {
+ "field": "stringType2",
+ "rowOperator": "is",
+ "value": "value2",
+ "type": "set",
+ "keys": true
+ }],
+ operator: 'or'
+ }, {
+ row: [{
+ "field": "intType",
+ "rowOperator": "=",
+ "value": "0",
+ "type": "int",
+ "keys": undefined
+ }]
+ }],
+ operator: "and"
+ })).toEqual({ error: false, queryString: 'Count(Intersect(Union(UnionRows(Rows(field=stringType, like=\"%value%\")), Row(stringType2is\"value2\")), Row(intType=0)))' })
+ });
+
+ it('stringifies basic GroupBy queries', () => {
+ expect(stringifyGroupBy({
+ groupByCall: {
+ primary: 'primary'
+ }
+ })).toEqual('GroupBy(Rows(primary))');
+ });
+
+ it('stringifies GroupBy queries with secondary grouping', () => {
+ expect(stringifyGroupBy({
+ groupByCall: {
+ primary: 'primary',
+ secondary: 'secondary'
+ }
+ })).toEqual('GroupBy(Rows(primary), Rows(secondary))');
+ });
+
+ it('stringifies GroupBy queries with a filter', () => {
+ expect(stringifyGroupBy({
+ groupByCall: {
+ primary: 'primary'
+ },
+ filter: 'filterString'
+ })).toEqual('GroupBy(Rows(primary), filter=filterString)');
+ });
+
+ it('stringifies GroupBy queries with a sort', () => {
+ expect(stringifyGroupBy({
+ groupByCall: {
+ primary: 'primary'
+ },
+ sort: [{ sortValue: 'count desc' }]
+ })).toEqual('GroupBy(Rows(primary), sort=\"count desc\")');
+ });
+
+ it('stringifies GroupBy queries with a secondary sum sort', () => {
+ expect(stringifyGroupBy({
+ groupByCall: {
+ primary: 'primary'
+ },
+ sort: [
+ { sortValue: 'count desc' },
+ { sortValue: 'sum asc', field: 'field' }
+ ]
+ })).toEqual('GroupBy(Rows(primary), sort=\"count desc, sum asc\", aggregate=Sum(field=field))');
+ });
+
+ it('stringifies GroupBy queries with a filter and sort', () => {
+ expect(stringifyGroupBy({
+ groupByCall: {
+ primary: 'primary'
+ },
+ filter: 'filterString',
+ sort: [
+ { sortValue: 'sum asc', field: 'field' }
+ ]
+ })).toEqual('GroupBy(Rows(primary), filter=filterString, sort=\"sum asc\", aggregate=Sum(field=field))');
+ });
+});
diff --git a/lattice/src/App/QBuilder/utils.ts b/lattice/src/App/QueryBuilder/utils.ts
similarity index 81%
rename from lattice/src/App/QBuilder/utils.ts
rename to lattice/src/App/QueryBuilder/utils.ts
index 5722fb31f..441b0ab8d 100644
--- a/lattice/src/App/QBuilder/utils.ts
+++ b/lattice/src/App/QueryBuilder/utils.ts
@@ -1,4 +1,4 @@
-import { RowGrouping } from './rowTypes';
+import { Operator, RowGrouping } from './rowTypes';
import { getIPRange } from 'get-ip-range';
export const stringifyExtract = (query: any) => {
@@ -29,8 +29,9 @@ export const stringifyCount = (query: any) => {
}
}
-const stringifyRowData = (rowCalls, operator) => {
+export const stringifyRowData = (rowCalls: RowGrouping[], operator?: Operator) => {
let queryString = '';
+ let error = false;
if (rowCalls.length === 0) {
queryString = 'All()';
} else {
@@ -58,7 +59,7 @@ const stringifyRowData = (rowCalls, operator) => {
.join(', ');
rowString = `Union(${ipRows})`;
} catch (error) {
- return { error: true, query: error.message };
+ return { error: true, queryString: error.message };
}
} else if (rowOperator === 'like') {
if (value.includes('%') || value.includes('_')) {
@@ -80,34 +81,36 @@ const stringifyRowData = (rowCalls, operator) => {
});
});
- queryString = rowsMap
- .map((group, idx) => {
- let joined = '';
- if (group.length > 1) {
- joined = group.map((r) => r).join(', ');
- const operator = rowCalls[idx].operator;
- if (operator === 'and') {
- joined = `Intersect(${joined})`;
- } else if (operator === 'or') {
- joined = `Union(${joined})`;
+ if (!error) {
+ queryString = rowsMap
+ .map((group, idx) => {
+ let joined = '';
+ if (group.length > 1) {
+ joined = group.map((r) => r).join(', ');
+ const operator = rowCalls[idx].operator;
+ if (operator === 'and') {
+ joined = `Intersect(${joined})`;
+ } else if (operator === 'or') {
+ joined = `Union(${joined})`;
+ }
+ } else {
+ joined = group[0];
}
- } else {
- joined = group[0];
- }
- return rowCalls[idx].isNot ? `Not(${joined})` : joined;
- })
- .join(', ');
+ return rowCalls[idx].isNot ? `Not(${joined})` : joined;
+ })
+ .join(', ');
- if (rowCalls.length > 1 && operator) {
- if (operator === 'and') {
- queryString = `Intersect(${queryString})`;
- } else if (operator === 'or') {
- queryString = `Union(${queryString})`;
+ if (rowCalls.length > 1 && operator) {
+ if (operator === 'and') {
+ queryString = `Intersect(${queryString})`;
+ } else if (operator === 'or') {
+ queryString = `Union(${queryString})`;
+ }
}
}
}
- return { error: false, queryString };
+ return { error, queryString };
}
export const stringifyGroupBy = (query: any) => {