99use Middlewares \Utils \HttpErrorException ;
1010
1111use DBA \AbstractModelFactory ;
12+ use DBA \Aggregation ;
1213use DBA \JoinFilter ;
1314use DBA \Factory ;
1415use DBA \ContainFilter ;
@@ -382,9 +383,11 @@ public static function getManyResources(object $apiClass, Request $request, Resp
382383 $ aliasedfeatures = $ apiClass ->getAliasedFeatures ();
383384 $ factory = $ apiClass ->getFactory ();
384385
385- // TODO: Maximum and default should be configurable per server instance
386386 $ defaultPageSize = 10000 ;
387387 $ maxPageSize = 50000 ;
388+ // TODO: if 0.14.4 release has happened, following parameters can be retrieved from config
389+ // $defaultPageSize = SConfig::getInstance()->getVal(DConfig::DEFAULT_PAGE_SIZE);
390+ // $maxPageSize = SConfig::getInstance()->getVal(DConfig::MAX_PAGE_SIZE);
388391
389392 $ pageAfter = $ apiClass ->getQueryParameterFamilyMember ($ request , 'page ' , 'after ' ) ?? 0 ;
390393 $ pageSize = $ apiClass ->getQueryParameterFamilyMember ($ request , 'page ' , 'size ' ) ?? $ defaultPageSize ;
@@ -426,18 +429,28 @@ public static function getManyResources(object $apiClass, Request $request, Resp
426429 /* Include relation filters */
427430 $ finalFs = array_merge ($ aFs , $ relationFs );
428431
432+ $ primaryKey = $ apiClass ->getPrimaryKey ();
429433 //according to JSON API spec, first and last have to be calculated if inexpensive to compute
430434 //(https://jsonapi.org/profiles/ethanresnick/cursor-pagination/#auto-id-links))
431- //if this query is too expensive for big tables, it should be removed
432- $ max = $ factory ->minMaxFilter ($ finalFs , $ apiClass ->getPrimaryKey (), "MAX " );
435+ //if this query is too expensive for big tables, it can be removed
436+ $ agg1 = new Aggregation ($ primaryKey , Aggregation::MAX );
437+ $ agg2 = new Aggregation ($ primaryKey , Aggregation::MIN );
438+ $ agg3 = new Aggregation ($ primaryKey , Aggregation::COUNT );
439+ $ aggregation_results = $ factory ->multicolAggregationFilter ($ finalFs , [$ agg1 , $ agg2 , $ agg3 ]);
440+
441+ $ max = $ aggregation_results [$ agg1 ->getName ()];
442+ $ min = $ aggregation_results [$ agg2 ->getName ()];
443+ $ total = $ aggregation_results [$ agg3 ->getName ()];
444+
445+ $ totalPages = ceil ($ total / $ pageSize );
433446
434447 //pagination filters need to be added after max has been calculated
435448 $ finalFs [Factory::LIMIT ] = new LimitFilter ($ pageSize );
436449
437- $ finalFs [Factory::FILTER ][] = new QueryFilter ($ apiClass -> getPrimaryKey () , $ pageAfter , '> ' , $ factory );
450+ $ finalFs [Factory::FILTER ][] = new QueryFilter ($ primaryKey , $ pageAfter , '> ' , $ factory );
438451 $ pageBefore = $ apiClass ->getQueryParameterFamilyMember ($ request , 'page ' , 'before ' );
439452 if (isset ($ pageBefore )) {
440- $ finalFs [Factory::FILTER ][] = new QueryFilter ($ apiClass -> getPrimaryKey () , $ pageBefore , '< ' , $ factory );
453+ $ finalFs [Factory::FILTER ][] = new QueryFilter ($ primaryKey , $ pageBefore , '< ' , $ factory );
441454 }
442455
443456 /* Request objects */
@@ -525,12 +538,8 @@ public static function getManyResources(object $apiClass, Request $request, Resp
525538 }
526539 // Build prev link
527540 $ prevId = $ defaultSort == "DESC " ? $ maxId : $ minId ;
528- if ($ prevId != 1 ) { //only set previous page when its not the first page
541+ if ($ prevId != $ min ) { //only set previous page when its not the first page
529542 $ prevParams = $ selfParams ;
530- //This scenario might return a link to an empty array if the elements with the lowest id are deleted, but this is allowed according
531- //to the json API spec https://jsonapi.org/profiles/ethanresnick/cursor-pagination/#auto-id-links
532- //We could also get the lowest id the same way we got the max, but this is probably unnecessary expensive.
533- //But pull request: https://github.com/hashtopolis/server/pull/1069 would create a cheaper way of doing this in a single query
534543 $ prevParams ['page ' ]['before ' ] = $ prevId ;
535544 unset($ prevParams ['page ' ]['after ' ]);
536545 $ linksPrev = $ request ->getUri ()->getPath () . '? ' . urldecode (http_build_query ($ prevParams ));
@@ -541,7 +550,7 @@ public static function getManyResources(object $apiClass, Request $request, Resp
541550 $ firstParams = $ request ->getQueryParams ();
542551 unset($ firstParams ['page ' ]['before ' ]);
543552 $ firstParams ['page ' ]['size ' ] = $ pageSize ;
544- $ firstParams ['page ' ]['after ' ] = 0 ;
553+ $ firstParams ['page ' ]['after ' ] = $ min ;
545554 $ linksFirst = $ request ->getUri ()->getPath () . '? ' . urldecode (http_build_query ($ firstParams ));
546555 $ links = [
547556 "self " => $ linksSelf ,
@@ -551,8 +560,9 @@ public static function getManyResources(object $apiClass, Request $request, Resp
551560 "prev " => $ linksPrev ,
552561 ];
553562
563+ $ metadata = ["page " => ["total_pages " => $ totalPages ]];
554564 // Generate JSON:API GET output
555- $ ret = self ::createJsonResponse ($ dataResources , $ links , $ includedResources );
565+ $ ret = self ::createJsonResponse ($ dataResources , $ links , $ includedResources, $ metadata );
556566
557567 $ body = $ response ->getBody ();
558568 $ body ->write ($ apiClass ->ret2json ($ ret ));
0 commit comments