@@ -20,6 +20,63 @@ function getTable (resourceConfig) {
20
20
return resourceConfig . table || underscore ( resourceConfig . name )
21
21
}
22
22
23
+ function processRelationField ( resourceConfig , query , field , criteria , options , joinedTables ) {
24
+ let fieldParts = field . split ( '.' )
25
+ let localResourceConfig = resourceConfig
26
+ let relationPath = [ ]
27
+ let relationName = null ;
28
+
29
+ while ( fieldParts . length >= 2 ) {
30
+ relationName = fieldParts . shift ( )
31
+ let [ relation ] = localResourceConfig . relationList . filter ( r => r . relation === relationName || r . localField === relationName )
32
+
33
+ if ( relation ) {
34
+ let relationResourceConfig = resourceConfig . getResource ( relation . relation )
35
+ relationPath . push ( relation . relation )
36
+
37
+ if ( relation . type === 'belongsTo' || relation . type === 'hasOne' ) {
38
+ // Apply table join for belongsTo/hasOne property (if not done already)
39
+ if ( ! joinedTables . some ( t => t === relationPath . join ( '.' ) ) ) {
40
+ let table = getTable ( localResourceConfig )
41
+ let localId = `${ table } .${ relation . localKey } `
42
+
43
+ let relationTable = getTable ( relationResourceConfig )
44
+ let foreignId = `${ relationTable } .${ relationResourceConfig . idAttribute } `
45
+
46
+ query . join ( relationTable , localId , foreignId )
47
+ joinedTables . push ( relationPath . join ( '.' ) )
48
+ }
49
+ } else if ( relation . type === 'hasMany' ) {
50
+ // Perform `WHERE EXISTS` subquery for hasMany property
51
+ let existsParams = {
52
+ [ `${ relationName } .${ fieldParts . splice ( 0 ) . join ( '.' ) } ` ] : criteria // remaining field(s) handled by EXISTS subquery
53
+ } ;
54
+ let subQueryTable = getTable ( relationResourceConfig ) ;
55
+ let subQueryOptions = deepMixIn ( { query : knex ( this . defaults ) . select ( `${ subQueryTable } .*` ) . from ( subQueryTable ) } , options )
56
+ let subQuery = this . filterQuery ( relationResourceConfig , existsParams , subQueryOptions )
57
+ . whereRaw ( '??.??=??.??' , [
58
+ getTable ( relationResourceConfig ) ,
59
+ relation . foreignKey ,
60
+ getTable ( localResourceConfig ) ,
61
+ localResourceConfig . idAttribute
62
+ ] )
63
+ if ( Object . keys ( criteria ) . some ( k => k . indexOf ( '|' ) > - 1 ) ) {
64
+ query . orWhereExists ( subQuery ) ;
65
+ } else {
66
+ query . whereExists ( subQuery ) ;
67
+ }
68
+ }
69
+
70
+ localResourceConfig = relationResourceConfig
71
+ } else {
72
+ // hopefully a qualified local column
73
+ }
74
+ }
75
+ relationName = fieldParts . shift ( ) ;
76
+
77
+ return relationName ? `${ getTable ( localResourceConfig ) } .${ relationName } ` : null ;
78
+ }
79
+
23
80
function loadWithRelations ( items , resourceConfig , options ) {
24
81
let tasks = [ ]
25
82
let instance = Array . isArray ( items ) ? null : items
@@ -297,179 +354,127 @@ class DSSqlAdapter {
297
354
}
298
355
}
299
356
300
- let processRelationField = ( field ) => {
301
- let parts = field . split ( '.' )
302
- let localResourceConfig = resourceConfig
303
- let relationPath = [ ]
304
-
305
- while ( parts . length >= 2 ) {
306
- let relationName = parts . shift ( )
307
- let [ relation ] = localResourceConfig . relationList . filter ( r => r . relation === relationName || r . localField === relationName )
308
-
309
- if ( relation ) {
310
- let relationResourceConfig = resourceConfig . getResource ( relation . relation )
311
- relationPath . push ( relation . relation )
312
-
313
- if ( relation . type === 'belongsTo' || relation . type === 'hasOne' ) {
314
- // Apply table join for belongsTo/hasOne property (if not done already)
315
- if ( ! joinedTables . some ( t => t === relationPath . join ( '.' ) ) ) {
316
- let table = getTable ( localResourceConfig )
317
- let localId = `${ table } .${ relation . localKey } `
318
-
319
- let relationTable = getTable ( relationResourceConfig )
320
- let foreignId = `${ relationTable } .${ relationResourceConfig . idAttribute } `
321
-
322
- query . join ( relationTable , localId , foreignId )
323
- joinedTables . push ( relationPath . join ( '.' ) )
324
- }
325
- } else if ( relation . type === 'hasMany' ) {
326
- // Perform `WHERE EXISTS` subquery for hasMany property
327
- let existsParams = {
328
- [ parts [ 0 ] ] : criteria
329
- } ;
330
- let subQuery = this . filterQuery ( relationResourceConfig , existsParams , options )
331
- . whereRaw ( '??.??=??.??' , [
332
- getTable ( relationResourceConfig ) ,
333
- relation . foreignKey ,
334
- getTable ( localResourceConfig ) ,
335
- localResourceConfig . idAttribute
336
- ] )
337
- if ( Object . keys ( criteria ) . some ( k => k . indexOf ( '|' ) > - 1 ) ) {
338
- query . orWhereExists ( subQuery ) ;
339
- } else {
340
- query . whereExists ( subQuery ) ;
341
- }
342
- criteria = null ; // criteria handled by EXISTS subquery
343
- }
344
-
345
- localResourceConfig = relationResourceConfig
346
- } else {
347
- // hopefully a qualified local column
348
- }
349
- }
350
-
351
- return `${ getTable ( localResourceConfig ) } .${ parts [ 0 ] } `
352
- }
353
-
354
357
if ( contains ( field , '.' ) ) {
355
358
if ( contains ( field , ',' ) ) {
356
359
let splitFields = field . split ( ',' ) . map ( c => c . trim ( ) )
357
- field = splitFields . map ( splitField => processRelationField ( splitField ) ) . join ( ',' )
360
+ field = splitFields . map ( splitField => processRelationField . call ( this , resourceConfig , query , splitField , criteria , options , joinedTables ) ) . join ( ',' )
358
361
} else {
359
- field = processRelationField ( field , query , resourceConfig , joinedTables )
362
+ field = processRelationField . call ( this , resourceConfig , query , field , criteria , options , joinedTables )
360
363
}
361
364
}
362
365
363
- forOwn ( criteria , ( v , op ) => {
364
- if ( op in ( this . queryOperators || { } ) ) {
365
- // Custom or overridden operator
366
- query = this . queryOperators [ op ] ( query , field , v )
367
- } else {
368
- // Builtin operators
369
- if ( op === '==' || op === '===' ) {
370
- if ( v === null ) {
371
- query = query . whereNull ( field )
372
- } else {
373
- query = query . where ( field , v )
374
- }
375
- } else if ( op === '!=' || op === '!==' ) {
376
- if ( v === null ) {
377
- query = query . whereNotNull ( field )
378
- } else {
379
- query = query . where ( field , '!=' , v )
380
- }
381
- } else if ( op === '>' ) {
382
- query = query . where ( field , '>' , v )
383
- } else if ( op === '>=' ) {
384
- query = query . where ( field , '>=' , v )
385
- } else if ( op === '<' ) {
386
- query = query . where ( field , '<' , v )
387
- } else if ( op === '<=' ) {
388
- query = query . where ( field , '<=' , v )
389
- // } else if (op === 'isectEmpty') {
390
- // subQuery = subQuery ? subQuery.and(row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)
391
- // } else if (op === 'isectNotEmpty') {
392
- // subQuery = subQuery ? subQuery.and(row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)
393
- } else if ( op === 'in' ) {
394
- query = query . where ( field , 'in' , v )
395
- } else if ( op === 'notIn' ) {
396
- query = query . whereNotIn ( field , v )
397
- } else if ( op === 'near' ) {
398
- const milesRegex = / ( \d + ( \. \d + ) ? ) \s * ( m | M ) i l e s $ /
399
- const kilometersRegex = / ( \d + ( \. \d + ) ? ) \s * ( k | K ) $ /
400
-
401
- let radius
402
- let unitsPerDegree
403
- if ( typeof v . radius === 'number' || milesRegex . test ( v . radius ) ) {
404
- radius = typeof v . radius === 'number' ? v . radius : v . radius . match ( milesRegex ) [ 1 ]
405
- unitsPerDegree = 69.0 // miles per degree
406
- } else if ( kilometersRegex . test ( v . radius ) ) {
407
- radius = v . radius . match ( kilometersRegex ) [ 1 ]
408
- unitsPerDegree = 111.045 // kilometers per degree;
409
- } else {
410
- throw new Error ( 'Unknown radius distance units' )
411
- }
366
+ if ( field ) {
367
+ forOwn ( criteria , ( v , op ) => {
368
+ if ( op in ( this . queryOperators || { } ) ) {
369
+ // Custom or overridden operator
370
+ query = this . queryOperators [ op ] ( query , field , v )
371
+ } else {
372
+ // Builtin operators
373
+ if ( op === '==' || op === '===' ) {
374
+ if ( v === null ) {
375
+ query = query . whereNull ( field )
376
+ } else {
377
+ query = query . where ( field , v )
378
+ }
379
+ } else if ( op === '!=' || op === '!==' ) {
380
+ if ( v === null ) {
381
+ query = query . whereNotNull ( field )
382
+ } else {
383
+ query = query . where ( field , '!=' , v )
384
+ }
385
+ } else if ( op === '>' ) {
386
+ query = query . where ( field , '>' , v )
387
+ } else if ( op === '>=' ) {
388
+ query = query . where ( field , '>=' , v )
389
+ } else if ( op === '<' ) {
390
+ query = query . where ( field , '<' , v )
391
+ } else if ( op === '<=' ) {
392
+ query = query . where ( field , '<=' , v )
393
+ // } else if (op === 'isectEmpty') {
394
+ // subQuery = subQuery ? subQuery.and(row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)
395
+ // } else if (op === 'isectNotEmpty') {
396
+ // subQuery = subQuery ? subQuery.and(row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)
397
+ } else if ( op === 'in' ) {
398
+ query = query . where ( field , 'in' , v )
399
+ } else if ( op === 'notIn' ) {
400
+ query = query . whereNotIn ( field , v )
401
+ } else if ( op === 'near' ) {
402
+ const milesRegex = / ( \d + ( \. \d + ) ? ) \s * ( m | M ) i l e s $ /
403
+ const kilometersRegex = / ( \d + ( \. \d + ) ? ) \s * ( k | K ) $ /
404
+
405
+ let radius
406
+ let unitsPerDegree
407
+ if ( typeof v . radius === 'number' || milesRegex . test ( v . radius ) ) {
408
+ radius = typeof v . radius === 'number' ? v . radius : v . radius . match ( milesRegex ) [ 1 ]
409
+ unitsPerDegree = 69.0 // miles per degree
410
+ } else if ( kilometersRegex . test ( v . radius ) ) {
411
+ radius = v . radius . match ( kilometersRegex ) [ 1 ]
412
+ unitsPerDegree = 111.045 // kilometers per degree;
413
+ } else {
414
+ throw new Error ( 'Unknown radius distance units' )
415
+ }
412
416
413
- let [ latitudeColumn , longitudeColumn ] = field . split ( ',' ) . map ( c => c . trim ( ) )
414
- let [ latitude , longitude ] = v . center
415
-
416
- // Uses indexes on `latitudeColumn` / `longitudeColumn` if available
417
- query = query
418
- . whereBetween ( latitudeColumn , [
419
- latitude - ( radius / unitsPerDegree ) ,
420
- latitude + ( radius / unitsPerDegree )
421
- ] )
422
- . whereBetween ( longitudeColumn , [
423
- longitude - ( radius / ( unitsPerDegree * Math . cos ( latitude * ( Math . PI / 180 ) ) ) ) ,
424
- longitude + ( radius / ( unitsPerDegree * Math . cos ( latitude * ( Math . PI / 180 ) ) ) )
425
- ] )
426
-
427
- if ( v . calculateDistance ) {
428
- let distanceColumn = ( typeof v . calculateDistance === 'string' ) ? v . calculateDistance : 'distance'
429
- query = query . select ( knex . raw ( `
430
- ${ unitsPerDegree } * DEGREES(ACOS(
431
- COS(RADIANS(?)) * COS(RADIANS(${ latitudeColumn } )) *
432
- COS(RADIANS(${ longitudeColumn } ) - RADIANS(?)) +
433
- SIN(RADIANS(?)) * SIN(RADIANS(${ latitudeColumn } ))
434
- )) AS ${ distanceColumn } ` , [ latitude , longitude , latitude ] ) )
435
- }
436
- } else if ( op === 'like' ) {
437
- query = query . where ( field , 'like' , v )
438
- } else if ( op === '|like' ) {
439
- query = query . orWhere ( field , 'like' , v )
440
- } else if ( op === '|==' || op === '|===' ) {
441
- if ( v === null ) {
442
- query = query . orWhereNull ( field )
443
- } else {
444
- query = query . orWhere ( field , v )
445
- }
446
- } else if ( op === '|!=' || op === '|!==' ) {
447
- if ( v === null ) {
448
- query = query . orWhereNotNull ( field )
417
+ let [ latitudeColumn , longitudeColumn ] = field . split ( ',' ) . map ( c => c . trim ( ) )
418
+ let [ latitude , longitude ] = v . center
419
+
420
+ // Uses indexes on `latitudeColumn` / `longitudeColumn` if available
421
+ query = query
422
+ . whereBetween ( latitudeColumn , [
423
+ latitude - ( radius / unitsPerDegree ) ,
424
+ latitude + ( radius / unitsPerDegree )
425
+ ] )
426
+ . whereBetween ( longitudeColumn , [
427
+ longitude - ( radius / ( unitsPerDegree * Math . cos ( latitude * ( Math . PI / 180 ) ) ) ) ,
428
+ longitude + ( radius / ( unitsPerDegree * Math . cos ( latitude * ( Math . PI / 180 ) ) ) )
429
+ ] )
430
+
431
+ if ( v . calculateDistance ) {
432
+ let distanceColumn = ( typeof v . calculateDistance === 'string' ) ? v . calculateDistance : 'distance'
433
+ query = query . select ( knex . raw ( `
434
+ ${ unitsPerDegree } * DEGREES(ACOS(
435
+ COS(RADIANS(?)) * COS(RADIANS(${ latitudeColumn } )) *
436
+ COS(RADIANS(${ longitudeColumn } ) - RADIANS(?)) +
437
+ SIN(RADIANS(?)) * SIN(RADIANS(${ latitudeColumn } ))
438
+ )) AS ${ distanceColumn } ` , [ latitude , longitude , latitude ] ) )
439
+ }
440
+ } else if ( op === 'like' ) {
441
+ query = query . where ( field , 'like' , v )
442
+ } else if ( op === '|like' ) {
443
+ query = query . orWhere ( field , 'like' , v )
444
+ } else if ( op === '|==' || op === '|===' ) {
445
+ if ( v === null ) {
446
+ query = query . orWhereNull ( field )
447
+ } else {
448
+ query = query . orWhere ( field , v )
449
+ }
450
+ } else if ( op === '|!=' || op === '|!==' ) {
451
+ if ( v === null ) {
452
+ query = query . orWhereNotNull ( field )
453
+ } else {
454
+ query = query . orWhere ( field , '!=' , v )
455
+ }
456
+ } else if ( op === '|>' ) {
457
+ query = query . orWhere ( field , '>' , v )
458
+ } else if ( op === '|>=' ) {
459
+ query = query . orWhere ( field , '>=' , v )
460
+ } else if ( op === '|<' ) {
461
+ query = query . orWhere ( field , '<' , v )
462
+ } else if ( op === '|<=' ) {
463
+ query = query . orWhere ( field , '<=' , v )
464
+ // } else if (op === '|isectEmpty') {
465
+ // subQuery = subQuery ? subQuery.or(row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)
466
+ // } else if (op === '|isectNotEmpty') {
467
+ // subQuery = subQuery ? subQuery.or(row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)
468
+ } else if ( op === '|in' ) {
469
+ query = query . orWhere ( field , 'in' , v )
470
+ } else if ( op === '|notIn' ) {
471
+ query = query . orWhereNotIn ( field , v )
449
472
} else {
450
- query = query . orWhere ( field , '!=' , v )
473
+ throw new Error ( 'Operator not found' )
451
474
}
452
- } else if ( op === '|>' ) {
453
- query = query . orWhere ( field , '>' , v )
454
- } else if ( op === '|>=' ) {
455
- query = query . orWhere ( field , '>=' , v )
456
- } else if ( op === '|<' ) {
457
- query = query . orWhere ( field , '<' , v )
458
- } else if ( op === '|<=' ) {
459
- query = query . orWhere ( field , '<=' , v )
460
- // } else if (op === '|isectEmpty') {
461
- // subQuery = subQuery ? subQuery.or(row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().eq(0)
462
- // } else if (op === '|isectNotEmpty') {
463
- // subQuery = subQuery ? subQuery.or(row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)) : row(field).default([]).setIntersection(r.expr(v).default([])).count().ne(0)
464
- } else if ( op === '|in' ) {
465
- query = query . orWhere ( field , 'in' , v )
466
- } else if ( op === '|notIn' ) {
467
- query = query . orWhereNotIn ( field , v )
468
- } else {
469
- throw new Error ( 'Operator not found' )
470
475
}
471
- }
472
- } )
476
+ } )
477
+ }
473
478
} )
474
479
}
475
480
0 commit comments