@@ -18,50 +18,6 @@ function getTable (resourceConfig) {
1818return resourceConfig . table || underscore ( resourceConfig . name )
1919}
2020
21- /**
22- * Lookup and apply table joins to query if field contains a `.`
23- *@param {string } field - Field defined in where filter
24- *@param {object } query - knex query to modify
25- *@param {object } resourceConfig - Resource of primary query/table
26- *@param {string[] } existingJoins - Array of fully qualitifed field names for
27- * any existing table joins for query
28- *@returns {string } - field updated to perspective of applied joins
29- */
30- function applyTableJoins ( field , query , resourceConfig , existingJoins ) {
31- if ( DSUtils . contains ( field , '.' ) ) {
32- let parts = field . split ( '.' )
33- let localResourceConfig = resourceConfig
34-
35- let relationPath = [ ]
36- while ( parts . length >= 2 ) {
37- let relationName = parts . shift ( )
38- let relationResourceConfig = resourceConfig . getResource ( relationName )
39- relationPath . push ( relationName )
40-
41- if ( ! existingJoins . some ( t => t === relationPath . join ( '.' ) ) ) {
42- let [ relation ] = localResourceConfig . relationList . filter ( r => r . relation === relationName )
43- if ( relation ) {
44- let table = getTable ( localResourceConfig )
45- let localId = `${ table } .${ relation . localKey } `
46-
47- let relationTable = getTable ( relationResourceConfig )
48- let foreignId = `${ relationTable } .${ relationResourceConfig . idAttribute } `
49-
50- query . join ( relationTable , localId , foreignId )
51- existingJoins . push ( relationPath . join ( '.' ) )
52- } else {
53- // hopefully a qualified local column
54- }
55- }
56- localResourceConfig = relationResourceConfig
57- }
58-
59- field = `${ getTable ( localResourceConfig ) } .${ parts [ 0 ] } `
60- }
61-
62- return field
63- }
64-
6521function loadWithRelations ( items , resourceConfig , options ) {
6622let tasks = [ ]
6723let instance = Array . isArray ( items ) ?null :items
@@ -291,6 +247,7 @@ class DSSqlAdapter {
291247
292248filterQuery ( resourceConfig , params , options ) {
293249let table = getTable ( resourceConfig )
250+ let joinedTables = [ ]
294251let query
295252
296253if ( params instanceof Object . getPrototypeOf ( this . query . client ) . QueryBuilder ) {
@@ -308,8 +265,6 @@ class DSSqlAdapter {
308265params . orderBy = params . orderBy || params . sort
309266params . skip = params . skip || params . offset
310267
311- let joinedTables = [ ]
312-
313268DSUtils . forEach ( DSUtils . keys ( params ) , k => {
314269let v = params [ k ]
315270if ( ! DSUtils . contains ( reserved , k ) ) {
@@ -331,16 +286,68 @@ class DSSqlAdapter {
331286'==' :criteria
332287}
333288}
334-
335- DSUtils . forOwn ( criteria , ( v , op ) => {
336- // Apply table joins (if needed)
289+
290+ let processRelationField = ( field ) => {
291+ let parts = field . split ( '.' )
292+ let localResourceConfig = resourceConfig
293+ let relationPath = [ ]
294+
295+ while ( parts . length >= 2 ) {
296+ let relationName = parts . shift ( )
297+ let relationResourceConfig = resourceConfig . getResource ( relationName )
298+ relationPath . push ( relationName )
299+
300+ if ( localResourceConfig . relationList ) {
301+ let [ relation ] = localResourceConfig . relationList . filter ( r => r . relation === relationName )
302+ if ( relation ) {
303+ if ( relation . type === 'belongsTo' || relation . type === 'hasOne' ) {
304+ // Apply table join for belongsTo/hasOne property (if not done already)
305+ if ( ! joinedTables . some ( t => t === relationPath . join ( '.' ) ) ) {
306+ let table = getTable ( localResourceConfig )
307+ let localId = `${ table } .${ relation . localKey } `
308+
309+ let relationTable = getTable ( relationResourceConfig )
310+ let foreignId = `${ relationTable } .${ relationResourceConfig . idAttribute } `
311+
312+ query . join ( relationTable , localId , foreignId )
313+ joinedTables . push ( relationPath . join ( '.' ) )
314+ }
315+ } else if ( relation . type === 'hasMany' ) {
316+ // Perform `WHERE EXISTS` subquery for hasMany property
317+ let table = getTable ( localResourceConfig )
318+ let localId = `${ table } .${ localResourceConfig . idAttribute } `
319+
320+ let relationTable = getTable ( relationResourceConfig )
321+ let foreignId = `${ relationTable } .${ relation . foreignKey } `
322+
323+ let existsParams = {
324+ [ foreignId ] :{ '===' :knex . raw ( localId ) } ,
325+ [ parts [ 0 ] ] :criteria
326+ } ;
327+ query . whereExists ( this . filterQuery ( relationResourceConfig , existsParams , options ) ) ;
328+ criteria = null ; // criteria handled by EXISTS subquery
329+ }
330+ } else {
331+ // hopefully a qualified local column
332+ }
333+
334+ localResourceConfig = relationResourceConfig
335+ }
336+ }
337+
338+ return `${ getTable ( localResourceConfig ) } .${ parts [ 0 ] } `
339+ }
340+
341+ if ( DSUtils . contains ( field , '.' ) ) {
337342if ( DSUtils . contains ( field , ',' ) ) {
338343let splitFields = field . split ( ',' ) . map ( c => c . trim ( ) )
339- field = splitFields . map ( splitField => applyTableJoins ( splitField , query , resourceConfig , joinedTables ) ) . join ( ',' )
344+ field = splitFields . map ( splitField => processRelationField ( splitField ) ) . join ( ',' )
340345} else {
341- field = applyTableJoins ( field , query , resourceConfig , joinedTables )
346+ field = processRelationField ( field , query , resourceConfig , joinedTables )
342347}
343-
348+ }
349+
350+ DSUtils . forOwn ( criteria , ( v , op ) => {
344351if ( op === '==' || op === '===' ) {
345352if ( v === null ) {
346353query = query . whereNull ( field )