From 1f41bfc07335bb131178d69f52ba5a8c1bef2333 Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Wed, 3 May 2017 13:12:50 +0300 Subject: [PATCH 01/10] update global z-index config --- client/modules/core/components/material-ui-container.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/modules/core/components/material-ui-container.jsx b/client/modules/core/components/material-ui-container.jsx index 155e4fb..ac77017 100644 --- a/client/modules/core/components/material-ui-container.jsx +++ b/client/modules/core/components/material-ui-container.jsx @@ -6,7 +6,7 @@ import { grey800 } from 'material-ui/styles/colors'; const muiTheme = getMuiTheme({ zIndex: { - layer: 1400, + layer: 1500, }, palette: { textColor: grey800, From b8284a7a16c6dead40a80a65c1c49245029766b2 Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Fri, 12 May 2017 17:09:31 +0300 Subject: [PATCH 02/10] fix bugs in sql parser --- lib/sql_parser.js | 88 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 8 deletions(-) diff --git a/lib/sql_parser.js b/lib/sql_parser.js index 986b7fd..5a0317f 100644 --- a/lib/sql_parser.js +++ b/lib/sql_parser.js @@ -16,6 +16,7 @@ export default { }; objectSQL.groupBy = getGroupBy(query, objectSQL.select); objectSQL.orderBy = getOrderBy(query, objectSQL.select); + return objectSQL; }, parseToQueryObject(rawQuery, fields) { @@ -42,7 +43,8 @@ export default { }; function getSelect(query) { - let select = query.match(/^\s*select\s+([\w([\])"'`.,/\s]+)from\s/i); + // query.match(/^\s*select\s+([\w([\])"'`.,/\s]+)from\s/i); + let select = query.match(/^\s*select\s+([\w([\])"'`*:&;.,/\s]+)from\s/i); if (!select) return { error: 'Wrong syntax: "SELECT FROM " expected' }; select = select[1]; const asNames = select.match(/\s+as\s+\w+/ig); @@ -74,21 +76,25 @@ const parseQueryPartial = (partial, paramName) => partial.slice(0, paramName.index).match(/(\w+)\/(\w+)/i); function getFrom(query) { - const from = query.match(/\sfrom\s+([\w([\])"'`.,/=<>\s]+$)/i)[1].replace(cutOff, ''); + const matchRes = query.match(/\sfrom\s+([\w([\])"'`.,/=<>\s]+$)/im); + if (!matchRes) return null; + const from = matchRes[1].replace(cutOff, ''); const name = getParamName(from); const path = parseQueryPartial(from, name); return { db: path[1], collection: path[2], name: name[1] }; } function getJoin(query) { - const from = query.match(/\sjoin\s+([\w([\])"'`.,/=<>\s]+$)/i)[1].replace(cutOff, ''); + const matchRes = query.match(/\sjoin\s+([\w([\])"'`.,/=<>\s]+$)/im); + if (!matchRes) return null; + const from = matchRes[1].replace(cutOff, ''); const name = getParamName(from); const path = parseQueryPartial(from, name); return path[2]; } function getGroupBy(query, select) { - let groupBy = query.match(/\sgroup by\s+([\w([\])"'`.,/=<>\s]+$)/i); + let groupBy = query.match(/^\s*group by\s+([\w([\])"'`*:&;.,/\s]+)/im); if (groupBy) { groupBy = groupBy[1].replace(cutOff, '') .replace(/\s/g, ''); @@ -119,19 +125,57 @@ function getOrderBy(query, select) { } function getHaving(query) { - let having = query.match(/\shaving\s+([\w([\])"'`.,/=<>\s]+$)/i); + let having = query.match(/^\s*having\s+([\w([\])"'`*:&;.,<>%@=\/\s]+)limit\s/im); if (having) { having = having[1].replace(cutOff, ''); - return parseFiltering(having); + having = removeBraces(having); + + return parseHaveingConditions(having); } return null; + + + function removeBraces(queryPart) { + let result = ''; + + if (isCountExist(queryPart)) { + const countWordINdex = getWordIndex(queryPart, 'count('); + const afterCountIndex = getWordIndex(queryPart, ')', countWordINdex); + result += removebracesWithRegex(queryPart.substr(0, countWordINdex)); + result += queryPart.substring(countWordINdex, afterCountIndex + 1); + result += removeBraces(queryPart.substring(afterCountIndex + 1)); + } else { + result += removebracesWithRegex(queryPart); + } + return result; + } + + function isCountExist(queryToCheck) { + return queryToCheck.match(/COUNT[(]/gi); + } + + function removebracesWithRegex(line) { + return line.replace(/[()]/g, ''); + } + + function getWordIndex(line, word, fromPosition = 0) { + let result = line.indexOf(word, fromPosition); + if (result === -1) { + result = line.indexOf(word.toUpperCase(), fromPosition); + } + return result; + } } function getWhere(query) { - let where = query.match(/\swhere\s+([\w([\])"'`.,/=<>\s]+$)/i); + let where = query.match(/\swhere\s+([\w([\])"'`.,/=<>\s]+$)/im); if (where) { where = where[1].replace(cutOff, ''); - return parseFiltering(where); + const parsed = parseFiltering(where); + parsed.forEach( (item) => { + item.exp2 = item.exp2.replace(/^["]/i, '').replace(/["]$/i, ''); + }); + return parsed; } return null; } @@ -148,6 +192,34 @@ function getOffset(query) { return null; } +function parseHaveingConditions(str) { + const filters = []; + const andOr = str.match(/\s+(and|or)\s+/gi); + + if (andOr) { + let rest = str.slice(); + andOr.forEach(item => { + const part = rest.slice(0, rest.indexOf(item)); + filters.push(part); + rest = rest.slice(rest.indexOf(item) + item.length); + }); + filters.push(rest); + } else { + filters.push(str); + } + return filters.map((filter, i) => { + const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; + // const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`.,/\\s]+)`, 'i'); + const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`*:&;%@.,/\\s]+)`, 'i'); + obj.exp2 = filter.match(regex)[1]; + obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, ''); + obj.operator = obj.operator.replace(/\s/g, ''); + obj.exp2 = obj.exp2.replace(/^\s+|\s+$/g, ''); + if (andOr && i < andOr.length) obj.join = andOr[i].replace(/\s/g, ''); + return obj; + }); +} + function parseFiltering(str) { const filters = []; const andOr = str.match(/\s+(and|or)\s+/gi); From cc1b67bb2b5ca0ab59dcbdbcd9ed196fa610eaf7 Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Tue, 16 May 2017 14:33:07 +0300 Subject: [PATCH 03/10] fix error with dimentions and measures fix problems with sql parser fix some other problems --- .../presentation_mode/chart_viewer.jsx | 1 + .../modules/publishing/components/chart.jsx | 1 + client/modules/workplace/actions/pivot.js | 3 + client/modules/workplace/actions/preview.js | 90 ++++++++++++++++--- .../workplace/actions/workplace_state.js | 41 +++++++++ .../components/preview/chart_canvas.jsx | 8 ++ .../components/preview/data_visualisation.jsx | 4 + .../workplace/components/preview/preview.jsx | 5 ++ .../components/sql_editor/sql_button.jsx | 3 + .../constructors/chart-constructor.js | 8 ++ .../workplace/containers/preview/preview.js | 6 ++ .../modules/workplace/containers/workplace.js | 3 + lib/data_processing.js | 17 ++++ 13 files changed, 176 insertions(+), 14 deletions(-) diff --git a/client/modules/home/components/dashboards/presentation_mode/chart_viewer.jsx b/client/modules/home/components/dashboards/presentation_mode/chart_viewer.jsx index 55786eb..234f5b3 100644 --- a/client/modules/home/components/dashboards/presentation_mode/chart_viewer.jsx +++ b/client/modules/home/components/dashboards/presentation_mode/chart_viewer.jsx @@ -17,6 +17,7 @@ const ChartViewer = ({ dashboard, charts, meteorMethodCall, routeTo, setChartVie prevArrow: , nextArrow: , }; + return (
{ } const { viewObject: { data, chartType, dataTimeStamp, pivot }, queryObject } = chart; const needRedraw = !dataTimeStamp || Date.now() - dataTimeStamp < 1000; + if (chartType) { return ( { - if (err) cb({ error: true }); + if (err) { + // console.log('!!!!!!!!!!!method call error'); + // console.log(err); + cb({ error: true }); + } else if (LocalState.get('QUASAR_REQUEST_ID') === answerId || isSingleRequest) { + // console.log('!!!!!!!!!!!method call success'); + // console.log('data ', data); + // console.log('fields ', fields); + + + // console.log('!!!!!! Client Processong data after submitting sql editor '); const processedData = dataProcessing.process(data, fields); + // console.log('222222222222222222222222 before callback'); cb({ data: processedData }); } } ); }, - determineDefaultFields({ Notificator }, fields, chartType, pivot) { + determineDefaultFields({ Notificator, LocalState }, fields, chartType, pivot) { + + // todo + // there are problen in this function + + // console.log('determineDefaultFields function call 222222222'); + + // console.log('notificator ', Notificator); + // console.log('fields ', fields); + // console.log('chartType ', chartType); + // console.log('pivot ', pivot); + + console.log('///////////////////////////////////////////////'); + // console.log('fields ', fields); + // console.log('recieved constructors ', pivot.model.constructors); + + + const viewObj = LocalState.get('VIEW_OBJECT'); + + // console.log('ViewObject ', viewObj); + let result; if (pivot && pivot.model) { - const measuresId = pivot.model.values; - let dimensionsCounter = 0; - fields.forEach(f => { - if (!~measuresId.indexOf(f.id)) { - dimensionsCounter++; - if (dimensionsCounter < 3) f.constructorType = 'dimensions'; - } else { - f.constructorType = 'measures'; - } - }); - result = fields; - } else { + if (pivot.model.constructors) { + result = determineFieldsBySavedConstructors(fields, pivot.model.constructors); + } else { + result = determineFieldsByModel(fields); + } + } else + { const getNeededfields = () => { const fieldsArray = []; _.each(chartTypeRules[chartType], (val, key) => { @@ -102,7 +132,39 @@ export default { }); if (neededFields.filter(f => f.id).length) result = fields; } + + console.log('result ', result); + return result; + + function determineFieldsBySavedConstructors(fieldsArr, constructors) { + console.log('dimentions ', constructors.dimensions); + console.log('measures ', constructors.measures); + + fieldsArr.forEach(field => { + console.log(field.name, ' filed id=', field.id, 'dimentions includes ', constructors.dimensions.includes(field.id)); + if (constructors.dimensions.includes(field.id)) { + field.constructorType = 'dimensions'; + } else if (constructors.measures.includes(field.id)) { + field.constructorType = 'measures'; + } + }); + return fieldsArr; + } + + function determineFieldsByModel(fieldsArr) { + const measuresId = pivot.model.values; + let dimensionsCounter = 0; + fieldsArr.forEach(f => { + if (!~measuresId.indexOf(f.id)) { + dimensionsCounter++; + if (dimensionsCounter < 3) f.constructorType = 'dimensions'; + } else { + f.constructorType = 'measures'; + } + }); + return fieldsArr; + } }, getNewChartModelFields(context, { chartType, fields }, { fieldId, isChecked, label }) { diff --git a/client/modules/workplace/actions/workplace_state.js b/client/modules/workplace/actions/workplace_state.js index eae7a6a..01ed96e 100644 --- a/client/modules/workplace/actions/workplace_state.js +++ b/client/modules/workplace/actions/workplace_state.js @@ -4,11 +4,21 @@ import SQLParser from '/lib/sql_parser'; export default { setSQLQuery({ LocalState }, query) { + console.log('|||||||||||||||||||||||||||||||||||||||||'); + console.log('Set sql query object fubction after submit sql form'); + const fields = LocalState.get('COLLECTION_FIELDS'); const oldQueryObj = LocalState.get('SQL_QUERY_OBJECT'); + setFieldsConstructorsType(oldQueryObj.fields, LocalState); + const queryObj = SQLParser.parseToQueryObject(query, fields); queryObj.from = oldQueryObj.from; queryObj.on = oldQueryObj.on; + + + console.log('Old sql query ', oldQueryObj); + console.log('Parserd sql query', queryObj); + LocalState.set('SQL_QUERY_OBJECT', queryObj); }, @@ -109,3 +119,34 @@ function resetData(LocalState) { LocalState.set('VIEW_OBJECT', viewObj); } } + +function setFieldsConstructorsType(fieldsList, LocalState) { + let isConstructorsFinded = false; + const constructorTypes = { + measures: [], + dimensions: [], + }; + + fieldsList.forEach((item, index) => { + const constructorType = item.constructorType; + if (constructorType) { + constructorTypes[constructorType].push(index); + isConstructorsFinded = true; + } + }); + + // console.log('......................................'); + // console.log('Construcor types ', constructorTypes); + + + if (isConstructorsFinded) { + const viewObj = LocalState.get('VIEW_OBJECT'); + viewObj.pivot.model.constructors = constructorTypes; + + console.log('/////////////////////////////////////////'); + console.log('fields ', fieldsList); + console.log('saved constructors ', viewObj.pivot.model.constructors); + + LocalState.set('VIEW_OBJECT', viewObj); + } +} diff --git a/client/modules/workplace/components/preview/chart_canvas.jsx b/client/modules/workplace/components/preview/chart_canvas.jsx index e61db68..4cb9936 100644 --- a/client/modules/workplace/components/preview/chart_canvas.jsx +++ b/client/modules/workplace/components/preview/chart_canvas.jsx @@ -24,6 +24,14 @@ class ChartCanvas extends React.Component { render() { const { data, fields, chartType, heightIsFixed, tableType, needRedraw, maxHeight } = this.props; + + + // console.log('Render chart canvas!!'); + // console.log('Data processing call after submit sql editor'); + // console.log('fields ', fields); + + // there are wrong data in fields object + const chartData = dataProcessing.chart(data, fields, chartType, tableType); const Chart = getChart(chartType); let height; diff --git a/client/modules/workplace/components/preview/data_visualisation.jsx b/client/modules/workplace/components/preview/data_visualisation.jsx index ae4203e..32edf9e 100644 --- a/client/modules/workplace/components/preview/data_visualisation.jsx +++ b/client/modules/workplace/components/preview/data_visualisation.jsx @@ -22,6 +22,10 @@ class DataVisualisation extends React.Component { updateSQLQueryObj } = this.props; const { chartType } = viewObject; const tableHeight = `${window.innerHeight - 74}px`; + + // console.log('preview/data_visualization file!!!!!!'); + // console.log('fields ', queryObject.fields); + return (
{tableType === 'simple' && diff --git a/client/modules/workplace/components/preview/preview.jsx b/client/modules/workplace/components/preview/preview.jsx index b706080..d6a45d2 100644 --- a/client/modules/workplace/components/preview/preview.jsx +++ b/client/modules/workplace/components/preview/preview.jsx @@ -43,7 +43,12 @@ class Preview extends React.Component { render() { const { savedQuery, queryObject, viewObject, tableType } = this.props; const { isQueryChanged, isLive } = this.state; + + // console.log('components/ preview.jsx'); + // console.log('fields ', queryObject.fields); + const isFields = !_.isEmpty(queryObject.fields); + return (
{ const { LocalState } = context(); const viewObj = LocalState.get('VIEW_OBJECT') || {}; + + // console.log('viewObject ', viewObj); + const { chartType, pivot } = viewObj; const queryObj = LocalState.get('SQL_QUERY_OBJECT') || {}; const { fields } = queryObj; if (chartType && !fields.filter(f => f.constructorType !== undefined).length) { + console.log('.......................................'); + console.log('determine fields call '); const defaultedFields = determineDefaultFields(fields, chartType, pivot); + console.log('determine fields call result ', defaultedFields); if (defaultedFields) updateSQLQueryObj({ fields: defaultedFields }); + + console.log('new qsql query obj ', LocalState.get('SQL_QUERY_OBJECT')); } onData(null, { viewObj, queryObj }); }; diff --git a/client/modules/workplace/containers/preview/preview.js b/client/modules/workplace/containers/preview/preview.js index 7de7904..0402fda 100644 --- a/client/modules/workplace/containers/preview/preview.js +++ b/client/modules/workplace/containers/preview/preview.js @@ -4,6 +4,12 @@ import { useDeps, composeWithTracker, composeAll } from 'mantra-core'; export const composer = ({ context, getStringSQLQuery }, onData) => { const { LocalState } = context(); const queryObject = LocalState.get('SQL_QUERY_OBJECT') || {}; + + console.log('get SQL_QUERY_OBJECT before fail'); + console.log(LocalState.get('SQL_QUERY_OBJECT')); + console.log('View object pivot'); + console.log(LocalState.get('VIEW_OBJECT').pivot); + const viewObject = LocalState.get('VIEW_OBJECT') || {}; viewObject.query = getStringSQLQuery(queryObject); diff --git a/client/modules/workplace/containers/workplace.js b/client/modules/workplace/containers/workplace.js index 9b0d553..6169489 100644 --- a/client/modules/workplace/containers/workplace.js +++ b/client/modules/workplace/containers/workplace.js @@ -12,6 +12,9 @@ export const composer = ({ context, setSQLQueryObj }, onData) => { const { chartType, query: savedQuery } = viewObject; parseCollectionFieldNames(queryObject.collectionFields); setSQLQueryObj(queryObject, tableType); + + // console.log('VIEW_OBJECT from database ', viewObject); + LocalState.set('VIEW_OBJECT', viewObject); onData(null, { isSavedChart: true, tableType, chartType, savedQuery }); } diff --git a/lib/data_processing.js b/lib/data_processing.js index 47b2b19..e7a63d0 100644 --- a/lib/data_processing.js +++ b/lib/data_processing.js @@ -5,6 +5,11 @@ import { _ } from 'meteor/underscore'; export default { process(rawData, fields) { + + // console.log('process data function99999'); + // console.log('rawData ', rawData); + // console.log('fields ', fields); + let data = rawData; if (!rawData) return null; const dateField = fields.find((field) => field.currentType === 'date'); @@ -20,6 +25,11 @@ export default { }, othersChart(rawData, fields, chartType) { + // + // console.log('Others Chart function!!!!!!!!!!!!'); + // console.log('Fields ', fields); + // console.log('Chart type ', chartType); + const dimensions = this.filterFields('dimensions', fields, chartType); const measures = this.filterFields('measures', fields, chartType); const dName = dimensions[0] && dimensions[0].name; @@ -71,6 +81,12 @@ export default { }, getStackedDatasets(rawData, labels, dimensions, measures, fields) { + // console.log('getStackedDatasets function call !!!!!!!!!!'); + + console.log('dimensions ', dimensions[0].name, dimensions[1].name); + console.log('mesures ', measures[0].name); + + const dim1Name = dimensions[0].name; const dim2Name = dimensions[1].name; const measureName = measures[0].name; @@ -80,6 +96,7 @@ export default { const dims = [dim1Name, dim2Name]; const aggFunc = fields.find(f => f.name === measureName).expression .match(/avg|sum|min|max|count/i)[0]; + // console.log('After error log !!!!!!!!!!!!!!!!!!!!!!!!!!!'); fullRawData = this.getSubTotals(rawData, aggFunc, dims, measureName); } else fullRawData = rawData; From b1809c41470e183826e91bbed7be66751822f8de Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Tue, 16 May 2017 16:16:40 +0300 Subject: [PATCH 04/10] fix parcing HAVING part of query --- client/modules/workplace/actions/pivot.js | 3 -- client/modules/workplace/actions/preview.js | 38 ------------------- .../workplace/actions/workplace_state.js | 14 +++---- .../constructors/chart-constructor.js | 8 ---- .../workplace/containers/preview/preview.js | 6 --- lib/data_processing.js | 11 ------ lib/sql_parser.js | 16 +++++++- 7 files changed, 19 insertions(+), 77 deletions(-) diff --git a/client/modules/workplace/actions/pivot.js b/client/modules/workplace/actions/pivot.js index fd2f983..5cdd5a6 100644 --- a/client/modules/workplace/actions/pivot.js +++ b/client/modules/workplace/actions/pivot.js @@ -36,9 +36,6 @@ export default { } }, updateSorting({ LocalState }, column) { - - console.log('updateSorting function'); - const col = typeof column === 'object' ? _.omit(column, ['colGroup1Value', 'colGroup2Value']) : column; const viewObject = LocalState.get('VIEW_OBJECT'); diff --git a/client/modules/workplace/actions/preview.js b/client/modules/workplace/actions/preview.js index 8d7781e..6989ef6 100644 --- a/client/modules/workplace/actions/preview.js +++ b/client/modules/workplace/actions/preview.js @@ -39,51 +39,19 @@ export default { const database = LocalState.get('CURRENT_DATABASE'); LocalState.set('QUASAR_REQUEST_ID', requestId); - // console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); - // console.log('get qusaar data method call'); - Meteor.call('quasar.getData', database, query, requestId, (err, { data = [], answerId } = {}) => { if (err) { - // console.log('!!!!!!!!!!!method call error'); - // console.log(err); cb({ error: true }); } else if (LocalState.get('QUASAR_REQUEST_ID') === answerId || isSingleRequest) { - // console.log('!!!!!!!!!!!method call success'); - // console.log('data ', data); - // console.log('fields ', fields); - - - // console.log('!!!!!! Client Processong data after submitting sql editor '); const processedData = dataProcessing.process(data, fields); - // console.log('222222222222222222222222 before callback'); cb({ data: processedData }); } } ); }, determineDefaultFields({ Notificator, LocalState }, fields, chartType, pivot) { - - // todo - // there are problen in this function - - // console.log('determineDefaultFields function call 222222222'); - - // console.log('notificator ', Notificator); - // console.log('fields ', fields); - // console.log('chartType ', chartType); - // console.log('pivot ', pivot); - - console.log('///////////////////////////////////////////////'); - // console.log('fields ', fields); - // console.log('recieved constructors ', pivot.model.constructors); - - - const viewObj = LocalState.get('VIEW_OBJECT'); - - // console.log('ViewObject ', viewObj); - let result; if (pivot && pivot.model) { if (pivot.model.constructors) { @@ -133,16 +101,10 @@ export default { if (neededFields.filter(f => f.id).length) result = fields; } - console.log('result ', result); - return result; function determineFieldsBySavedConstructors(fieldsArr, constructors) { - console.log('dimentions ', constructors.dimensions); - console.log('measures ', constructors.measures); - fieldsArr.forEach(field => { - console.log(field.name, ' filed id=', field.id, 'dimentions includes ', constructors.dimensions.includes(field.id)); if (constructors.dimensions.includes(field.id)) { field.constructorType = 'dimensions'; } else if (constructors.measures.includes(field.id)) { diff --git a/client/modules/workplace/actions/workplace_state.js b/client/modules/workplace/actions/workplace_state.js index 01ed96e..c88e69c 100644 --- a/client/modules/workplace/actions/workplace_state.js +++ b/client/modules/workplace/actions/workplace_state.js @@ -17,6 +17,11 @@ export default { console.log('Old sql query ', oldQueryObj); + + //todo + // there are problems with parsed object + // there are no filters fields in fields array items + console.log('Parserd sql query', queryObj); LocalState.set('SQL_QUERY_OBJECT', queryObj); @@ -135,18 +140,9 @@ function setFieldsConstructorsType(fieldsList, LocalState) { } }); - // console.log('......................................'); - // console.log('Construcor types ', constructorTypes); - - if (isConstructorsFinded) { const viewObj = LocalState.get('VIEW_OBJECT'); viewObj.pivot.model.constructors = constructorTypes; - - console.log('/////////////////////////////////////////'); - console.log('fields ', fieldsList); - console.log('saved constructors ', viewObj.pivot.model.constructors); - LocalState.set('VIEW_OBJECT', viewObj); } } diff --git a/client/modules/workplace/containers/constructors/chart-constructor.js b/client/modules/workplace/containers/constructors/chart-constructor.js index 08951a5..048c42d 100644 --- a/client/modules/workplace/containers/constructors/chart-constructor.js +++ b/client/modules/workplace/containers/constructors/chart-constructor.js @@ -4,20 +4,12 @@ import ChartConstructor from '../../components/constructors/chart-constructor.js export const composer = ({ context, determineDefaultFields, updateSQLQueryObj }, onData) => { const { LocalState } = context(); const viewObj = LocalState.get('VIEW_OBJECT') || {}; - - // console.log('viewObject ', viewObj); - const { chartType, pivot } = viewObj; const queryObj = LocalState.get('SQL_QUERY_OBJECT') || {}; const { fields } = queryObj; if (chartType && !fields.filter(f => f.constructorType !== undefined).length) { - console.log('.......................................'); - console.log('determine fields call '); const defaultedFields = determineDefaultFields(fields, chartType, pivot); - console.log('determine fields call result ', defaultedFields); if (defaultedFields) updateSQLQueryObj({ fields: defaultedFields }); - - console.log('new qsql query obj ', LocalState.get('SQL_QUERY_OBJECT')); } onData(null, { viewObj, queryObj }); }; diff --git a/client/modules/workplace/containers/preview/preview.js b/client/modules/workplace/containers/preview/preview.js index 0402fda..7de7904 100644 --- a/client/modules/workplace/containers/preview/preview.js +++ b/client/modules/workplace/containers/preview/preview.js @@ -4,12 +4,6 @@ import { useDeps, composeWithTracker, composeAll } from 'mantra-core'; export const composer = ({ context, getStringSQLQuery }, onData) => { const { LocalState } = context(); const queryObject = LocalState.get('SQL_QUERY_OBJECT') || {}; - - console.log('get SQL_QUERY_OBJECT before fail'); - console.log(LocalState.get('SQL_QUERY_OBJECT')); - console.log('View object pivot'); - console.log(LocalState.get('VIEW_OBJECT').pivot); - const viewObject = LocalState.get('VIEW_OBJECT') || {}; viewObject.query = getStringSQLQuery(queryObject); diff --git a/lib/data_processing.js b/lib/data_processing.js index e7a63d0..5c7ac89 100644 --- a/lib/data_processing.js +++ b/lib/data_processing.js @@ -5,11 +5,6 @@ import { _ } from 'meteor/underscore'; export default { process(rawData, fields) { - - // console.log('process data function99999'); - // console.log('rawData ', rawData); - // console.log('fields ', fields); - let data = rawData; if (!rawData) return null; const dateField = fields.find((field) => field.currentType === 'date'); @@ -81,12 +76,6 @@ export default { }, getStackedDatasets(rawData, labels, dimensions, measures, fields) { - // console.log('getStackedDatasets function call !!!!!!!!!!'); - - console.log('dimensions ', dimensions[0].name, dimensions[1].name); - console.log('mesures ', measures[0].name); - - const dim1Name = dimensions[0].name; const dim2Name = dimensions[1].name; const measureName = measures[0].name; diff --git a/lib/sql_parser.js b/lib/sql_parser.js index 5a0317f..f4f72e8 100644 --- a/lib/sql_parser.js +++ b/lib/sql_parser.js @@ -20,13 +20,20 @@ export default { return objectSQL; }, parseToQueryObject(rawQuery, fields) { + console.log('sql_parser parse to query Object function'); + const query = this.parse(rawQuery); const queryObject = {}; const having = getHavingObj(query); + console.log('query ', query); + console.log('having object ', having); + queryObject.pagination = getPagination(query); queryObject.fields = getFields(query, having); + console.log('queryObject.fields ', queryObject.fields); + query.groupBy && query.groupBy.forEach(fieldIndex => { queryObject.fields[fieldIndex].grouping = true; }); @@ -125,11 +132,16 @@ function getOrderBy(query, select) { } function getHaving(query) { - let having = query.match(/^\s*having\s+([\w([\])"'`*:&;.,<>%@=\/\s]+)limit\s/im); + console.log('having parser!!!!!!!!!!!!!!!!!!!'); + + let having = query.match(/^\s*having\s+([\w([\])"'`*:&;.,-<>%@=\/\s]+)limit\s/im); if (having) { having = having[1].replace(cutOff, ''); having = removeBraces(having); + console.log('parseHaveingConditions '); + console.log(parseHaveingConditions(having)); + return parseHaveingConditions(having); } return null; @@ -210,7 +222,7 @@ function parseHaveingConditions(str) { return filters.map((filter, i) => { const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; // const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`.,/\\s]+)`, 'i'); - const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`*:&;%@.,/\\s]+)`, 'i'); + const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`*:&;%@.,-/\\s]+)`, 'i'); obj.exp2 = filter.match(regex)[1]; obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, ''); obj.operator = obj.operator.replace(/\s/g, ''); From e8009855810bdf958e67f5bb30156467904d5fe1 Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Tue, 16 May 2017 16:30:19 +0300 Subject: [PATCH 05/10] remove redudant code --- client/modules/workplace/actions/workplace_state.js | 12 ------------ .../workplace/components/preview/chart_canvas.jsx | 8 -------- .../components/preview/data_visualisation.jsx | 3 --- .../modules/workplace/components/preview/preview.jsx | 4 ---- .../workplace/components/sql_editor/sql_button.jsx | 3 --- client/modules/workplace/containers/workplace.js | 2 -- lib/data_processing.js | 6 ------ lib/sql_parser.js | 7 ------- 8 files changed, 45 deletions(-) diff --git a/client/modules/workplace/actions/workplace_state.js b/client/modules/workplace/actions/workplace_state.js index c88e69c..28cb2c0 100644 --- a/client/modules/workplace/actions/workplace_state.js +++ b/client/modules/workplace/actions/workplace_state.js @@ -4,9 +4,6 @@ import SQLParser from '/lib/sql_parser'; export default { setSQLQuery({ LocalState }, query) { - console.log('|||||||||||||||||||||||||||||||||||||||||'); - console.log('Set sql query object fubction after submit sql form'); - const fields = LocalState.get('COLLECTION_FIELDS'); const oldQueryObj = LocalState.get('SQL_QUERY_OBJECT'); setFieldsConstructorsType(oldQueryObj.fields, LocalState); @@ -15,15 +12,6 @@ export default { queryObj.from = oldQueryObj.from; queryObj.on = oldQueryObj.on; - - console.log('Old sql query ', oldQueryObj); - - //todo - // there are problems with parsed object - // there are no filters fields in fields array items - - console.log('Parserd sql query', queryObj); - LocalState.set('SQL_QUERY_OBJECT', queryObj); }, diff --git a/client/modules/workplace/components/preview/chart_canvas.jsx b/client/modules/workplace/components/preview/chart_canvas.jsx index 4cb9936..e61db68 100644 --- a/client/modules/workplace/components/preview/chart_canvas.jsx +++ b/client/modules/workplace/components/preview/chart_canvas.jsx @@ -24,14 +24,6 @@ class ChartCanvas extends React.Component { render() { const { data, fields, chartType, heightIsFixed, tableType, needRedraw, maxHeight } = this.props; - - - // console.log('Render chart canvas!!'); - // console.log('Data processing call after submit sql editor'); - // console.log('fields ', fields); - - // there are wrong data in fields object - const chartData = dataProcessing.chart(data, fields, chartType, tableType); const Chart = getChart(chartType); let height; diff --git a/client/modules/workplace/components/preview/data_visualisation.jsx b/client/modules/workplace/components/preview/data_visualisation.jsx index 32edf9e..8507f44 100644 --- a/client/modules/workplace/components/preview/data_visualisation.jsx +++ b/client/modules/workplace/components/preview/data_visualisation.jsx @@ -23,9 +23,6 @@ class DataVisualisation extends React.Component { const { chartType } = viewObject; const tableHeight = `${window.innerHeight - 74}px`; - // console.log('preview/data_visualization file!!!!!!'); - // console.log('fields ', queryObject.fields); - return (
{tableType === 'simple' && diff --git a/client/modules/workplace/components/preview/preview.jsx b/client/modules/workplace/components/preview/preview.jsx index d6a45d2..13c7c42 100644 --- a/client/modules/workplace/components/preview/preview.jsx +++ b/client/modules/workplace/components/preview/preview.jsx @@ -43,10 +43,6 @@ class Preview extends React.Component { render() { const { savedQuery, queryObject, viewObject, tableType } = this.props; const { isQueryChanged, isLive } = this.state; - - // console.log('components/ preview.jsx'); - // console.log('fields ', queryObject.fields); - const isFields = !_.isEmpty(queryObject.fields); return ( diff --git a/client/modules/workplace/components/sql_editor/sql_button.jsx b/client/modules/workplace/components/sql_editor/sql_button.jsx index 52a7e5a..6c46021 100644 --- a/client/modules/workplace/components/sql_editor/sql_button.jsx +++ b/client/modules/workplace/components/sql_editor/sql_button.jsx @@ -39,9 +39,6 @@ class SQLButton extends React.Component { this.setState({ query }); } handleSubmit() { - - // console.log('SQl editor submit !!!!!'); - const { query } = this.state; if (query !== this.props.query) { this.props.setSQLQuery(query); diff --git a/client/modules/workplace/containers/workplace.js b/client/modules/workplace/containers/workplace.js index 6169489..3e15a6b 100644 --- a/client/modules/workplace/containers/workplace.js +++ b/client/modules/workplace/containers/workplace.js @@ -13,8 +13,6 @@ export const composer = ({ context, setSQLQueryObj }, onData) => { parseCollectionFieldNames(queryObject.collectionFields); setSQLQueryObj(queryObject, tableType); - // console.log('VIEW_OBJECT from database ', viewObject); - LocalState.set('VIEW_OBJECT', viewObject); onData(null, { isSavedChart: true, tableType, chartType, savedQuery }); } diff --git a/lib/data_processing.js b/lib/data_processing.js index 5c7ac89..47b2b19 100644 --- a/lib/data_processing.js +++ b/lib/data_processing.js @@ -20,11 +20,6 @@ export default { }, othersChart(rawData, fields, chartType) { - // - // console.log('Others Chart function!!!!!!!!!!!!'); - // console.log('Fields ', fields); - // console.log('Chart type ', chartType); - const dimensions = this.filterFields('dimensions', fields, chartType); const measures = this.filterFields('measures', fields, chartType); const dName = dimensions[0] && dimensions[0].name; @@ -85,7 +80,6 @@ export default { const dims = [dim1Name, dim2Name]; const aggFunc = fields.find(f => f.name === measureName).expression .match(/avg|sum|min|max|count/i)[0]; - // console.log('After error log !!!!!!!!!!!!!!!!!!!!!!!!!!!'); fullRawData = this.getSubTotals(rawData, aggFunc, dims, measureName); } else fullRawData = rawData; diff --git a/lib/sql_parser.js b/lib/sql_parser.js index f4f72e8..4049999 100644 --- a/lib/sql_parser.js +++ b/lib/sql_parser.js @@ -50,7 +50,6 @@ export default { }; function getSelect(query) { - // query.match(/^\s*select\s+([\w([\])"'`.,/\s]+)from\s/i); let select = query.match(/^\s*select\s+([\w([\])"'`*:&;.,/\s]+)from\s/i); if (!select) return { error: 'Wrong syntax: "SELECT FROM " expected' }; select = select[1]; @@ -132,16 +131,11 @@ function getOrderBy(query, select) { } function getHaving(query) { - console.log('having parser!!!!!!!!!!!!!!!!!!!'); let having = query.match(/^\s*having\s+([\w([\])"'`*:&;.,-<>%@=\/\s]+)limit\s/im); if (having) { having = having[1].replace(cutOff, ''); having = removeBraces(having); - - console.log('parseHaveingConditions '); - console.log(parseHaveingConditions(having)); - return parseHaveingConditions(having); } return null; @@ -221,7 +215,6 @@ function parseHaveingConditions(str) { } return filters.map((filter, i) => { const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; - // const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`.,/\\s]+)`, 'i'); const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`*:&;%@.,-/\\s]+)`, 'i'); obj.exp2 = filter.match(regex)[1]; obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, ''); From b36e0649a47345def39ef3454e65a277d52c796b Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Tue, 16 May 2017 16:39:46 +0300 Subject: [PATCH 06/10] remove redudant consolelogs --- lib/sql_parser.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/sql_parser.js b/lib/sql_parser.js index 4049999..47c5110 100644 --- a/lib/sql_parser.js +++ b/lib/sql_parser.js @@ -20,20 +20,13 @@ export default { return objectSQL; }, parseToQueryObject(rawQuery, fields) { - console.log('sql_parser parse to query Object function'); - const query = this.parse(rawQuery); const queryObject = {}; const having = getHavingObj(query); - console.log('query ', query); - console.log('having object ', having); - queryObject.pagination = getPagination(query); queryObject.fields = getFields(query, having); - console.log('queryObject.fields ', queryObject.fields); - query.groupBy && query.groupBy.forEach(fieldIndex => { queryObject.fields[fieldIndex].grouping = true; }); From 11c9ea68d0a462c6fec04945b2a8bacb4bbc7eab Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Wed, 17 May 2017 10:08:46 +0300 Subject: [PATCH 07/10] update some code --- client/modules/workplace/actions/preview.js | 53 +++++++++---------- .../workplace/actions/workplace_state.js | 23 ++++---- lib/sql_parser.js | 4 +- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/client/modules/workplace/actions/preview.js b/client/modules/workplace/actions/preview.js index 6989ef6..afa5514 100644 --- a/client/modules/workplace/actions/preview.js +++ b/client/modules/workplace/actions/preview.js @@ -51,13 +51,37 @@ export default { } ); }, + determineDefaultFields({ Notificator, LocalState }, fields, chartType, pivot) { + const determineFieldsBySavedConstructors = (fieldsArr, constructors) => + fieldsArr.map(field => { + if (constructors.dimensions.includes(field.id)) { + field.constructorType = 'dimensions'; + } else if (constructors.measures.includes(field.id)) { + field.constructorType = 'measures'; + } + return field; + }); + const determineFieldsByModel = (fieldsArr, pivotModel) => { + const measuresId = pivotModel.values; + let dimensionsCounter = 0; + return fieldsArr.map(field => { + if (!~measuresId.indexOf(field.id)) { + dimensionsCounter++; + if (dimensionsCounter < 3) field.constructorType = 'dimensions'; + } else { + field.constructorType = 'measures'; + } + return field; + }); + }; let result; + if (pivot && pivot.model) { if (pivot.model.constructors) { result = determineFieldsBySavedConstructors(fields, pivot.model.constructors); } else { - result = determineFieldsByModel(fields); + result = determineFieldsByModel(fields, pivot.model); } } else { @@ -102,37 +126,12 @@ export default { } return result; - - function determineFieldsBySavedConstructors(fieldsArr, constructors) { - fieldsArr.forEach(field => { - if (constructors.dimensions.includes(field.id)) { - field.constructorType = 'dimensions'; - } else if (constructors.measures.includes(field.id)) { - field.constructorType = 'measures'; - } - }); - return fieldsArr; - } - - function determineFieldsByModel(fieldsArr) { - const measuresId = pivot.model.values; - let dimensionsCounter = 0; - fieldsArr.forEach(f => { - if (!~measuresId.indexOf(f.id)) { - dimensionsCounter++; - if (dimensionsCounter < 3) f.constructorType = 'dimensions'; - } else { - f.constructorType = 'measures'; - } - }); - return fieldsArr; - } }, getNewChartModelFields(context, { chartType, fields }, { fieldId, isChecked, label }) { const checkedFieldsNumber = fields.filter(f => f.constructorType === label).length; const newFields = fields.map(f => _.extend(_.clone(f), - f.id === fieldId ? { constructorType: isChecked ? label : null } : {}) + f.id === fieldId ? { constructorType: isChecked ? label : null } : {}) ); const maxCheckedFields = (() => { let res = chartTypeRules[chartType][label]; diff --git a/client/modules/workplace/actions/workplace_state.js b/client/modules/workplace/actions/workplace_state.js index 28cb2c0..cda31fe 100644 --- a/client/modules/workplace/actions/workplace_state.js +++ b/client/modules/workplace/actions/workplace_state.js @@ -6,7 +6,13 @@ export default { setSQLQuery({ LocalState }, query) { const fields = LocalState.get('COLLECTION_FIELDS'); const oldQueryObj = LocalState.get('SQL_QUERY_OBJECT'); - setFieldsConstructorsType(oldQueryObj.fields, LocalState); + const fieldsConstructors = getFieldsConstructorsType(oldQueryObj.fields); + + if (fieldsConstructors) { + const viewObj = LocalState.get('VIEW_OBJECT'); + viewObj.pivot.model.constructors = fieldsConstructors; + LocalState.set('VIEW_OBJECT', viewObj); + } const queryObj = SQLParser.parseToQueryObject(query, fields); queryObj.from = oldQueryObj.from; @@ -113,8 +119,9 @@ function resetData(LocalState) { } } -function setFieldsConstructorsType(fieldsList, LocalState) { - let isConstructorsFinded = false; +function getFieldsConstructorsType(fieldsList, LocalState) { + let isConstructorsFound = false; + let result = false; const constructorTypes = { measures: [], dimensions: [], @@ -124,13 +131,11 @@ function setFieldsConstructorsType(fieldsList, LocalState) { const constructorType = item.constructorType; if (constructorType) { constructorTypes[constructorType].push(index); - isConstructorsFinded = true; + isConstructorsFound = true; } }); - if (isConstructorsFinded) { - const viewObj = LocalState.get('VIEW_OBJECT'); - viewObj.pivot.model.constructors = constructorTypes; - LocalState.set('VIEW_OBJECT', viewObj); - } + if (isConstructorsFound) { result = constructorTypes; } + + return result; } diff --git a/lib/sql_parser.js b/lib/sql_parser.js index 47c5110..4883936 100644 --- a/lib/sql_parser.js +++ b/lib/sql_parser.js @@ -129,7 +129,7 @@ function getHaving(query) { if (having) { having = having[1].replace(cutOff, ''); having = removeBraces(having); - return parseHaveingConditions(having); + return parseHavingConditions(having); } return null; @@ -191,7 +191,7 @@ function getOffset(query) { return null; } -function parseHaveingConditions(str) { +function parseHavingConditions(str) { const filters = []; const andOr = str.match(/\s+(and|or)\s+/gi); From 2c41af7fbd2a470b703aa81341584cf44501f26a Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Wed, 17 May 2017 12:42:13 +0300 Subject: [PATCH 08/10] fix dimentions and measures setting logic bug --- client/modules/workplace/actions/preview.js | 16 +++++++++------- .../modules/workplace/actions/workplace_state.js | 16 ++++++++++------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/client/modules/workplace/actions/preview.js b/client/modules/workplace/actions/preview.js index afa5514..3f018be 100644 --- a/client/modules/workplace/actions/preview.js +++ b/client/modules/workplace/actions/preview.js @@ -52,6 +52,8 @@ export default { ); }, + // todo !!! + // this function must be refactored in th future determineDefaultFields({ Notificator, LocalState }, fields, chartType, pivot) { const determineFieldsBySavedConstructors = (fieldsArr, constructors) => fieldsArr.map(field => { @@ -75,16 +77,16 @@ export default { return field; }); }; + const viewObj = LocalState.get('VIEW_OBJECT'); let result; + if (viewObj.fieldsConstructors) { + return determineFieldsBySavedConstructors(fields, viewObj.fieldsConstructors); + } + if (pivot && pivot.model) { - if (pivot.model.constructors) { - result = determineFieldsBySavedConstructors(fields, pivot.model.constructors); - } else { - result = determineFieldsByModel(fields, pivot.model); - } - } else - { + result = determineFieldsByModel(fields, pivot.model); + } else { const getNeededfields = () => { const fieldsArray = []; _.each(chartTypeRules[chartType], (val, key) => { diff --git a/client/modules/workplace/actions/workplace_state.js b/client/modules/workplace/actions/workplace_state.js index cda31fe..1389752 100644 --- a/client/modules/workplace/actions/workplace_state.js +++ b/client/modules/workplace/actions/workplace_state.js @@ -10,7 +10,7 @@ export default { if (fieldsConstructors) { const viewObj = LocalState.get('VIEW_OBJECT'); - viewObj.pivot.model.constructors = fieldsConstructors; + viewObj.fieldsConstructors = fieldsConstructors; LocalState.set('VIEW_OBJECT', viewObj); } @@ -119,23 +119,27 @@ function resetData(LocalState) { } } -function getFieldsConstructorsType(fieldsList, LocalState) { +function getFieldsConstructorsType(fieldsList) { let isConstructorsFound = false; - let result = false; const constructorTypes = { measures: [], dimensions: [], }; - fieldsList.forEach((item, index) => { - const constructorType = item.constructorType; + let result = fieldsList.map((field, index) => { + const constructorType = field.constructorType; if (constructorType) { constructorTypes[constructorType].push(index); isConstructorsFound = true; } + return field; }); - if (isConstructorsFound) { result = constructorTypes; } + if (isConstructorsFound) { + result = constructorTypes; + } else { + result = false; + } return result; } From c42a3ccae35a084bff7950aa10f25ffbace7b21d Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Thu, 18 May 2017 12:21:58 +0300 Subject: [PATCH 09/10] sql parser files structure refactored --- .../workplace/actions/workplace_state.js | 37 +- lib/sql_parser.js | 333 ------------------ lib/sql_parser/index.js | 1 + .../parser_utils/get_collection_fields.js | 32 ++ lib/sql_parser/parser_utils/get_fields.js | 14 + lib/sql_parser/parser_utils/get_from.js | 13 + lib/sql_parser/parser_utils/get_group_by.js | 18 + lib/sql_parser/parser_utils/get_having.js | 65 ++++ .../parser_utils/get_having_object.js | 17 + lib/sql_parser/parser_utils/get_join.js | 13 + lib/sql_parser/parser_utils/get_limit.js | 7 + lib/sql_parser/parser_utils/get_offset.js | 7 + lib/sql_parser/parser_utils/get_order_by.js | 22 ++ lib/sql_parser/parser_utils/get_pagination.js | 8 + lib/sql_parser/parser_utils/get_select.js | 29 ++ lib/sql_parser/parser_utils/get_where.js | 44 +++ lib/sql_parser/parser_utils/index.js | 13 + lib/sql_parser/sql_parser.js | 55 +++ lib/sql_parser/utils.js | 46 +++ 19 files changed, 421 insertions(+), 353 deletions(-) delete mode 100644 lib/sql_parser.js create mode 100644 lib/sql_parser/index.js create mode 100644 lib/sql_parser/parser_utils/get_collection_fields.js create mode 100644 lib/sql_parser/parser_utils/get_fields.js create mode 100644 lib/sql_parser/parser_utils/get_from.js create mode 100644 lib/sql_parser/parser_utils/get_group_by.js create mode 100644 lib/sql_parser/parser_utils/get_having.js create mode 100644 lib/sql_parser/parser_utils/get_having_object.js create mode 100644 lib/sql_parser/parser_utils/get_join.js create mode 100644 lib/sql_parser/parser_utils/get_limit.js create mode 100644 lib/sql_parser/parser_utils/get_offset.js create mode 100644 lib/sql_parser/parser_utils/get_order_by.js create mode 100644 lib/sql_parser/parser_utils/get_pagination.js create mode 100644 lib/sql_parser/parser_utils/get_select.js create mode 100644 lib/sql_parser/parser_utils/get_where.js create mode 100644 lib/sql_parser/parser_utils/index.js create mode 100644 lib/sql_parser/sql_parser.js create mode 100644 lib/sql_parser/utils.js diff --git a/client/modules/workplace/actions/workplace_state.js b/client/modules/workplace/actions/workplace_state.js index 1389752..f2ec70e 100644 --- a/client/modules/workplace/actions/workplace_state.js +++ b/client/modules/workplace/actions/workplace_state.js @@ -1,6 +1,6 @@ import { _ } from 'meteor/underscore'; import { pivotCellsLimit } from '/lib/constants'; -import SQLParser from '/lib/sql_parser'; +import { parseToQueryObject } from '/lib/sql_parser'; export default { setSQLQuery({ LocalState }, query) { @@ -14,7 +14,7 @@ export default { LocalState.set('VIEW_OBJECT', viewObj); } - const queryObj = SQLParser.parseToQueryObject(query, fields); + const queryObj = parseToQueryObject(query, fields); queryObj.from = oldQueryObj.from; queryObj.on = oldQueryObj.on; @@ -119,27 +119,24 @@ function resetData(LocalState) { } } -function getFieldsConstructorsType(fieldsList) { - let isConstructorsFound = false; - const constructorTypes = { - measures: [], - dimensions: [], - }; +const isPropEmptyArray = (prop, obj) => !obj[prop].length; - let result = fieldsList.map((field, index) => { - const constructorType = field.constructorType; +const getFieldsConstructor = fields => fields.reduce( + (constructor, { constructorType }, index) => { if (constructorType) { - constructorTypes[constructorType].push(index); - isConstructorsFound = true; + constructor[constructorType].push(index); } - return field; - }); - - if (isConstructorsFound) { - result = constructorTypes; - } else { - result = false; + return constructor; + }, { + measures: [], + dimensions: [], } +); + +function getFieldsConstructorsType(fields) { + const constructor = getFieldsConstructor(fields); + const isDimensions = !isPropEmptyArray('dimensions', constructor); + const isMeasures = !isPropEmptyArray('measures', constructor); - return result; + return (isDimensions || isMeasures) ? constructor : false; } diff --git a/lib/sql_parser.js b/lib/sql_parser.js deleted file mode 100644 index 4883936..0000000 --- a/lib/sql_parser.js +++ /dev/null @@ -1,333 +0,0 @@ -import { _ } from 'meteor/underscore'; - -const cutOff = - /(\sgroup by\s|\sorder by\s|\swhere\s|\shaving\s|\slimit\s|\soffset\s)+[\w([\])"'`.,/=<>\s]+$/i; - -export default { - parse(query) { - const objectSQL = { - select: getSelect(query), - from: getFrom(query), - having: getHaving(query), - where: getWhere(query), - limit: getLimit(query), - offset: getOffset(query), - join: getJoin(query), - }; - objectSQL.groupBy = getGroupBy(query, objectSQL.select); - objectSQL.orderBy = getOrderBy(query, objectSQL.select); - - return objectSQL; - }, - parseToQueryObject(rawQuery, fields) { - const query = this.parse(rawQuery); - const queryObject = {}; - const having = getHavingObj(query); - - queryObject.pagination = getPagination(query); - queryObject.fields = getFields(query, having); - - query.groupBy && query.groupBy.forEach(fieldIndex => { - queryObject.fields[fieldIndex].grouping = true; - }); - query.orderBy && query.orderBy.forEach(field => { - queryObject.fields[field.index].sort = field.type; - }); - - queryObject.collectionFields = getCollectionFields(query, fields); - - queryObject.join = query.join; - - return queryObject; - }, -}; - -function getSelect(query) { - let select = query.match(/^\s*select\s+([\w([\])"'`*:&;.,/\s]+)from\s/i); - if (!select) return { error: 'Wrong syntax: "SELECT FROM " expected' }; - select = select[1]; - const asNames = select.match(/\s+as\s+\w+/ig); - let names = []; - let expressions = []; - if (asNames) { - names = asNames.map(item => item.replace(/(^\s+as\s|\s)+/ig, '')); - expressions = [select.slice(0, select.indexOf(asNames[0]))]; - for (let i = 0; i < asNames.length - 1; i++) { - expressions.push(select.slice(select.indexOf(asNames[i]) + asNames[i].length, - select.indexOf(asNames[i + 1]))); - } - } else { - expressions = select.match(/\w+\.\w+/ig); - if (expressions) { - names = expressions.map(exp => exp.match(/\w+\.(\w+)/i)[1]); - } else { - return { error: 'Wrong fields!' }; - } - } - return names.map((name, i) => ({ - name: names[i], - expression: expressions[i].replace(/(^,|\s)/g, ''), - })); -} - -const getParamName = (queryPartial) => queryPartial.match(/\s+as\s+(\w+)/i); -const parseQueryPartial = (partial, paramName) => - partial.slice(0, paramName.index).match(/(\w+)\/(\w+)/i); - -function getFrom(query) { - const matchRes = query.match(/\sfrom\s+([\w([\])"'`.,/=<>\s]+$)/im); - if (!matchRes) return null; - const from = matchRes[1].replace(cutOff, ''); - const name = getParamName(from); - const path = parseQueryPartial(from, name); - return { db: path[1], collection: path[2], name: name[1] }; -} - -function getJoin(query) { - const matchRes = query.match(/\sjoin\s+([\w([\])"'`.,/=<>\s]+$)/im); - if (!matchRes) return null; - const from = matchRes[1].replace(cutOff, ''); - const name = getParamName(from); - const path = parseQueryPartial(from, name); - return path[2]; -} - -function getGroupBy(query, select) { - let groupBy = query.match(/^\s*group by\s+([\w([\])"'`*:&;.,/\s]+)/im); - if (groupBy) { - groupBy = groupBy[1].replace(cutOff, '') - .replace(/\s/g, ''); - return select.reduce((previous, field, i) => { - if (~groupBy.indexOf(field.expression)) previous.push(i); - return previous; - }, []); - } - return null; -} - -function getOrderBy(query, select) { - let orderBy = query.match(/\sorder by\s+([\w([\])"'`.,/=<>\s]+$)/i); - if (orderBy) { - orderBy = orderBy[1].replace(cutOff, ''); - return select.reduce((previous, field, index) => { - if (~orderBy.indexOf(field.name)) { - const obj = { index }; - const regex = new RegExp(`${field.name}\\s+(asc|desc)(\\s|,|$)`, 'i'); - const match = orderBy.match(regex); - if (match) obj.type = match[1].toLowerCase(); - previous.push(obj); - } - return previous; - }, []); - } - return null; -} - -function getHaving(query) { - - let having = query.match(/^\s*having\s+([\w([\])"'`*:&;.,-<>%@=\/\s]+)limit\s/im); - if (having) { - having = having[1].replace(cutOff, ''); - having = removeBraces(having); - return parseHavingConditions(having); - } - return null; - - - function removeBraces(queryPart) { - let result = ''; - - if (isCountExist(queryPart)) { - const countWordINdex = getWordIndex(queryPart, 'count('); - const afterCountIndex = getWordIndex(queryPart, ')', countWordINdex); - result += removebracesWithRegex(queryPart.substr(0, countWordINdex)); - result += queryPart.substring(countWordINdex, afterCountIndex + 1); - result += removeBraces(queryPart.substring(afterCountIndex + 1)); - } else { - result += removebracesWithRegex(queryPart); - } - return result; - } - - function isCountExist(queryToCheck) { - return queryToCheck.match(/COUNT[(]/gi); - } - - function removebracesWithRegex(line) { - return line.replace(/[()]/g, ''); - } - - function getWordIndex(line, word, fromPosition = 0) { - let result = line.indexOf(word, fromPosition); - if (result === -1) { - result = line.indexOf(word.toUpperCase(), fromPosition); - } - return result; - } -} - -function getWhere(query) { - let where = query.match(/\swhere\s+([\w([\])"'`.,/=<>\s]+$)/im); - if (where) { - where = where[1].replace(cutOff, ''); - const parsed = parseFiltering(where); - parsed.forEach( (item) => { - item.exp2 = item.exp2.replace(/^["]/i, '').replace(/["]$/i, ''); - }); - return parsed; - } - return null; -} - -function getLimit(query) { - const limit = query.match(/\slimit\s+(\d+)/i); - if (limit) return +limit[1]; - return null; -} - -function getOffset(query) { - const offset = query.match(/\soffset\s+(\d+)/i); - if (offset) return +offset[1]; - return null; -} - -function parseHavingConditions(str) { - const filters = []; - const andOr = str.match(/\s+(and|or)\s+/gi); - - if (andOr) { - let rest = str.slice(); - andOr.forEach(item => { - const part = rest.slice(0, rest.indexOf(item)); - filters.push(part); - rest = rest.slice(rest.indexOf(item) + item.length); - }); - filters.push(rest); - } else { - filters.push(str); - } - return filters.map((filter, i) => { - const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; - const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`*:&;%@.,-/\\s]+)`, 'i'); - obj.exp2 = filter.match(regex)[1]; - obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, ''); - obj.operator = obj.operator.replace(/\s/g, ''); - obj.exp2 = obj.exp2.replace(/^\s+|\s+$/g, ''); - if (andOr && i < andOr.length) obj.join = andOr[i].replace(/\s/g, ''); - return obj; - }); -} - -function parseFiltering(str) { - const filters = []; - const andOr = str.match(/\s+(and|or)\s+/gi); - if (andOr) { - let rest = str.slice(); - andOr.forEach(item => { - const part = rest.slice(0, rest.indexOf(item)); - filters.push(part); - rest = rest.slice(rest.indexOf(item) + item.length); - }); - filters.push(rest); - } else { - filters.push(str); - } - return filters.map((filter, i) => { - const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; - const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`.,/\\s]+)`, 'i'); - obj.exp2 = filter.match(regex)[1]; - obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, ''); - obj.operator = obj.operator.replace(/\s/g, ''); - obj.exp2 = obj.exp2.replace(/^\s+|\s+$/g, ''); - if (andOr && i < andOr.length) obj.join = andOr[i].replace(/\s/g, ''); - return obj; - }); -} - -function getPagination(query) { - return { - limit: query.limit, - page: query.skip ? query.skip / query.limit : 1, - }; -} - -function getHavingObj(query) { - const having = {}; - if (query.having) { - query.having.forEach(item => { - if (!having[item.exp1]) having[item.exp1] = []; - having[item.exp1].push({ - value: item.exp2, - joinOperator: item.join, - operator: item.operator, - }); - }); - } - return having; -} - -function getFields(query, having) { - return query.select.map((field, i) => ( - { - id: i, - name: field.name, - expression: field.expression, - grouping: field.date ? field.date : false, - sort: false, - filters: having[field.expression], - } - )); -} - -function getCollectionFields(query, fields) { - const collectionFields = {}; - if (query.where) { - query.where.forEach(item => { - const expression = item.exp1; - - if (!collectionFields[expression]) { - collectionFields[expression] = { - filters: [], - name: _.last(expression.split('.')), - constructorType: 'filters', - type: getType(expression, fields[0]), - expression, - }; - collectionFields[expression].filters = []; - } - - collectionFields[expression].filters.push({ - value: item.exp2, - joinOperator: item.join, - operator: item.operator, - }); - }); - } - return collectionFields; -} - -function getType(expression, fields) { - let currentValue = fields; - const path = expression.split('.'); - path.forEach(pathItem => { - currentValue = currentValue[pathItem]; - }); - return getItemType(currentValue); -} - -function getItemType(value) { - if (_.isNumber(value)) return 'number'; - if (_.isDate(value)) return 'date'; - if (isDate(value)) return 'date'; - if (_.isString(value)) return 'string'; - if (_.isBoolean(value)) return 'boolean'; - if (_.isArray(value)) return 'array'; - if (_.isObject(value)) return 'object'; - return null; -} - -function isDate(value) { - const dateObj = new Date(value); - return dateObj.toString() !== 'Invalid Date' && - dateObj.toISOString().replace('.000', '') === value; -} diff --git a/lib/sql_parser/index.js b/lib/sql_parser/index.js new file mode 100644 index 0000000..ae2315f --- /dev/null +++ b/lib/sql_parser/index.js @@ -0,0 +1 @@ +export * from './sql_parser'; diff --git a/lib/sql_parser/parser_utils/get_collection_fields.js b/lib/sql_parser/parser_utils/get_collection_fields.js new file mode 100644 index 0000000..ee57293 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_collection_fields.js @@ -0,0 +1,32 @@ +import { _ } from 'meteor/underscore'; +import { getType } from '../utils'; + +const getCollectionFields = (query, fields) => { + const collectionFields = {}; + if (query.where) { + // todo refactor + query.where.forEach(item => { + const expression = item.exp1; + + if (!collectionFields[expression]) { + collectionFields[expression] = { + filters: [], + name: _.last(expression.split('.')), + constructorType: 'filters', + type: getType(expression, fields[0]), + expression, + }; + collectionFields[expression].filters = []; + } + + collectionFields[expression].filters.push({ + value: item.exp2, + joinOperator: item.join, + operator: item.operator, + }); + }); + } + return collectionFields; +}; + +export { getCollectionFields }; diff --git a/lib/sql_parser/parser_utils/get_fields.js b/lib/sql_parser/parser_utils/get_fields.js new file mode 100644 index 0000000..1fec375 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_fields.js @@ -0,0 +1,14 @@ +const getFields = (query, having) => { + return query.select.map((field, i) => ( + { + id: i, + name: field.name, + expression: field.expression, + grouping: field.date ? field.date : false, + sort: false, + filters: having[field.expression], + } + )); +}; + +export { getFields }; diff --git a/lib/sql_parser/parser_utils/get_from.js b/lib/sql_parser/parser_utils/get_from.js new file mode 100644 index 0000000..86b654b --- /dev/null +++ b/lib/sql_parser/parser_utils/get_from.js @@ -0,0 +1,13 @@ +import { cutOff, getParamName, parseQueryPartial } from '../utils'; + + +const getFrom = query => { + const matchRes = query.match(/\sfrom\s+([\w([\])"'`.,/=<>\s]+$)/im); + if (!matchRes) return null; + const from = matchRes[1].replace(cutOff, ''); + const name = getParamName(from); + const path = parseQueryPartial(from, name); + return { db: path[1], collection: path[2], name: name[1] }; +}; + +export { getFrom }; diff --git a/lib/sql_parser/parser_utils/get_group_by.js b/lib/sql_parser/parser_utils/get_group_by.js new file mode 100644 index 0000000..4bf8f1d --- /dev/null +++ b/lib/sql_parser/parser_utils/get_group_by.js @@ -0,0 +1,18 @@ +import { cutOff } from '../utils'; + +const getGroupBy = (query, select) => { + let groupBy = query.match(/^\s*group by\s+([\w([\])"'`*:&;.,/\s]+)/im); + if (groupBy) { + // todo refactor + groupBy = groupBy[1].replace(cutOff, '') + .replace(/\s/g, ''); + + return select.reduce((previous, field, i) => { + if (~groupBy.indexOf(field.expression)) previous.push(i); + return previous; + }, []); + } + return null; +}; + +export { getGroupBy }; diff --git a/lib/sql_parser/parser_utils/get_having.js b/lib/sql_parser/parser_utils/get_having.js new file mode 100644 index 0000000..6ebde92 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_having.js @@ -0,0 +1,65 @@ +import { replaceCuttOff } from '../utils'; + +const parseHavingConditions = (str) => { + const filters = []; + const andOr = str.match(/\s+(and|or)\s+/gi); + + if (andOr) { + let rest = str.slice(); + andOr.forEach(item => { + const part = rest.slice(0, rest.indexOf(item)); + filters.push(part); + rest = rest.slice(rest.indexOf(item) + item.length); + }); + filters.push(rest); + } else { + filters.push(str); + } + return filters.map((filter, i) => { + const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; + const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`*:&;%@.,-/\\s]+)`, 'i'); + obj.exp2 = filter.match(regex)[1]; + obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, ''); + obj.operator = obj.operator.replace(/\s/g, ''); + obj.exp2 = obj.exp2.replace(/^\s+|\s+$/g, ''); + if (andOr && i < andOr.length) obj.join = andOr[i].replace(/\s/g, ''); + return obj; + }); +}; + + +const getHaving = query => { + // todo refactor + const getWordIndex = (line, word, fromPosition = 0) => { + let result = line.indexOf(word, fromPosition); + if (result === -1) { + result = line.indexOf(word.toUpperCase(), fromPosition); + } + return result; + }; + const isCountExist = (queryToCheck) => queryToCheck.match(/COUNT[(]/gi); + const removebracesWithRegex = (line) => line.replace(/[()]/g, ''); + const removeBraces = (queryPart) => { + let result = ''; + if (isCountExist(queryPart)) { + const countWordINdex = getWordIndex(queryPart, 'count('); + const afterCountIndex = getWordIndex(queryPart, ')', countWordINdex); + result += removebracesWithRegex(queryPart.substr(0, countWordINdex)); + result += queryPart.substring(countWordINdex, afterCountIndex + 1); + result += removeBraces(queryPart.substring(afterCountIndex + 1)); + } else { + result += removebracesWithRegex(queryPart); + } + return result; + }; + const having = query.match(/^\s*having\s+([\w([\])"'`*:&;.,-<>%@=\/\s]+)limit\s/im) || []; + if (!having.length) return null; + + return having + .filter((el, i) => i === 1) + .map(replaceCuttOff) + .map(removeBraces) + .map(parseHavingConditions)[0]; +}; + +export { getHaving }; diff --git a/lib/sql_parser/parser_utils/get_having_object.js b/lib/sql_parser/parser_utils/get_having_object.js new file mode 100644 index 0000000..f477417 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_having_object.js @@ -0,0 +1,17 @@ +const getHavingObj = query => { + const having = {}; + if (query.having) { + // todo refactor + query.having.forEach(item => { + if (!having[item.exp1]) having[item.exp1] = []; + having[item.exp1].push({ + value: item.exp2, + joinOperator: item.join, + operator: item.operator, + }); + }); + } + return having; +}; + +export { getHavingObj }; diff --git a/lib/sql_parser/parser_utils/get_join.js b/lib/sql_parser/parser_utils/get_join.js new file mode 100644 index 0000000..4154b29 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_join.js @@ -0,0 +1,13 @@ +import { cutOff, getParamName, parseQueryPartial } from '../utils'; + +const getJoin = query => { + const matchRes = query.match(/\sjoin\s+([\w([\])"'`.,/=<>\s]+$)/im); + if (!matchRes) return null; + // todo reafactor + const from = matchRes[1].replace(cutOff, ''); + const name = getParamName(from); + const path = parseQueryPartial(from, name); + return path[2]; +}; + +export { getJoin }; diff --git a/lib/sql_parser/parser_utils/get_limit.js b/lib/sql_parser/parser_utils/get_limit.js new file mode 100644 index 0000000..6479f03 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_limit.js @@ -0,0 +1,7 @@ +const getLimit = query => { + const limit = query.match(/\slimit\s+(\d+)/i); + if (limit) return +limit[1]; + return null; +}; + +export { getLimit }; diff --git a/lib/sql_parser/parser_utils/get_offset.js b/lib/sql_parser/parser_utils/get_offset.js new file mode 100644 index 0000000..80a7c12 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_offset.js @@ -0,0 +1,7 @@ +const getOffset = query => { + const offset = query.match(/\soffset\s+(\d+)/i); + if (offset) return +offset[1]; + return null; +}; + +export { getOffset }; diff --git a/lib/sql_parser/parser_utils/get_order_by.js b/lib/sql_parser/parser_utils/get_order_by.js new file mode 100644 index 0000000..17b4cc2 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_order_by.js @@ -0,0 +1,22 @@ +import { cutOff } from '../utils'; + +const getOrderBy = (query, select) => { + let orderBy = query.match(/\sorder by\s+([\w([\])"'`.,/=<>\s]+$)/i); + if (orderBy) { + // todo reactor + orderBy = orderBy[1].replace(cutOff, ''); + return select.reduce((previous, field, index) => { + if (~orderBy.indexOf(field.name)) { + const obj = { index }; + const regex = new RegExp(`${field.name}\\s+(asc|desc)(\\s|,|$)`, 'i'); + const match = orderBy.match(regex); + if (match) obj.type = match[1].toLowerCase(); + previous.push(obj); + } + return previous; + }, []); + } + return null; +}; + +export { getOrderBy }; diff --git a/lib/sql_parser/parser_utils/get_pagination.js b/lib/sql_parser/parser_utils/get_pagination.js new file mode 100644 index 0000000..1d9ca58 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_pagination.js @@ -0,0 +1,8 @@ +const getPagination = query => { + return { + limit: query.limit, + page: query.skip ? query.skip / query.limit : 1, + }; +}; + +export { getPagination }; diff --git a/lib/sql_parser/parser_utils/get_select.js b/lib/sql_parser/parser_utils/get_select.js new file mode 100644 index 0000000..8961e55 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_select.js @@ -0,0 +1,29 @@ +const getSelect = query => { + let select = query.match(/^\s*select\s+([\w([\])"'`*:&;.,/\s]+)from\s/i); + if (!select) return { error: 'Wrong syntax: "SELECT FROM " expected' }; + select = select[1]; + const asNames = select.match(/\s+as\s+\w+/ig); + let names = []; + let expressions = []; + if (asNames) { + names = asNames.map(item => item.replace(/(^\s+as\s|\s)+/ig, '')); + expressions = [select.slice(0, select.indexOf(asNames[0]))]; + for (let i = 0; i < asNames.length - 1; i++) { + expressions.push(select.slice(select.indexOf(asNames[i]) + asNames[i].length, + select.indexOf(asNames[i + 1]))); + } + } else { + expressions = select.match(/\w+\.\w+/ig); + if (expressions) { + names = expressions.map(exp => exp.match(/\w+\.(\w+)/i)[1]); + } else { + return { error: 'Wrong fields!' }; + } + } + return names.map((name, i) => ({ + name: names[i], + expression: expressions[i].replace(/(^,|\s)/g, ''), + })); +}; + +export { getSelect }; diff --git a/lib/sql_parser/parser_utils/get_where.js b/lib/sql_parser/parser_utils/get_where.js new file mode 100644 index 0000000..97c1cb1 --- /dev/null +++ b/lib/sql_parser/parser_utils/get_where.js @@ -0,0 +1,44 @@ +import { cutOff } from '../utils'; + +const parseFiltering = str => { + const filters = []; + const andOr = str.match(/\s+(and|or)\s+/gi); + if (andOr) { + let rest = str.slice(); + andOr.forEach(item => { + const part = rest.slice(0, rest.indexOf(item)); + filters.push(part); + rest = rest.slice(rest.indexOf(item) + item.length); + }); + filters.push(rest); + } else { + filters.push(str); + } + return filters.map((filter, i) => { + const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; + const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`.,/\\s]+)`, 'i'); + obj.exp2 = filter.match(regex)[1]; + obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, ''); + obj.operator = obj.operator.replace(/\s/g, ''); + obj.exp2 = obj.exp2.replace(/^\s+|\s+$/g, ''); + if (andOr && i < andOr.length) obj.join = andOr[i].replace(/\s/g, ''); + return obj; + }); +}; + +const getWhere = query => { + let where = query.match(/\swhere\s+([\w([\])"'`.,/=<>\s]+$)/im); + if (where) { + // todo refactor + where = where[1].replace(cutOff, ''); + const parsed = parseFiltering(where); + // todo refactor + parsed.forEach((item) => { + item.exp2 = item.exp2.replace(/^["]/i, '').replace(/["]$/i, ''); + }); + return parsed; + } + return null; +}; + +export { getWhere }; diff --git a/lib/sql_parser/parser_utils/index.js b/lib/sql_parser/parser_utils/index.js new file mode 100644 index 0000000..8be33de --- /dev/null +++ b/lib/sql_parser/parser_utils/index.js @@ -0,0 +1,13 @@ +export * from './get_collection_fields'; +export * from './get_fields'; +export * from './get_from'; +export * from './get_group_by'; +export * from './get_having'; +export * from './get_having_object'; +export * from './get_join'; +export * from './get_limit'; +export * from './get_offset'; +export * from './get_order_by'; +export * from './get_pagination'; +export * from './get_select'; +export * from './get_where'; diff --git a/lib/sql_parser/sql_parser.js b/lib/sql_parser/sql_parser.js new file mode 100644 index 0000000..50bba37 --- /dev/null +++ b/lib/sql_parser/sql_parser.js @@ -0,0 +1,55 @@ +import { + getSelect, + getFrom, + getHaving, + getWhere, + getLimit, + getOffset, + getJoin, + getGroupBy, + getOrderBy, + getHavingObj, + getPagination, + getFields, + getCollectionFields, +} from './parser_utils'; + +const parse = (query) => { + const objectSQL = { + select: getSelect(query), + from: getFrom(query), + having: getHaving(query), + where: getWhere(query), + limit: getLimit(query), + offset: getOffset(query), + join: getJoin(query), + }; + objectSQL.groupBy = getGroupBy(query, objectSQL.select); + objectSQL.orderBy = getOrderBy(query, objectSQL.select); + + return objectSQL; +}; + +const parseToQueryObject = (rawQuery, fields) => { + const query = parse(rawQuery); + const queryObject = {}; + const having = getHavingObj(query); + + queryObject.pagination = getPagination(query); + queryObject.fields = getFields(query, having); + + query.groupBy && query.groupBy.forEach(fieldIndex => { + queryObject.fields[fieldIndex].grouping = true; + }); + query.orderBy && query.orderBy.forEach(field => { + queryObject.fields[field.index].sort = field.type; + }); + + queryObject.collectionFields = getCollectionFields(query, fields); + + queryObject.join = query.join; + + return queryObject; +}; + +export { parseToQueryObject }; diff --git a/lib/sql_parser/utils.js b/lib/sql_parser/utils.js new file mode 100644 index 0000000..e051213 --- /dev/null +++ b/lib/sql_parser/utils.js @@ -0,0 +1,46 @@ +import { _ } from 'meteor/underscore'; + +const cutOff = + /(\sgroup by\s|\sorder by\s|\swhere\s|\shaving\s|\slimit\s|\soffset\s)+[\w([\])"'`.,/=<>\s]+$/i; + +const replaceCuttOff = string => string.replace(cutOff, ''); + +const getParamName = (queryPartial) => queryPartial.match(/\s+as\s+(\w+)/i); + +const parseQueryPartial = (partial, paramName) => + partial.slice(0, paramName.index).match(/(\w+)\/(\w+)/i); + +const isDate = value => { + const dateObj = new Date(value); + return dateObj.toString() !== 'Invalid Date' && + dateObj.toISOString().replace('.000', '') === value; +}; + +const getItemType = value => { + if (_.isNumber(value)) return 'number'; + if (_.isDate(value)) return 'date'; + if (isDate(value)) return 'date'; + if (_.isString(value)) return 'string'; + if (_.isBoolean(value)) return 'boolean'; + if (_.isArray(value)) return 'array'; + if (_.isObject(value)) return 'object'; + return null; +}; + +const getType = (expression, fields) => { + let currentValue = fields; + const path = expression.split('.'); + // todo refactor + path.forEach(pathItem => { + currentValue = currentValue[pathItem]; + }); + return getItemType(currentValue); +}; + +export { + cutOff, + replaceCuttOff, + getParamName, + parseQueryPartial, + getType, +}; From 72d518c9715a7a0e7776c3875672cbc0f3383370 Mon Sep 17 00:00:00 2001 From: BagrijRoman Date: Thu, 18 May 2017 13:37:55 +0300 Subject: [PATCH 10/10] refactoring save commit --- lib/sql_parser/parser_utils/get_having.js | 35 ++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/sql_parser/parser_utils/get_having.js b/lib/sql_parser/parser_utils/get_having.js index 6ebde92..30c1bb1 100644 --- a/lib/sql_parser/parser_utils/get_having.js +++ b/lib/sql_parser/parser_utils/get_having.js @@ -1,20 +1,29 @@ import { replaceCuttOff } from '../utils'; -const parseHavingConditions = (str) => { - const filters = []; - const andOr = str.match(/\s+(and|or)\s+/gi); - if (andOr) { - let rest = str.slice(); - andOr.forEach(item => { - const part = rest.slice(0, rest.indexOf(item)); - filters.push(part); - rest = rest.slice(rest.indexOf(item) + item.length); - }); - filters.push(rest); - } else { - filters.push(str); +const parseFiltersToArray = (filters, andOrConditionsList) => { + let tmpFiltersLine = filters.slice(); + const filtersArray = andOrConditionsList.map(conditionItem => { + const linePart = tmpFiltersLine.slice(0, tmpFiltersLine.indexOf(conditionItem)); + tmpFiltersLine = + tmpFiltersLine.slice(tmpFiltersLine.indexOf(conditionItem) + conditionItem.length); + return linePart; + }); + filtersArray.push(tmpFiltersLine); + return filtersArray; +}; + +const divideFiltersByLogicalConditions = (str, andOrConditions) => { + if (andOrConditions) { + return parseFiltersToArray(str, andOrConditions); } + return [str]; +}; + +const parseHavingConditions = (haveingString) => { + const andOr = haveingString.match(/\s+(and|or)\s+/gi); + const filters = divideFiltersByLogicalConditions(haveingString, andOr); + return filters.map((filter, i) => { const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] }; const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`*:&;%@.,-/\\s]+)`, 'i');