@@ -165,6 +165,10 @@ const transformDotField = (fieldName) => {
165
165
return name ;
166
166
}
167
167
168
+ const transformAggregateField = ( fieldName ) => {
169
+ return fieldName . substr ( 1 ) ;
170
+ }
171
+
168
172
const validateKeys = ( object ) => {
169
173
if ( typeof object == 'object' ) {
170
174
for ( const key in object ) {
@@ -1366,6 +1370,140 @@ export class PostgresStorageAdapter {
1366
1370
} ) ;
1367
1371
}
1368
1372
1373
+ distinct ( className , schema , query , fieldName ) {
1374
+ debug ( 'distinct' , className , query ) ;
1375
+ let field = fieldName ;
1376
+ let column = fieldName ;
1377
+ if ( fieldName . indexOf ( '.' ) >= 0 ) {
1378
+ field = transformDotFieldToComponents ( fieldName ) . join ( '->' ) ;
1379
+ column = fieldName . split ( '.' ) [ 0 ] ;
1380
+ }
1381
+ const isArrayField = schema . fields
1382
+ && schema . fields [ fieldName ]
1383
+ && schema . fields [ fieldName ] . type === 'Array' ;
1384
+ const values = [ field , column , className ] ;
1385
+ const where = buildWhereClause ( { schema, query, index : 4 } ) ;
1386
+ values . push ( ...where . values ) ;
1387
+
1388
+ const wherePattern = where . pattern . length > 0 ? `WHERE ${ where . pattern } ` : '' ;
1389
+ let qs = `SELECT DISTINCT ON ($1:raw) $2:raw FROM $3:name ${ wherePattern } ` ;
1390
+ if ( isArrayField ) {
1391
+ qs = `SELECT distinct jsonb_array_elements($1:raw) as $2:raw FROM $3:name ${ wherePattern } ` ;
1392
+ }
1393
+ debug ( qs , values ) ;
1394
+ return this . _client . any ( qs , values )
1395
+ . catch ( ( ) => [ ] )
1396
+ . then ( ( results ) => {
1397
+ if ( fieldName . indexOf ( '.' ) === - 1 ) {
1398
+ return results . map ( object => object [ field ] ) ;
1399
+ }
1400
+ const child = fieldName . split ( '.' ) [ 1 ] ;
1401
+ return results . map ( object => object [ column ] [ child ] ) ;
1402
+ } ) ;
1403
+ }
1404
+
1405
+ aggregate ( className , pipeline ) {
1406
+ debug ( 'aggregate' , className , pipeline ) ;
1407
+ const values = [ className ] ;
1408
+ let columns = [ ] ;
1409
+ let countField = null ;
1410
+ let wherePattern = '' ;
1411
+ let limitPattern = '' ;
1412
+ let skipPattern = '' ;
1413
+ let sortPattern = '' ;
1414
+ let groupPattern = '' ;
1415
+ for ( let i = 0 ; i < pipeline . length ; i += 1 ) {
1416
+ const stage = pipeline [ i ] ;
1417
+ if ( stage . $group ) {
1418
+ for ( const field in stage . $group ) {
1419
+ const value = stage . $group [ field ] ;
1420
+ if ( value === null || value === undefined ) {
1421
+ continue ;
1422
+ }
1423
+ if ( field === '_id' ) {
1424
+ columns . push ( `${ transformAggregateField ( value ) } AS "objectId"` ) ;
1425
+ groupPattern = `GROUP BY ${ transformAggregateField ( value ) } ` ;
1426
+ continue ;
1427
+ }
1428
+ if ( value . $sum ) {
1429
+ if ( typeof value . $sum === 'string' ) {
1430
+ columns . push ( `SUM(${ transformAggregateField ( value . $sum ) } ) AS "${ field } "` ) ;
1431
+ } else {
1432
+ countField = field ;
1433
+ columns . push ( `COUNT(*) AS "${ field } "` ) ;
1434
+ }
1435
+ }
1436
+ if ( value . $max ) {
1437
+ columns . push ( `MAX(${ transformAggregateField ( value . $max ) } ) AS "${ field } "` ) ;
1438
+ }
1439
+ if ( value . $min ) {
1440
+ columns . push ( `MIN(${ transformAggregateField ( value . $min ) } ) AS "${ field } "` ) ;
1441
+ }
1442
+ if ( value . $avg ) {
1443
+ columns . push ( `AVG(${ transformAggregateField ( value . $avg ) } ) AS "${ field } "` ) ;
1444
+ }
1445
+ }
1446
+ columns . join ( ',' ) ;
1447
+ } else {
1448
+ columns . push ( '*' ) ;
1449
+ }
1450
+ if ( stage . $project ) {
1451
+ if ( columns . includes ( '*' ) ) {
1452
+ columns = [ ] ;
1453
+ }
1454
+ for ( const field in stage . $project ) {
1455
+ const value = stage . $project [ field ] ;
1456
+ if ( ( value === 1 || value === true ) ) {
1457
+ columns . push ( field ) ;
1458
+ }
1459
+ }
1460
+ }
1461
+ if ( stage . $match ) {
1462
+ const patterns = [ ] ;
1463
+ for ( const field in stage . $match ) {
1464
+ const value = stage . $match [ field ] ;
1465
+ Object . keys ( ParseToPosgresComparator ) . forEach ( cmp => {
1466
+ if ( value [ cmp ] ) {
1467
+ const pgComparator = ParseToPosgresComparator [ cmp ] ;
1468
+ patterns . push ( `${ field } ${ pgComparator } ${ value [ cmp ] } ` ) ;
1469
+ }
1470
+ } ) ;
1471
+ }
1472
+ wherePattern = patterns . length > 0 ? `WHERE ${ patterns . join ( ' ' ) } ` : '' ;
1473
+ }
1474
+ if ( stage . $limit ) {
1475
+ limitPattern = `LIMIT ${ stage . $limit } ` ;
1476
+ }
1477
+ if ( stage . $skip ) {
1478
+ skipPattern = `OFFSET ${ stage . $skip } ` ;
1479
+ }
1480
+ if ( stage . $sort ) {
1481
+ const sort = stage . $sort ;
1482
+ const sorting = Object . keys ( sort ) . map ( ( key ) => {
1483
+ if ( sort [ key ] === 1 ) {
1484
+ return `"${ key } " ASC` ;
1485
+ }
1486
+ return `"${ key } " DESC` ;
1487
+ } ) . join ( ',' ) ;
1488
+ sortPattern = sort !== undefined && Object . keys ( sort ) . length > 0 ? `ORDER BY ${ sorting } ` : '' ;
1489
+ }
1490
+ }
1491
+
1492
+ const qs = `SELECT ${ columns } FROM $1:name ${ wherePattern } ${ sortPattern } ${ limitPattern } ${ skipPattern } ${ groupPattern } ` ;
1493
+ debug ( qs , values ) ;
1494
+ return this . _client . any ( qs , values ) . then ( results => {
1495
+ if ( countField ) {
1496
+ results [ 0 ] [ countField ] = parseInt ( results [ 0 ] [ countField ] , 10 ) ;
1497
+ }
1498
+ results . forEach ( result => {
1499
+ if ( ! result . hasOwnProperty ( 'objectId' ) ) {
1500
+ result . objectId = null ;
1501
+ }
1502
+ } ) ;
1503
+ return results ;
1504
+ } ) ;
1505
+ }
1506
+
1369
1507
performInitialization ( { VolatileClassesSchemas } ) {
1370
1508
debug ( 'performInitialization' ) ;
1371
1509
const promises = VolatileClassesSchemas . map ( ( schema ) => {
0 commit comments