diff --git a/lib/helpers.js b/lib/helpers.js index 6807b59..24a7a4c 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -21,7 +21,8 @@ const postgresTypeMap = new WeakMap([ module.exports = { primaryKeyTypes, postgresTypeMap, inputValue, - inputRecord, outputRecord, getCode + inputRecord, outputRecord, getCode, constructFilterRelationQuery, + constructTypesPathToChild, isRelationFilter, getRelationFilterSegments } @@ -117,3 +118,75 @@ function outputBuffer (value) { if (Buffer.isBuffer(value)) return value return buffer(value.slice(2), 'hex') } + + +function constructFilterRelationQuery ( tableSetupConfiguration, + typeMap, + recordTypes, + typesPathToParent, + pathSegmentsToParent, + whereOperation, + value, + query = '' ) { + if ( !typesPathToParent.length ) + return query + + + const primaryKey = tableSetupConfiguration.keys.primary + const pathSegment = pathSegmentsToParent[0] + const currentType = typesPathToParent[0] + if (!query) + query = ` + SELECT ${primaryKey} FROM "${typeMap[currentType] || currentType}" + WHERE ${whereOperation(pathSegment, value)} \n + ` + + + else { + const recordType = recordTypes[currentType] + const isArray = recordType[pathSegment].isArray + query = `SELECT ${primaryKey} + FROM "${typeMap[currentType] || currentType}" + WHERE "${pathSegment}" + ${isArray ? '&& ARRAY' : 'IN'} ( ${query} ) \n + ` + } + + return constructFilterRelationQuery( tableSetupConfiguration, + typeMap, + recordTypes, + typesPathToParent.slice(1), + pathSegmentsToParent.slice(1), + whereOperation, + value, + query) +} + + +function constructTypesPathToChild ( recordTypes, parent, + remainingPathSegments, typesPath ) { + if ( !remainingPathSegments.length ) + return typesPath + + + const segment = remainingPathSegments[0] + const nextType = parent[segment].link + + //complex type + if ( nextType ) { + typesPath.push( nextType ) + parent = recordTypes[nextType] + } + return constructTypesPathToChild(recordTypes, parent, + remainingPathSegments.slice(1), typesPath ) +} + + +function isRelationFilter ( field ) { + return field.split(':').length > 1 +} + + +function getRelationFilterSegments ( field ) { + return field.split(':') +} diff --git a/lib/index.js b/lib/index.js index 6388ebd..39324d9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -311,11 +311,51 @@ module.exports = Adapter => class PostgreSQLAdapter extends Adapter { } for (const field in options.fuzzyMatch) { - let value = options.fuzzyMatch[field] - //We assume the request has been validaded on another level. (so only strings, no arrays, etc...) - index++ - parameters.push(inputValue(value)) - where.push(`"${field}" ~* $${index}`) + const value = options.fuzzyMatch[field] + //simple filter + if ( ! helpers.isRelationFilter(field) ) { + // We assume the request has been validaded on another level. + // (so only strings, no arrays, etc...) + index++ + parameters.push(inputValue(value)) + where.push(`"${field}" ~* $${index}`) + } + //relationship filter + else { + // eslint-disable-next-line no-loop-func + const whereOperation = (field, value) => { + index++ + parameters.push(inputValue(value)) + return `"${field}" ~* $${index}` + } + const relationFilterSegments = helpers.getRelationFilterSegments(field) + + const typesPath = helpers + .constructTypesPathToChild( recordTypes, + recordTypes[type], + relationFilterSegments, + [] ) + + const segmentsInversed = relationFilterSegments.slice().reverse() + const query = helpers + .constructFilterRelationQuery( { keys: this.keys }, + typeMap, + recordTypes, + typesPath.slice().reverse(), + segmentsInversed, + whereOperation, + value, + '' + ) + + const startField = relationFilterSegments[0] + const isArray = fields[startField][isArrayKey] + if (isArray) + where.push(`"${startField}" && ARRAY(${query})`) + + else + where.push(`"${startField}" in (${query})`) + } } where = where.length ? `where ${where.join(' and ')}` : ''