diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index d93a8325..54add7f1 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -321,10 +321,27 @@ class ApiController extends Controller { ); // Preload all tag previews + $previews = $this->timelineQuery->getTagPreviews($folder); + + // Convert to map with key as systemtagid + $previews_map = []; + foreach ($previews as &$preview) { + $key = $preview["systemtagid"]; + if (!array_key_exists($key, $previews_map)) { + $previews_map[$key] = []; + } + unset($preview["systemtagid"]); + $previews_map[$key][] = $preview; + } + + // Add previews to list foreach ($list as &$tag) { - $tag["previews"] = $this->timelineQuery->getTagPreviews( - $folder, $tag["id"], - ); + $key = $tag["id"]; + if (array_key_exists($key, $previews_map)) { + $tag["previews"] = $previews_map[$key]; + } else { + $tag["previews"] = []; + } } return new JSONResponse($list, Http::STATUS_OK); diff --git a/lib/Db/TimelineQueryFaces.php b/lib/Db/TimelineQueryFaces.php index 67761890..581560ff 100644 --- a/lib/Db/TimelineQueryFaces.php +++ b/lib/Db/TimelineQueryFaces.php @@ -56,6 +56,7 @@ trait TimelineQueryFaces { public function getFacePreviews(Folder $folder) { $query = $this->connection->getQueryBuilder(); + // Windowing $rowNumber = $query->createFunction('ROW_NUMBER() OVER (PARTITION BY rfd.cluster_id) as n'); // SELECT face detections for ID diff --git a/lib/Db/TimelineQueryTags.php b/lib/Db/TimelineQueryTags.php index 4ed1bae7..d89f1714 100644 --- a/lib/Db/TimelineQueryTags.php +++ b/lib/Db/TimelineQueryTags.php @@ -69,14 +69,17 @@ trait TimelineQueryTags { return $tags; } - public function getTagPreviews(Folder $folder, int $tagId) { + public function getTagPreviews(Folder $folder) { $query = $this->connection->getQueryBuilder(); + // Windowing + $rowNumber = $query->createFunction('ROW_NUMBER() OVER (PARTITION BY stom.systemtagid) as n'); + // SELECT all photos with this tag - $query->select('f.fileid', 'f.etag')->from('systemtag_object_mapping', 'stom')->where($query->expr()->andX( - $query->expr()->eq('stom.objecttype', $query->createNamedParameter("files")), - $query->expr()->eq('stom.systemtagid', $query->createNamedParameter($tagId, IQueryBuilder::PARAM_INT)), - )); + $query->select('f.fileid', 'f.etag', 'stom.systemtagid', $rowNumber)->from( + 'systemtag_object_mapping', 'stom')->where( + $query->expr()->eq('stom.objecttype', $query->createNamedParameter("files")), + ); // WHERE these items are memories indexed photos $query->innerJoin('stom', 'memories', 'm', $query->expr()->eq('m.fileid', 'stom.objectid')); @@ -84,15 +87,24 @@ trait TimelineQueryTags { // WHERE these photos are in the user's requested folder recursively $query->innerJoin('m', 'filecache', 'f', $this->getFilecacheJoinQuery($query, $folder, true, false)); - // MAX 4 results - $query->setMaxResults(4); + // Make this a sub query + $fun = $query->createFunction('(' . $query->getSQL() . ')'); - // FETCH all previews - $previews = $query->executeQuery()->fetchAll(); + // Create outer query + $outerQuery = $this->connection->getQueryBuilder(); + $outerQuery->setParameters($query->getParameters()); + $outerQuery->select('*')->from($fun, 't'); + $outerQuery->where($query->expr()->lte('t.n', $outerQuery->createParameter('nc'))); + $outerQuery->setParameter('nc', 4, IQueryBuilder::PARAM_INT); + + // FETCH all tag previews + $previews = $outerQuery->executeQuery()->fetchAll(); // Post-process foreach($previews as &$row) { $row["fileid"] = intval($row["fileid"]); + $row["systemtagid"] = intval($row["systemtagid"]); + unset($row["n"]); } return $previews;