Speed up tags
parent
759076c4ac
commit
de845bd543
|
@ -278,7 +278,7 @@ class ApiController extends Controller
|
||||||
|
|
||||||
// Run actual query
|
// Run actual query
|
||||||
$list = [];
|
$list = [];
|
||||||
$t = (int) ($this->request->getParam('t'));
|
$t = (int) $this->request->getParam('t');
|
||||||
if ($t & 1) { // personal
|
if ($t & 1) { // personal
|
||||||
$list = array_merge($list, $this->timelineQuery->getAlbums($user->getUID()));
|
$list = array_merge($list, $this->timelineQuery->getAlbums($user->getUID()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,20 +22,27 @@ class TimelineQuery
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function debugQuery(IQueryBuilder $query, string $sql = '')
|
public static function debugQuery(IQueryBuilder &$query, string $sql = '')
|
||||||
{
|
{
|
||||||
// Print the query and exit
|
// Print the query and exit
|
||||||
$sql = empty($sql) ? $query->getSQL() : $sql;
|
$sql = empty($sql) ? $query->getSQL() : $sql;
|
||||||
$params = $query->getParameters();
|
|
||||||
$sql = str_replace('*PREFIX*', 'oc_', $sql);
|
$sql = str_replace('*PREFIX*', 'oc_', $sql);
|
||||||
foreach ($params as $key => $value) {
|
self::replaceQueryParams($query, $sql);
|
||||||
$sql = str_replace(':'.$key, $query->getConnection()->getDatabasePlatform()->quoteStringLiteral($value), $sql);
|
|
||||||
}
|
|
||||||
echo "{$sql}";
|
echo "{$sql}";
|
||||||
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function replaceQueryParams(IQueryBuilder &$query, string $sql)
|
||||||
|
{
|
||||||
|
$params = $query->getParameters();
|
||||||
|
foreach ($params as $key => $value) {
|
||||||
|
$sql = str_replace(':'.$key, $query->getConnection()->getDatabasePlatform()->quoteStringLiteral($value), $sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
|
||||||
public function getInfoById(int $id): array
|
public function getInfoById(int $id): array
|
||||||
{
|
{
|
||||||
$qb = $this->connection->getQueryBuilder();
|
$qb = $this->connection->getQueryBuilder();
|
||||||
|
|
|
@ -179,9 +179,9 @@ trait TimelineQueryDays
|
||||||
return $day;
|
return $day;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function executeQueryWithCTEs(IQueryBuilder &$query)
|
private function executeQueryWithCTEs(IQueryBuilder &$query, string $psql = '')
|
||||||
{
|
{
|
||||||
$sql = $query->getSQL();
|
$sql = empty($psql) ? $query->getSQL() : $psql;
|
||||||
$params = $query->getParameters();
|
$params = $query->getParameters();
|
||||||
$types = $query->getParameterTypes();
|
$types = $query->getParameterTypes();
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ trait TimelineQueryDays
|
||||||
/**
|
/**
|
||||||
* Get all folders inside a top folder.
|
* Get all folders inside a top folder.
|
||||||
*/
|
*/
|
||||||
private function joinSubfoldersRecursive(
|
private function addSubfolderJoinParams(
|
||||||
IQueryBuilder &$query,
|
IQueryBuilder &$query,
|
||||||
Folder &$folder,
|
Folder &$folder,
|
||||||
bool $archive
|
bool $archive
|
||||||
|
@ -226,7 +226,6 @@ trait TimelineQueryDays
|
||||||
// Add query parameters
|
// Add query parameters
|
||||||
$query->setParameter('topFolderId', $topFolderId, IQueryBuilder::PARAM_INT);
|
$query->setParameter('topFolderId', $topFolderId, IQueryBuilder::PARAM_INT);
|
||||||
$query->setParameter('excludedFolderId', $excludedFolderId, IQueryBuilder::PARAM_INT);
|
$query->setParameter('excludedFolderId', $excludedFolderId, IQueryBuilder::PARAM_INT);
|
||||||
$query->innerJoin('f', 'cte_folders', 'cte_f', $query->expr()->eq('f.parent', 'cte_f.fileid'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -253,7 +252,8 @@ trait TimelineQueryDays
|
||||||
$pathOp = null;
|
$pathOp = null;
|
||||||
if ($recursive) {
|
if ($recursive) {
|
||||||
// Join with folders CTE
|
// Join with folders CTE
|
||||||
$this->joinSubfoldersRecursive($query, $folder, $archive);
|
$this->addSubfolderJoinParams($query, $folder, $archive);
|
||||||
|
$query->innerJoin('f', 'cte_folders', 'cte_f', $query->expr()->eq('f.parent', 'cte_f.fileid'));
|
||||||
} else {
|
} else {
|
||||||
// If getting non-recursively folder only check for parent
|
// If getting non-recursively folder only check for parent
|
||||||
$pathOp = $query->expr()->eq('f.parent', $query->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT));
|
$pathOp = $query->expr()->eq('f.parent', $query->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT));
|
||||||
|
|
|
@ -58,7 +58,17 @@ trait TimelineQueryTags
|
||||||
$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 = $this->joinFilecache($query, $folder, true, false);
|
// This is a hack to speed up the query instead of using joinFilecache
|
||||||
|
// The problem is objectid is VARCHAR(64) and fileid is BIGINT(20), so a
|
||||||
|
// join is extremely slow. Instead, we use a subquery to check existence.
|
||||||
|
//
|
||||||
|
// https://blog.sqlauthority.com/2010/06/05/sql-server-convert-in-to-exists-performance-talk/
|
||||||
|
|
||||||
|
$this->addSubfolderJoinParams($query, $folder, false);
|
||||||
|
$query->innerJoin('m', 'filecache', 'f', $query->expr()->andX(
|
||||||
|
$query->expr()->eq('f.fileid', 'm.fileid'),
|
||||||
|
$query->createFunction('EXISTS (SELECT 1 from *PREFIX*cte_folders WHERE *PREFIX*cte_folders.fileid = `f`.parent)')
|
||||||
|
));
|
||||||
|
|
||||||
// GROUP and ORDER by tag name
|
// GROUP and ORDER by tag name
|
||||||
$query->groupBy('st.name');
|
$query->groupBy('st.name');
|
||||||
|
@ -80,11 +90,17 @@ trait TimelineQueryTags
|
||||||
|
|
||||||
public function getTagPreviews(array &$tags, Folder &$folder)
|
public function getTagPreviews(array &$tags, Folder &$folder)
|
||||||
{
|
{
|
||||||
|
// This is really horrible but will have to do for now
|
||||||
|
$sql = '';
|
||||||
foreach ($tags as &$tag) {
|
foreach ($tags as &$tag) {
|
||||||
|
if (!empty($sql)) {
|
||||||
|
$sql .= ' UNION ALL ';
|
||||||
|
}
|
||||||
|
|
||||||
$query = $this->connection->getQueryBuilder();
|
$query = $this->connection->getQueryBuilder();
|
||||||
|
|
||||||
// SELECT all photos with this tag
|
// SELECT all photos with this tag
|
||||||
$query->select('f.fileid', 'f.etag')->from(
|
$query->select('f.fileid', 'f.etag', 'stom.systemtagid')->from(
|
||||||
'systemtag_object_mapping',
|
'systemtag_object_mapping',
|
||||||
'stom'
|
'stom'
|
||||||
)->where(
|
)->where(
|
||||||
|
@ -96,19 +112,42 @@ trait TimelineQueryTags
|
||||||
$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 = $this->joinFilecache($query, $folder, true, false);
|
// See the function above for an explanation of this hack
|
||||||
|
$this->addSubfolderJoinParams($query, $folder, false);
|
||||||
|
$query->innerJoin('m', 'filecache', 'f', $query->expr()->andX(
|
||||||
|
$query->expr()->eq('f.fileid', 'm.fileid'),
|
||||||
|
$query->createFunction('EXISTS (SELECT 1 from *PREFIX*cte_folders WHERE *PREFIX*cte_folders.fileid = `f`.parent)')
|
||||||
|
));
|
||||||
|
|
||||||
// MAX 4
|
// MAX 4
|
||||||
$query->setMaxResults(4);
|
$query->setMaxResults(4);
|
||||||
|
|
||||||
// FETCH tag previews
|
// Replace parameters
|
||||||
$cursor = $this->executeQueryWithCTEs($query);
|
$thisSql = self::replaceQueryParams($query, $query->getSQL());
|
||||||
$tag['previews'] = $cursor->fetchAll();
|
|
||||||
|
|
||||||
// Post-process
|
// Add clause
|
||||||
foreach ($tag['previews'] as &$row) {
|
$sql .= "({$thisSql})";
|
||||||
$row['fileid'] = (int) $row['fileid'];
|
}
|
||||||
|
|
||||||
|
// FETCH tag previews
|
||||||
|
$cursor = $this->executeQueryWithCTEs($query, $sql);
|
||||||
|
$ans = $cursor->fetchAll();
|
||||||
|
|
||||||
|
// Post-process
|
||||||
|
$previewMap = [];
|
||||||
|
foreach ($ans as &$row) {
|
||||||
|
$row['fileid'] = (int) $row['fileid'];
|
||||||
|
$key = (int) $row['systemtagid'];
|
||||||
|
unset($row['systemtagid']);
|
||||||
|
if (!isset($previewMap[$key])) {
|
||||||
|
$previewMap[$key] = [];
|
||||||
}
|
}
|
||||||
|
$previewMap[$key][] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add previews to tags
|
||||||
|
foreach ($tags as &$tag) {
|
||||||
|
$tag['previews'] = $previewMap[$tag['id']] ?? [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue