memories/lib/Db/TimelineQueryDays.php

272 lines
8.5 KiB
PHP
Raw Normal View History

2022-09-11 02:05:04 +00:00
<?php
2022-10-19 17:10:36 +00:00
2022-09-11 02:05:04 +00:00
declare(strict_types=1);
namespace OCA\Memories\Db;
use OCP\DB\QueryBuilder\IQueryBuilder;
2022-09-24 01:54:14 +00:00
use OCP\Files\Folder;
2022-10-19 17:10:36 +00:00
use OCP\IDBConnection;
2022-09-11 02:05:04 +00:00
2022-10-19 17:10:36 +00:00
trait TimelineQueryDays
{
2022-09-11 02:05:04 +00:00
protected IDBConnection $connection;
/**
2022-10-19 17:10:36 +00:00
* Get the days response from the database for the timeline.
*
2022-10-26 23:20:28 +00:00
* @param null|Folder $folder The folder to get the days from
* @param bool $recursive Whether to get the days recursively
* @param bool $archive Whether to get the days only from the archive folder
* @param array $queryTransforms An array of query transforms to apply to the query
2022-10-06 19:24:45 +00:00
*
* @return array The days response
2022-09-11 02:05:04 +00:00
*/
public function getDays(
2022-10-26 23:20:28 +00:00
&$folder,
2022-09-24 01:54:14 +00:00
string $uid,
bool $recursive,
2022-09-25 23:02:26 +00:00
bool $archive,
2022-09-12 01:33:38 +00:00
array $queryTransforms = []
2022-09-12 01:06:16 +00:00
): array {
2022-09-11 02:05:04 +00:00
$query = $this->connection->getQueryBuilder();
2022-09-12 01:03:40 +00:00
2022-09-24 01:54:14 +00:00
// Get all entries also present in filecache
$count = $query->func()->count($query->createFunction('DISTINCT m.fileid'), 'count');
$query->select('m.dayid', $count)
->from('memories', 'm')
2022-10-19 17:10:36 +00:00
->innerJoin('m', 'filecache', 'f', $this->getFilecacheJoinQuery($query, $folder, $recursive, $archive))
;
2022-09-24 01:54:14 +00:00
// Group and sort by dayid
$query->groupBy('m.dayid')
2022-10-19 17:10:36 +00:00
->orderBy('m.dayid', 'DESC')
;
2022-09-11 02:05:04 +00:00
2022-09-12 01:33:38 +00:00
// Apply all transformations
2022-10-06 21:19:47 +00:00
$this->applyAllTransforms($queryTransforms, $query, $uid);
2022-09-12 01:33:38 +00:00
2022-09-25 11:30:28 +00:00
$cursor = $query->executeQuery();
$rows = $cursor->fetchAll();
$cursor->closeCursor();
2022-10-19 17:10:36 +00:00
2022-09-11 02:05:04 +00:00
return $this->processDays($rows);
}
2022-10-06 19:24:45 +00:00
/**
2022-10-19 17:10:36 +00:00
* Get the day response from the database for the timeline.
*
2022-10-26 23:20:28 +00:00
* @param null|Folder $folder The folder to get the day from
* @param string $uid The user id
* @param int[] $dayid The day id
* @param bool $recursive If the query should be recursive
* @param bool $archive If the query should include only the archive folder
* @param array $queryTransforms The query transformations to apply
* @param mixed $day_ids
2022-10-19 17:10:36 +00:00
*
2022-10-06 19:24:45 +00:00
* @return array An array of day responses
2022-09-11 02:05:04 +00:00
*/
2022-09-24 01:54:14 +00:00
public function getDay(
2022-10-26 23:20:28 +00:00
&$folder,
2022-09-24 01:54:14 +00:00
string $uid,
2022-10-06 22:01:28 +00:00
$day_ids,
2022-09-24 01:54:14 +00:00
bool $recursive,
2022-09-25 23:02:26 +00:00
bool $archive,
2022-09-24 01:54:14 +00:00
array $queryTransforms = []
): array {
2022-09-11 02:05:04 +00:00
$query = $this->connection->getQueryBuilder();
2022-09-24 01:54:14 +00:00
// Get all entries also present in filecache
$fileid = $query->createFunction('DISTINCT m.fileid');
// We don't actually use m.datetaken here, but postgres
// needs that all fields in ORDER BY are also in SELECT
// when using DISTINCT on selected fields
2022-10-15 19:23:31 +00:00
$query->select($fileid, 'f.etag', 'm.isvideo', 'vco.categoryid', 'm.datetaken', 'm.dayid', 'm.w', 'm.h')
2022-09-24 01:54:14 +00:00
->from('memories', 'm')
2022-10-19 17:10:36 +00:00
->innerJoin('m', 'filecache', 'f', $this->getFilecacheJoinQuery($query, $folder, $recursive, $archive))
;
2022-10-06 22:01:28 +00:00
// Filter by dayid unless wildcard
2022-10-19 17:10:36 +00:00
if (null !== $day_ids) {
2022-10-06 22:01:28 +00:00
$query->andWhere($query->expr()->in('m.dayid', $query->createNamedParameter($day_ids, IQueryBuilder::PARAM_INT_ARRAY)));
} else {
// Limit wildcard to 100 results
$query->setMaxResults(100);
}
2022-09-24 01:54:14 +00:00
// Add favorite field
$this->addFavoriteTag($query, $uid);
// Group and sort by date taken
$query->orderBy('m.datetaken', 'DESC');
2022-10-16 19:18:31 +00:00
$query->addOrderBy('m.fileid', 'DESC'); // tie-breaker
2022-09-24 01:54:14 +00:00
// Apply all transformations
2022-10-06 21:19:47 +00:00
$this->applyAllTransforms($queryTransforms, $query, $uid);
2022-09-11 02:05:04 +00:00
2022-09-25 11:30:28 +00:00
$cursor = $query->executeQuery();
$rows = $cursor->fetchAll();
$cursor->closeCursor();
2022-10-19 17:10:36 +00:00
2022-09-24 01:54:14 +00:00
return $this->processDay($rows);
2022-09-11 02:05:04 +00:00
}
2022-10-19 17:10:36 +00:00
/**
* Process the days response.
*
* @param array $days
*/
private function processDays(&$days)
{
foreach ($days as &$row) {
2022-10-19 17:15:14 +00:00
$row['dayid'] = (int) $row['dayid'];
$row['count'] = (int) $row['count'];
2022-10-19 17:10:36 +00:00
}
return $days;
}
/**
* Process the single day response.
*
* @param array $day
*/
private function processDay(&$day)
{
foreach ($day as &$row) {
// We don't need date taken (see query builder)
unset($row['datetaken']);
// Convert field types
2022-10-19 17:15:14 +00:00
$row['fileid'] = (int) $row['fileid'];
$row['isvideo'] = (int) $row['isvideo'];
$row['dayid'] = (int) $row['dayid'];
$row['w'] = (int) $row['w'];
$row['h'] = (int) $row['h'];
2022-10-19 17:10:36 +00:00
if (!$row['isvideo']) {
unset($row['isvideo']);
}
if ($row['categoryid']) {
$row['isfavorite'] = 1;
}
unset($row['categoryid']);
// All transform processing
$this->processFace($row);
}
return $day;
}
2022-10-26 16:29:32 +00:00
/**
2022-10-26 17:06:45 +00:00
* Get all folders inside a top folder.
2022-10-26 16:29:32 +00:00
*/
private function getSubfolderIdsRecursive(
IDBConnection &$conn,
2022-10-19 17:10:36 +00:00
Folder &$folder,
bool $archive
) {
2022-10-26 16:29:32 +00:00
// CTE to get all folders recursively in the given top folder
$cte =
'WITH RECURSIVE cte_folders(fileid) AS (
SELECT
f.fileid
FROM
*PREFIX*filecache f
WHERE
f.fileid = :topFolderId
UNION ALL
SELECT
f.fileid
FROM
*PREFIX*filecache f
INNER JOIN cte_folders c
ON (f.parent = c.fileid
AND f.mimetype = 2
AND f.fileid NOT IN (:excludedFolderIds)
)
2022-10-25 00:14:50 +00:00
)
SELECT
fileid
FROM
cte_folders
';
2022-10-26 16:29:32 +00:00
// Query parameters, set at the end
$topFolderId = $folder->getId();
$excludedFolderIds = [-1]; // cannot be empty
2022-10-25 00:14:50 +00:00
2022-10-26 16:29:32 +00:00
/** @var Folder Archive folder if it exists */
$archiveFolder = null;
2022-10-25 00:14:50 +00:00
2022-10-26 16:29:32 +00:00
try {
$archiveFolder = $folder->get('.archive/');
} catch (\OCP\Files\NotFoundException $e) {
}
if (!$archive) {
// Exclude archive folder
if ($archiveFolder) {
$excludedFolderIds[] = $archiveFolder->getId();
2022-10-19 17:10:36 +00:00
}
2022-10-26 16:29:32 +00:00
} else {
// Only include archive folder
$topFolderId = $archiveFolder ? $archiveFolder->getId() : -1;
}
2022-10-26 18:13:58 +00:00
return array_map('intval', array_column($conn->executeQuery($cte, [
2022-10-26 16:29:32 +00:00
'topFolderId' => $topFolderId,
'excludedFolderIds' => $excludedFolderIds,
2022-10-26 18:13:58 +00:00
], [
'topFolderId' => IQueryBuilder::PARAM_INT,
'excludedFolderIds' => IQueryBuilder::PARAM_INT_ARRAY,
])->fetchAll(), 'fileid'));
2022-10-26 16:29:32 +00:00
}
2022-10-19 17:10:36 +00:00
2022-10-26 16:29:32 +00:00
/**
2022-10-26 17:06:45 +00:00
* Get the query for oc_filecache join.
2022-10-26 16:29:32 +00:00
*
2022-10-26 23:20:28 +00:00
* @param IQueryBuilder $query Query builder
* @param null|array|Folder $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
2022-10-26 16:29:32 +00:00
*/
private function getFilecacheJoinQuery(
IQueryBuilder &$query,
&$folder,
bool $recursive,
bool $archive
) {
2022-10-26 23:20:28 +00:00
// Join with memories
$baseOp = $query->expr()->eq('f.fileid', 'm.fileid');
if (null === $folder) {
return $baseOp; // No folder, get all
}
// Filter by folder (recursive or otherwise)
$pathOp = null;
2022-10-26 16:29:32 +00:00
if ($recursive) {
// Get all subfolder Ids recursively
$folderIds = [];
if ($folder instanceof Folder) {
2022-10-26 17:40:33 +00:00
$conn = $query->getConnection();
$folderIds = $this->getSubfolderIdsRecursive($conn, $folder, $archive);
2022-10-19 17:10:36 +00:00
} else {
2022-10-26 16:29:32 +00:00
$folderIds = $folder;
2022-10-19 17:10:36 +00:00
}
2022-10-25 00:14:50 +00:00
2022-10-26 16:29:32 +00:00
// Join with folder IDs
2022-10-26 23:20:28 +00:00
$pathOp = $query->expr()->in('f.parent', $query->createNamedParameter($folderIds, IQueryBuilder::PARAM_INT_ARRAY));
2022-10-19 17:10:36 +00:00
} else {
// If getting non-recursively folder only check for parent
2022-10-26 23:20:28 +00:00
$pathOp = $query->expr()->eq('f.parent', $query->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT));
2022-10-19 17:10:36 +00:00
}
return $query->expr()->andX(
2022-10-26 23:20:28 +00:00
$baseOp,
$pathOp,
2022-10-19 17:10:36 +00:00
);
}
2022-09-11 02:05:04 +00:00
}