Use separate tag preview endpoint

old-stable24
Varun Patil 2022-10-27 13:26:51 -07:00
parent 46eb0fd97a
commit a0d757adfc
4 changed files with 90 additions and 54 deletions

View File

@ -30,6 +30,7 @@ return [
['name' => 'api#day', 'url' => '/api/days/{id}', 'verb' => 'GET'], ['name' => 'api#day', 'url' => '/api/days/{id}', 'verb' => 'GET'],
['name' => 'api#tags', 'url' => '/api/tags', 'verb' => 'GET'], ['name' => 'api#tags', 'url' => '/api/tags', 'verb' => 'GET'],
['name' => 'api#tagPreviews', 'url' => '/api/tag-previews', 'verb' => 'GET'],
['name' => 'api#albums', 'url' => '/api/albums', 'verb' => 'GET'], ['name' => 'api#albums', 'url' => '/api/albums', 'verb' => 'GET'],

View File

@ -253,8 +253,40 @@ class ApiController extends Controller
$folder, $folder,
); );
// Preload all tag previews return new JSONResponse($list, Http::STATUS_OK);
$this->timelineQuery->getTagPreviews($list, $folder); }
/**
* @NoAdminRequired
*
* Get previews for a tag
*/
public function tagPreviews(): JSONResponse
{
$user = $this->userSession->getUser();
if (null === $user) {
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
}
// Check tags enabled for this user
if (!$this->tagsIsEnabled()) {
return new JSONResponse(['message' => 'Tags not enabled for user'], Http::STATUS_PRECONDITION_FAILED);
}
// If this isn't the timeline folder then things aren't going to work
$folder = $this->getRequestFolder();
if (null === $folder) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
// Get the tag
$tagName = $this->request->getParam('tag');
// Run actual query
$list = $this->timelineQuery->getTagPreviews(
$tagName,
$folder,
);
return new JSONResponse($list, Http::STATUS_OK); return new JSONResponse($list, Http::STATUS_OK);
} }

View File

@ -88,66 +88,46 @@ trait TimelineQueryTags
return $tags; return $tags;
} }
public function getTagPreviews(array &$tags, Folder &$folder) public function getTagPreviews(string $tagName, Folder &$folder)
{ {
// This is really horrible but will have to do for now $query = $this->connection->getQueryBuilder();
$sql = ''; $tagId = $this->getSystemTagId($query, $tagName);
foreach ($tags as &$tag) { if (false === $tagId) {
if (!empty($sql)) { return [];
$sql .= ' UNION ALL ';
}
$query = $this->connection->getQueryBuilder();
// SELECT all photos with this tag
$query->select('f.fileid', 'f.etag', 'stom.systemtagid')->from(
'systemtag_object_mapping',
'stom'
)->where(
$query->expr()->eq('stom.objecttype', $query->createNamedParameter('files')),
$query->expr()->eq('stom.systemtagid', $query->createNamedParameter($tag['id'])),
);
// WHERE these items are memories indexed photos
$query->innerJoin('stom', 'memories', 'm', $query->expr()->eq('m.fileid', 'stom.objectid'));
// WHERE these photos are in the user's requested folder recursively
// 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
$query->setMaxResults(4);
// Replace parameters
$thisSql = self::replaceQueryParams($query, $query->getSQL());
// Add clause
$sql .= "({$thisSql})";
} }
// SELECT all photos with this tag
$query->select('f.fileid', 'f.etag', 'stom.systemtagid')->from(
'systemtag_object_mapping',
'stom'
)->where(
$query->expr()->eq('stom.objecttype', $query->createNamedParameter('files')),
$query->expr()->eq('stom.systemtagid', $query->createNamedParameter($tagId)),
);
// WHERE these items are memories indexed photos
$query->innerJoin('stom', 'memories', 'm', $query->expr()->eq('m.fileid', 'stom.objectid'));
// WHERE these photos are in the user's requested folder recursively
// 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
$query->setMaxResults(4);
// FETCH tag previews // FETCH tag previews
$cursor = $this->executeQueryWithCTEs($query, $sql); $cursor = $this->executeQueryWithCTEs($query);
$ans = $cursor->fetchAll(); $ans = $cursor->fetchAll();
// Post-process // Post-process
$previewMap = [];
foreach ($ans as &$row) { foreach ($ans as &$row) {
$row['fileid'] = (int) $row['fileid']; $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 return $ans;
foreach ($tags as &$tag) {
$tag['previews'] = $previewMap[$tag['id']] ?? [];
}
} }
} }

View File

@ -34,7 +34,9 @@ import { generateUrl } from '@nextcloud/router'
import { getPreviewUrl } from "../../services/FileUtils"; import { getPreviewUrl } from "../../services/FileUtils";
import { getCurrentUser } from '@nextcloud/auth'; import { getCurrentUser } from '@nextcloud/auth';
import { NcCounterBubble } from '@nextcloud/vue' import { NcCounterBubble } from '@nextcloud/vue';
import axios from '@nextcloud/axios';
import * as utils from "../../services/Utils";
import GlobalMixin from '../../mixins/GlobalMixin'; import GlobalMixin from '../../mixins/GlobalMixin';
import { constants } from '../../services/Utils'; import { constants } from '../../services/Utils';
@ -112,7 +114,28 @@ export default class Tag extends Mixins(GlobalMixin) {
} }
// Look for previews // Look for previews
if (!this.data.previews) return; if (!this.data.previews) {
try {
const todayDayId = utils.dateToDayId(new Date());
const url = generateUrl(`/apps/memories/api/tag-previews?tag=${this.data.name}`);
const cacheUrl = `${url}&today=${Math.floor(todayDayId / 10)}`;
const cache = await utils.getCachedData(cacheUrl);
if (cache) {
this.data.previews = cache as any;
} else {
const res = await axios.get(url);
this.data.previews = res.data;
// Cache only if >= 4 previews
if (this.data.previews.length >= 4) {
utils.cacheData(cacheUrl, res.data);
}
}
} catch (e) {
this.error = true;
return;
}
}
// Reset flag // Reset flag
this.data.previews.forEach((p) => p.flag = 0); this.data.previews.forEach((p) => p.flag = 0);