UNPKG

tg-knex-query-resolver

Version:

TeselaGen's Knex based query resolver

209 lines (185 loc) 8.03 kB
const _ = require('lodash'); module.exports = function mapFields(filterQuery, tableMap, currentEntity, opts){ var modelMap = convertTableMapToModelMap(tableMap); return mapFieldNames(_.cloneDeep(filterQuery), modelMap, currentEntity, opts); function getColumnName(modelName, attrName){ try{ var columnName = modelMap[modelName].attributes[attrName].columnName; if(columnName != null) return columnName; }catch(e){ //this should be blank here } if (attrName === 'query') { throw new Error("You probably forgot to call .toJSON() on the qb object. Original error: Unrecognized attribute name: " + attrName + " for model: " + modelName + ''); } else { throw new Error("Unrecognized attribute name: " + attrName + " for model: " + modelName); } } function mapFieldNames(filterQuery, modelMap, currentModelName, opts){ if(filterQuery.type === 'root'){ currentModelName = filterQuery.entity; filterQuery.entity = getTableName(currentModelName); filterQuery.filters = filterQuery.filters.map(function(filter) { return mapFieldNames(filter, modelMap, currentModelName, opts); }); }else if(filterQuery.type === 'group'){ filterQuery.filters = filterQuery.filters.map(function(filter) { return mapFieldNames(filter, modelMap, currentModelName, opts); }); }else if(filterQuery.type === 'subquery'){ filterQuery.foreignKey = getColumnName(currentModelName, filterQuery.foreignKey); currentModelName = filterQuery.entity; filterQuery.entity = getTableName(currentModelName); filterQuery.key = getColumnName(currentModelName, filterQuery.key); filterQuery.filters = filterQuery.filters.map(function(filter) { return mapFieldNames(filter, modelMap, currentModelName, opts); }); }else if(filterQuery.type === 'expression'){ if(filterQuery.field.indexOf('.') > 1){ filterQuery = convertPathArgToSubquery(filterQuery.field, filterQuery, currentModelName) } else { filterQuery.field = getColumnName(currentModelName, filterQuery.field); filterQuery.args.forEach((arg) => { if(arg.__objectType === 'field'){ arg.field = getColumnName(currentModelName, arg.field); } }); } }else if(filterQuery.type === 'where'){ var renamedArgs = {}; var subqueries = []; _.each(filterQuery.args, (value, name) => { if(name.indexOf('.') > 1){ subqueries.push(convertPathArgToSubquery(name, value, currentModelName)); }else{ renamedArgs[getColumnName(currentModelName, name)] = value; } }); filterQuery.args = renamedArgs; if(subqueries.length > 0){ var clonedWhere = clone(filterQuery); var groupFilter = convertWhereToGroupFilter(filterQuery); groupFilter.filters = subqueries.concat(clonedWhere); } }else{ throw new Error("Unable to map fields for unknown query filter type:" + filterQuery.type); } return filterQuery; function getTableName(modelName){ try{ var tableName = modelMap[modelName].tableName; if(tableName != null) return tableName; }catch(e){ //this should be blank here } throw new Error("Unrecognized model name in query: " + modelName); } } function convertPathArgToSubquery(name, value, currentModelName, nested){ let filterQuery if(name.indexOf('.') > 1){ var relAttrDef = getAttributeDefinitionForRelationAttributeName(modelMap, currentModelName, name); var currentEntityName = modelMap[currentModelName].entityName; var relatedModelName = tableMap[relAttrDef.def.referenceName].modelName; filterQuery = { "type": "subquery", "key": "key on related model", "entity": "related model", "foreignKey": "key on current model", "filters": [] }; if(relAttrDef.type === "association"){ filterQuery.foreignKey = tableMap[currentEntityName].primaryKeyField; filterQuery.entity = relatedModelName; filterQuery.key = tableMap[relAttrDef.def.referenceName].attributes[relAttrDef.def.referenceKey].name; }else{ filterQuery.foreignKey = relAttrDef.def.name; filterQuery.entity = relatedModelName; filterQuery.key = tableMap[relAttrDef.def.referenceName].attributes[relAttrDef.def.referenceKey].name; } var newPath = name.split('.'); newPath.shift(); newPath = newPath.join('.'); let valueToUse = value if (value && value.type === 'expression') { valueToUse = _.assign({}, value, {field: newPath}) } var subFilter = convertPathArgToSubquery(newPath, valueToUse, relatedModelName, true); filterQuery.filters = [subFilter]; if (nested) { // if nested don't call mapFieldNames because mapFieldNames will handle all return filterQuery } } else if (value && value.type === 'expression') { value.field = getColumnName(currentModelName, value.field); value.args.forEach((arg) => { if(arg.__objectType === 'field'){ arg.field = getColumnName(currentModelName, arg.field); } }); filterQuery = value } else { filterQuery = { type: "where", args: { [name]: value } }; } return mapFieldNames(filterQuery, modelMap, currentModelName, opts); } } function getAttributeDefinitionForRelationAttributeName(modelMap, modelName, relAttrName){ relAttrName = relAttrName.split('.')[0] var relAttrDef; _.each(modelMap[modelName].attributes, (attrDef) => { if(attrDef.relAttrName === relAttrName) { relAttrDef = attrDef; return false; } }); if(!relAttrDef){ _.each(modelMap[modelName].associations, (assocDef) => { if(assocDef.name === relAttrName) { relAttrDef = assocDef; return false; } }); if (!relAttrDef) { throw new Error('Model ' + modelName + ' has no attribute/relation named: ' + relAttrName +'. This is probably due to a malformed "path" within a datatable schema.') } return { type: "association", def: relAttrDef } } return { type: "relation", def: relAttrDef } } function convertTableMapToModelMap(tableMap){ var modelMap = {}; _.each(tableMap, (model, entityName) => { var attrs = {}; _.each(model.attributes, (attrDef) => { attrs[attrDef.name] = attrDef; }); modelMap[model.modelName] = { tableName: model.tableName, attributes: attrs, associations: model.associations, entityName }; }) return modelMap; } function convertWhereToGroupFilter(filterQuery){ delete filterQuery.args; filterQuery.type = "group"; filterQuery.filters = []; filterQuery.operator = "and"; //wheres should always be ands! return filterQuery; } function clone(obj){ return JSON.parse(JSON.stringify(obj)); }