From c176de329787b005f90661a6cc8a758de108444a Mon Sep 17 00:00:00 2001 From: Mason Payne Date: Fri, 22 Jan 2021 23:18:15 -0700 Subject: [PATCH] add support for searching queries --- src/example-def.ts | 70 +++++++++++++++++++++++++++++++++++++ src/systemGenService.ts | 2 +- src/views/mapper-creator.ts | 53 ++++++++++++++-------------- 3 files changed, 98 insertions(+), 27 deletions(-) diff --git a/src/example-def.ts b/src/example-def.ts index 9110f6f..de29013 100644 --- a/src/example-def.ts +++ b/src/example-def.ts @@ -142,6 +142,76 @@ let def: SystemDef = { }, ] }, + { + component: 'task', + table: 'task', + type: 'search', + columns: [ + { + name: 'name', + table: 'task' + }, + { + name: 'description', + table: 'task' + }, + { + name: 'completed', + table: 'task' + }, + { + name: 'completed_date', + table: 'task' + }, + ], + orderBy: [ + { + column: { + name: 'modified', + table: 'task' + }, + direction: 'desc' + } + ], + filters: [ + { + param: 'list', + column: { + name: 'list_id', + table: 'task' + }, + comparison: '=', + required: true + }, + { + param: 'completed', + column: { + name: 'completed', + table: 'task' + }, + comparison: '=', + required: false + }, + { + param: 'name', + column: { + name: 'name', + table: 'task' + }, + comparison: 'LIKE', + required: false + }, + { + param: 'description', + column: { + name: 'description', + table: 'task' + }, + comparison: 'LIKE', + required: false + }, + ] + }, { component: 'task', table: 'task', diff --git a/src/systemGenService.ts b/src/systemGenService.ts index 451d6c6..7b6e78f 100644 --- a/src/systemGenService.ts +++ b/src/systemGenService.ts @@ -67,7 +67,7 @@ interface Order { interface Filter { param: string; // the query param used to get the value column: ColumnRef; - comparison: '=' | '!=' | '>' | '<' | 'contains'; + comparison: '=' | '!=' | '>' | '<' | 'contains' | 'LIKE'; required?: boolean; } diff --git a/src/views/mapper-creator.ts b/src/views/mapper-creator.ts index 7c7123a..dfb656e 100644 --- a/src/views/mapper-creator.ts +++ b/src/views/mapper-creator.ts @@ -6,6 +6,8 @@ function createMapperFunc(view: EndpointDef): string { let func: string = ''; if (view.type === 'list' || view.type === 'count') { func = buildGetList(view); + } else if (view.type === 'search') { + func = buildGetList(view, true); } else if (view.type === 'item') { func = buildGetItem(view); } else if (view.type === 'update') { @@ -22,10 +24,7 @@ function buildDeleteItem(view: EndpointDef): string { let func: string = ''; let funcName = buildFunctionName(view); let tables: string = `${view.table}`; - let filtersObj = buildFilters(view); - let filters: string = filtersObj.filters; - let requiredFilterValues: string = filtersObj.requiredFilterValues; - let optionalFilterParts = filtersObj.optionalFiltersCode; + let {filters, requiredQueryValues, optionalFilterParts} = constructFilters(view); let query: string = `DELETE FROM ${tables}${filters}`; let funcParams = 'params: {[key: string]: string}'; @@ -33,7 +32,7 @@ function buildDeleteItem(view: EndpointDef): string { public ${funcName}(${funcParams}) { let query: string = '${query}'; - let queryValues: string[] = [${requiredFilterValues}]; + let queryValues: string[] = [${requiredQueryValues}]; // optional params? ${optionalFilterParts} @@ -49,10 +48,7 @@ function buildGetItem(view: EndpointDef): string { let funcName = buildFunctionName(view); let columns: string = `${view.component}.${view.component}_id, ${view.columns.map(c => `${c.table}.${c.name}`).join(', ')}`; let tables: string = view.join?.length ? buildJoin(view) : `${view.table}`; - let filtersObj = buildFilters(view); - let filters: string = filtersObj.filters; - let requiredFilterValues: string = filtersObj.requiredFilterValues; - let optionalFilterParts = filtersObj.optionalFiltersCode; + let {filters, requiredQueryValues, optionalFilterParts} = constructFilters(view); let query: string = `SELECT ${columns} FROM ${tables}${filters} LIMIT 1`; let funcParams = 'params: {[key: string]: string}'; @@ -60,7 +56,7 @@ function buildGetItem(view: EndpointDef): string { public ${funcName}(${funcParams}) { let query: string = '${query}'; - let queryValues: string[] = [${requiredFilterValues}]; + let queryValues: string[] = [${requiredQueryValues}]; // optional params? ${optionalFilterParts} @@ -128,17 +124,8 @@ function getCreateOrUpdateFunc(query: string, view: EndpointDef): string { return func; } -function buildGetList(view: EndpointDef): string { - // TODO: optionally allow a raw query definition with predefined params - // This would be so much simpler and easy if it were just the queries wanted - let func: string = ''; - let funcName: string = ''; - let columns: string = view.type === 'count' ? `count(*)` : `${view.component}.${view.component}_id, ${view.columns.map(c => `${c.table}.${c.name}`).join(', ')}`; - let query: string = ''; - let tables: string = view.join?.length ? buildJoin(view) : `${view.table}`; +function constructFilters(view: EndpointDef, isSearch=false) { let filters: string = ''; - funcName = buildFunctionName(view); - let requiredFilterStrings = []; let requiredQueryValues = ''; let optionalFilterParts = ''; @@ -160,13 +147,27 @@ function buildGetList(view: EndpointDef): string { */ optionalFilterParts = optionalFilterParts + ` if (params.${f.param}) { - query = query + ' AND ${f.column.table}.${f.column.name} ${f.comparison} ?'; - queryValues.push(params.${f.param}); + query = query + ' ${isSearch ? 'OR' : 'AND'} ${f.column.table}.${f.column.name} ${isSearch ? 'LIKE' : f.comparison} ?'; + queryValues.push(${isSearch ? '\'%\' + params.' + f.param + ' + \'%\'' : 'params.' + f.param}); }`; } } filters = ` WHERE ${requiredFilterStrings.join(' AND ')}`; } + return {filters, requiredQueryValues, optionalFilterParts}; +} + +function buildGetList(view: EndpointDef, isSearch=false): string { + // TODO: optionally allow a raw query definition with predefined params + // This would be so much simpler and easy if it were just the queries wanted + let func: string = ''; + let funcName: string = ''; + let columns: string = view.type === 'count' ? `count(*)` : `${view.component}.${view.component}_id, ${view.columns.map(c => `${c.table}.${c.name}`).join(', ')}`; + let query: string = ''; + let tables: string = view.join?.length ? buildJoin(view) : `${view.table}`; + funcName = buildFunctionName(view); + + let {filters, requiredQueryValues, optionalFilterParts} = constructFilters(view, isSearch); query = `SELECT ${columns} FROM ${tables}${filters}`; let orderByCode = ''; @@ -200,7 +201,7 @@ function buildGetList(view: EndpointDef): string { return func; } -function buildFilters(view: EndpointDef): {filters: string, requiredFilterValues: string, optionalFiltersCode: string} { +function buildSearches(view: EndpointDef): {filters: string, requiredFilterValues: string, optionalFiltersCode: string} { let filtersObj: {filters: string, requiredFilterValues: string, optionalFiltersCode: string} = { filters: '', requiredFilterValues: '', @@ -229,8 +230,8 @@ function buildFilters(view: EndpointDef): {filters: string, requiredFilterValues */ optionalFilterParts = optionalFilterParts + ` if (params.${f.param}) { - query = query + ' AND ${f.column.table}.${f.column.name} ${f.comparison} ?'; - queryValues.push(params.${f.param}); + query = query + ' OR ${f.column.table}.${f.column.name} ${f.comparison} ?'; + queryValues.push('%' + params.${f.param} + '%'); }`; } } @@ -278,7 +279,7 @@ function buildFunctionName(view: EndpointDef): string { 'update': (view: EndpointDef) => `update${uppercaseFirstLetter(view.component)}`, 'create': (view: EndpointDef) => `create${uppercaseFirstLetter(view.component)}`, 'delete': (view: EndpointDef) => `delete${uppercaseFirstLetter(view.component)}`, - 'search': (view: EndpointDef) => `search${uppercaseFirstLetter(view.component)}`, + 'search': (view: EndpointDef) => `search${pluralize(uppercaseFirstLetter(view.component))}`, }; return selector[view.type](view); }