tag: get rid of windowing

old-stable24
Varun Patil 2022-10-26 09:29:32 -07:00
parent 352f8d3d54
commit 6d8f06c885
3 changed files with 97 additions and 101 deletions

View File

@ -248,28 +248,7 @@ class ApiController extends Controller
); );
// Preload all tag previews // Preload all tag previews
$previews = $this->timelineQuery->getTagPreviews($folder); $this->timelineQuery->getTagPreviews($list, $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) {
$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); return new JSONResponse($list, Http::STATUS_OK);
} }

View File

@ -159,34 +159,33 @@ trait TimelineQueryDays
return $day; return $day;
} }
/** Get the query for oc_filecache join */ /**
private function getFilecacheJoinQuery( * Get all folders inside a top folder
IQueryBuilder &$query, */
private function getSubfolderIdsRecursive(
IDBConnection &$conn,
Folder &$folder, Folder &$folder,
bool $recursive,
bool $archive bool $archive
) { ) {
$pathQuery = null; // CTE to get all folders recursively in the given top folder
if ($recursive) { $cte =
// CTE to get all folders recursively in the given top folder 'WITH RECURSIVE cte_folders(fileid) AS (
$cte = SELECT
'WITH RECURSIVE cte_folders(fileid) AS ( f.fileid
SELECT FROM
f.fileid *PREFIX*filecache f
FROM WHERE
*PREFIX*filecache f f.fileid = :topFolderId
WHERE UNION ALL
f.fileid = :topFolderId SELECT
UNION ALL f.fileid
SELECT FROM
f.fileid *PREFIX*filecache f
FROM INNER JOIN cte_folders c
*PREFIX*filecache f ON (f.parent = c.fileid
INNER JOIN cte_folders c AND f.mimetype = 2
ON (f.parent = c.fileid AND f.fileid NOT IN (:excludedFolderIds)
AND f.mimetype = 2 )
AND f.fileid NOT IN (:excludedFolderIds)
)
) )
SELECT SELECT
fileid fileid
@ -194,34 +193,60 @@ trait TimelineQueryDays
cte_folders cte_folders
'; ';
// Query parameters, set at the end // Query parameters, set at the end
$topFolderId = $folder->getId(); $topFolderId = $folder->getId();
$excludedFolderIds = [-1]; // cannot be empty $excludedFolderIds = [-1]; // cannot be empty
/** @var Folder Archive folder if it exists */ /** @var Folder Archive folder if it exists */
$archiveFolder = null; $archiveFolder = null;
try { try {
$archiveFolder = $folder->get('.archive/'); $archiveFolder = $folder->get('.archive/');
} catch (\OCP\Files\NotFoundException $e) { } catch (\OCP\Files\NotFoundException $e) {
}
if (!$archive) {
// Exclude archive folder
if ($archiveFolder) {
$excludedFolderIds[] = $archiveFolder->getId();
} }
} else {
// Only include archive folder
$topFolderId = $archiveFolder ? $archiveFolder->getId() : -1;
}
if (!$archive) { return array_column($conn->executeQuery($cte, [
// Exclude archive folder 'topFolderId' => $topFolderId,
if ($archiveFolder) { 'excludedFolderIds' => $excludedFolderIds,
$excludedFolderIds[] = $archiveFolder->getId(); ])->fetchAll(), 'fileid');
} }
/**
* Get the query for oc_filecache join
*
* @param IQueryBuilder $query Query builder
* @param Folder|array $folder Either the top folder or array of folder Ids
* @param bool $recursive Whether to get the days recursively
* @param bool $archive Whether to get the days only from the archive folder
*/
private function getFilecacheJoinQuery(
IQueryBuilder &$query,
&$folder,
bool $recursive,
bool $archive
) {
$pathQuery = null;
if ($recursive) {
// Get all subfolder Ids recursively
$folderIds = [];
if ($folder instanceof Folder) {
$folderIds = $this->getSubfolderIdsRecursive($query->getConnection(), $folder, $archive);
} else { } else {
// Only include archive folder $folderIds = $folder;
$topFolderId = $archiveFolder ? $archiveFolder->getId() : -1;
} }
// Join with CTE // Join with folder IDs
$pathQuery = $query->expr()->in('f.parent', $query->createFunction($cte)); $pathQuery = $query->expr()->in('f.parent', $query->createNamedParameter($folderIds, IQueryBuilder::PARAM_INT_ARRAY));
// Set query parameters
$query->setParameter('topFolderId', $topFolderId, IQueryBuilder::PARAM_INT);
$query->setParameter('excludedFolderIds', $excludedFolderIds, IQueryBuilder::PARAM_INT_ARRAY);
} else { } else {
// If getting non-recursively folder only check for parent // If getting non-recursively folder only check for parent
$pathQuery = $query->expr()->eq('f.parent', $query->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT)); $pathQuery = $query->expr()->eq('f.parent', $query->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT));

View File

@ -77,47 +77,39 @@ trait TimelineQueryTags
return $tags; return $tags;
} }
public function getTagPreviews(Folder $folder) public function getTagPreviews(array &$tags, Folder &$folder)
{ {
$query = $this->connection->getQueryBuilder(); // Cache subfolder ids to prevent duplicate requests
$folderIds = $this->getSubfolderIdsRecursive($this->connection, $folder, false);
// Windowing foreach ($tags as &$tag) {
$rowNumber = $query->createFunction('ROW_NUMBER() OVER (PARTITION BY stom.systemtagid) as n'); $query = $this->connection->getQueryBuilder();
// SELECT all photos with this tag // SELECT all photos with this tag
$query->select('f.fileid', 'f.etag', 'stom.systemtagid', $rowNumber)->from( $query->select('f.fileid', 'f.etag')->from(
'systemtag_object_mapping', 'systemtag_object_mapping',
'stom' 'stom'
)->where( )->where(
$query->expr()->eq('stom.objecttype', $query->createNamedParameter('files')), $query->expr()->eq('stom.objecttype', $query->createNamedParameter('files')),
); $query->expr()->eq('stom.systemtagid', $query->createNamedParameter($tag['id'])),
);
// WHERE these items are memories indexed photos // WHERE these items are memories indexed photos
$query->innerJoin('stom', 'memories', 'm', $query->expr()->eq('m.fileid', 'stom.objectid')); $query->innerJoin('stom', 'memories', 'm', $query->expr()->eq('m.fileid', 'stom.objectid'));
// WHERE these photos are in the user's requested folder recursively // WHERE these photos are in the user's requested folder recursively
$query->innerJoin('m', 'filecache', 'f', $this->getFilecacheJoinQuery($query, $folder, true, false)); $query->innerJoin('m', 'filecache', 'f', $this->getFilecacheJoinQuery($query, $folderIds, true, false));
// Make this a sub query // MAX 4
$fun = $query->createFunction('('.$query->getSQL().')'); $query->setMaxResults(4);
// Create outer query // FETCH tag previews
$outerQuery = $this->connection->getQueryBuilder(); $tag['previews'] = $query->executeQuery()->fetchAll();
$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 // Post-process
$previews = $outerQuery->executeQuery()->fetchAll(); foreach ($tag['previews'] as &$row) {
$row['fileid'] = (int) $row['fileid'];
// Post-process }
foreach ($previews as &$row) {
$row['fileid'] = (int) $row['fileid'];
$row['systemtagid'] = (int) $row['systemtagid'];
unset($row['n']);
} }
return $previews;
} }
} }