Skip to content

Commit 3a16f86

Browse files
authored
Feature/wip filtering relationship (#35)
* Attempt to allow fuzzy-match filter in fortune-postgres Notes ----- I am unsure what field of options may be used. The options.query already seemed to be used, so started looking for another field. As I understand it, Adapter.find leaves the possibility of extra fields on the options object unspecified. Therefore, I added the fuzzyMatch field. * WIP: fuzzy-matching on relationship As a try out, I extend the fuzzy matching on relationship - Abstracted away relation filter parsing - Change filter[author.posts] to filter[author:posts]. Ember (considered baseline use case) doesn't like parsing the former format in its queryParams parser. * Make sure actual table setup is used to construct queries See PR discussion remark #35 (comment) * Concised version of if/else branch see: #35 (comment) * automated linter fixes * fix linting
1 parent eff835e commit 3a16f86

File tree

2 files changed

+119
-6
lines changed

2 files changed

+119
-6
lines changed

lib/helpers.js

+74-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const postgresTypeMap = new WeakMap([
2121

2222
module.exports = {
2323
primaryKeyTypes, postgresTypeMap, inputValue,
24-
inputRecord, outputRecord, getCode
24+
inputRecord, outputRecord, getCode, constructFilterRelationQuery,
25+
constructTypesPathToChild, isRelationFilter, getRelationFilterSegments
2526
}
2627

2728

@@ -117,3 +118,75 @@ function outputBuffer (value) {
117118
if (Buffer.isBuffer(value)) return value
118119
return buffer(value.slice(2), 'hex')
119120
}
121+
122+
123+
function constructFilterRelationQuery ( tableSetupConfiguration,
124+
typeMap,
125+
recordTypes,
126+
typesPathToParent,
127+
pathSegmentsToParent,
128+
whereOperation,
129+
value,
130+
query = '' ) {
131+
if ( !typesPathToParent.length )
132+
return query
133+
134+
135+
const primaryKey = tableSetupConfiguration.keys.primary
136+
const pathSegment = pathSegmentsToParent[0]
137+
const currentType = typesPathToParent[0]
138+
if (!query)
139+
query = `
140+
SELECT ${primaryKey} FROM "${typeMap[currentType] || currentType}"
141+
WHERE ${whereOperation(pathSegment, value)} \n
142+
`
143+
144+
145+
else {
146+
const recordType = recordTypes[currentType]
147+
const isArray = recordType[pathSegment].isArray
148+
query = `SELECT ${primaryKey}
149+
FROM "${typeMap[currentType] || currentType}"
150+
WHERE "${pathSegment}"
151+
${isArray ? '&& ARRAY' : 'IN'} ( ${query} ) \n
152+
`
153+
}
154+
155+
return constructFilterRelationQuery( tableSetupConfiguration,
156+
typeMap,
157+
recordTypes,
158+
typesPathToParent.slice(1),
159+
pathSegmentsToParent.slice(1),
160+
whereOperation,
161+
value,
162+
query)
163+
}
164+
165+
166+
function constructTypesPathToChild ( recordTypes, parent,
167+
remainingPathSegments, typesPath ) {
168+
if ( !remainingPathSegments.length )
169+
return typesPath
170+
171+
172+
const segment = remainingPathSegments[0]
173+
const nextType = parent[segment].link
174+
175+
//complex type
176+
if ( nextType ) {
177+
typesPath.push( nextType )
178+
parent = recordTypes[nextType]
179+
}
180+
return constructTypesPathToChild(recordTypes, parent,
181+
remainingPathSegments.slice(1), typesPath )
182+
}
183+
184+
185+
function isRelationFilter ( field ) {
186+
return field.split(':').length > 1
187+
}
188+
189+
190+
function getRelationFilterSegments ( field ) {
191+
return field.split(':')
192+
}

lib/index.js

+45-5
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,51 @@ module.exports = Adapter => class PostgreSQLAdapter extends Adapter {
311311
}
312312

313313
for (const field in options.fuzzyMatch) {
314-
let value = options.fuzzyMatch[field]
315-
//We assume the request has been validaded on another level. (so only strings, no arrays, etc...)
316-
index++
317-
parameters.push(inputValue(value))
318-
where.push(`"${field}" ~* $${index}`)
314+
const value = options.fuzzyMatch[field]
315+
//simple filter
316+
if ( ! helpers.isRelationFilter(field) ) {
317+
// We assume the request has been validaded on another level.
318+
// (so only strings, no arrays, etc...)
319+
index++
320+
parameters.push(inputValue(value))
321+
where.push(`"${field}" ~* $${index}`)
322+
}
323+
//relationship filter
324+
else {
325+
// eslint-disable-next-line no-loop-func
326+
const whereOperation = (field, value) => {
327+
index++
328+
parameters.push(inputValue(value))
329+
return `"${field}" ~* $${index}`
330+
}
331+
const relationFilterSegments = helpers.getRelationFilterSegments(field)
332+
333+
const typesPath = helpers
334+
.constructTypesPathToChild( recordTypes,
335+
recordTypes[type],
336+
relationFilterSegments,
337+
[] )
338+
339+
const segmentsInversed = relationFilterSegments.slice().reverse()
340+
const query = helpers
341+
.constructFilterRelationQuery( { keys: this.keys },
342+
typeMap,
343+
recordTypes,
344+
typesPath.slice().reverse(),
345+
segmentsInversed,
346+
whereOperation,
347+
value,
348+
''
349+
)
350+
351+
const startField = relationFilterSegments[0]
352+
const isArray = fields[startField][isArrayKey]
353+
if (isArray)
354+
where.push(`"${startField}" && ARRAY(${query})`)
355+
356+
else
357+
where.push(`"${startField}" in (${query})`)
358+
}
319359
}
320360

321361
where = where.length ? `where ${where.join(' and ')}` : ''

0 commit comments

Comments
 (0)