From 66f9616942f99868856bbca733923d1976b478f3 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 3 Aug 2023 19:43:57 -0700 Subject: [PATCH] perf: reduce metadata queries Signed-off-by: Varun Patil --- lib/ClustersBackend/AlbumsBackend.php | 2 +- lib/ClustersBackend/Backend.php | 24 ++++++++- .../FaceRecognitionBackend.php | 2 +- lib/ClustersBackend/PlacesBackend.php | 2 +- lib/ClustersBackend/RecognizeBackend.php | 2 +- lib/ClustersBackend/TagsBackend.php | 2 +- lib/Controller/ClustersController.php | 6 --- lib/Controller/ImageController.php | 45 ++++++++++------ src/components/Metadata.vue | 52 ++++++++++--------- src/services/API.ts | 8 +-- src/services/dav/albums.ts | 4 +- src/services/dav/face.ts | 5 +- src/types.ts | 5 ++ 13 files changed, 97 insertions(+), 62 deletions(-) diff --git a/lib/ClustersBackend/AlbumsBackend.php b/lib/ClustersBackend/AlbumsBackend.php index 95db0738..5163af04 100644 --- a/lib/ClustersBackend/AlbumsBackend.php +++ b/lib/ClustersBackend/AlbumsBackend.php @@ -87,7 +87,7 @@ class AlbumsBackend extends Backend $this->tq->allowEmptyRoot(); } - public function getClusters(int $fileid = 0): array + protected function getClustersI(int $fileid = 0): array { // Run actual queries $list = []; diff --git a/lib/ClustersBackend/Backend.php b/lib/ClustersBackend/Backend.php index c324fc68..01a02e0f 100644 --- a/lib/ClustersBackend/Backend.php +++ b/lib/ClustersBackend/Backend.php @@ -61,9 +61,12 @@ abstract class Backend /** * Get the cluster list for the current user. * + * If the signature of this function changes, the + * getClusters function must be updated to match. + * * @param int $fileid Filter clusters by file ID (optional) */ - abstract public function getClusters(int $fileid = 0): array; + abstract protected function getClustersI(int $fileid = 0): array; /** * Get a cluster ID for the given cluster. @@ -125,10 +128,27 @@ abstract class Backend return (int) $photo['fileid']; } + /** + * Calls the getClusters implementation and appends the + * result with the cluster_id and cluster_type values. + * + * @param int $fileid Filter clusters by file ID (optional) + */ + public final function getClusters(int $fileid): array { + $list = $this->getClustersI($fileid); + + foreach ($list as &$cluster) { + $cluster['cluster_id'] = $this->getClusterId($cluster); + $cluster['cluster_type'] = $this->clusterType(); + } + + return $list; + } + /** * Register the backend. Do not override. */ - public static function register(): void + public static final function register(): void { Manager::register(static::clusterType(), static::class); } diff --git a/lib/ClustersBackend/FaceRecognitionBackend.php b/lib/ClustersBackend/FaceRecognitionBackend.php index 04df7fc8..6fddb20e 100644 --- a/lib/ClustersBackend/FaceRecognitionBackend.php +++ b/lib/ClustersBackend/FaceRecognitionBackend.php @@ -123,7 +123,7 @@ class FaceRecognitionBackend extends Backend unset($row['face_x'], $row['face_y'], $row['face_w'], $row['face_h'], $row['image_height'], $row['image_width']); } - public function getClusters(int $fileid = 0): array + protected function getClustersI(int $fileid = 0): array { if ($fileid) { throw new \Exception('FaceRecognitionBackend: fileid filter not implemented'); diff --git a/lib/ClustersBackend/PlacesBackend.php b/lib/ClustersBackend/PlacesBackend.php index 91afa2f9..ee2fdf69 100644 --- a/lib/ClustersBackend/PlacesBackend.php +++ b/lib/ClustersBackend/PlacesBackend.php @@ -63,7 +63,7 @@ class PlacesBackend extends Backend )); } - public function getClusters(int $fileid = 0): array + protected function getClustersI(int $fileid = 0): array { if ($fileid) { throw new \Exception('PlacesBackend: fileid filter not implemented'); diff --git a/lib/ClustersBackend/RecognizeBackend.php b/lib/ClustersBackend/RecognizeBackend.php index f053ecb6..9bbc8434 100644 --- a/lib/ClustersBackend/RecognizeBackend.php +++ b/lib/ClustersBackend/RecognizeBackend.php @@ -129,7 +129,7 @@ class RecognizeBackend extends Backend unset($row['face_w'], $row['face_h'], $row['face_x'], $row['face_y']); } - public function getClusters(int $fileid = 0): array + protected function getClustersI(int $fileid = 0): array { $query = $this->tq->getBuilder(); diff --git a/lib/ClustersBackend/TagsBackend.php b/lib/ClustersBackend/TagsBackend.php index 0d02feb7..302d0b12 100644 --- a/lib/ClustersBackend/TagsBackend.php +++ b/lib/ClustersBackend/TagsBackend.php @@ -67,7 +67,7 @@ class TagsBackend extends Backend )); } - public function getClusters(int $fileid = 0): array + protected function getClustersI(int $fileid = 0): array { if ($fileid) { throw new \Exception('TagsBackend: fileid filter not implemented'); diff --git a/lib/Controller/ClustersController.php b/lib/Controller/ClustersController.php index b7fe935d..3f128665 100644 --- a/lib/Controller/ClustersController.php +++ b/lib/Controller/ClustersController.php @@ -47,12 +47,6 @@ class ClustersController extends GenericApiController $list = $this->backend->getClusters($fileid); - // Set cluster_id and cluster_type for each cluster - foreach ($list as &$cluster) { - $cluster['cluster_id'] = $this->backend->getClusterId($cluster); - $cluster['cluster_type'] = $this->backend->clusterType(); - } - return new JSONResponse($list, Http::STATUS_OK); }); } diff --git a/lib/Controller/ImageController.php b/lib/Controller/ImageController.php index 1b948119..c90c61ee 100644 --- a/lib/Controller/ImageController.php +++ b/lib/Controller/ImageController.php @@ -180,31 +180,19 @@ class ImageController extends GenericApiController int $id, bool $basic = false, bool $current = false, - bool $tags = false + bool $tags = false, + string $clusters = '' ): Http\Response { - return Util::guardEx(function () use ($id, $basic, $current, $tags) { + return Util::guardEx(function () use ($id, $basic, $current, $tags, $clusters) { $file = $this->fs->getUserFile($id); // Get the image info - $info = $this->timelineQuery->getInfoById($file->getId(), $basic); + $info = $this->timelineQuery->getInfoById($id, $basic); // Add fileid and etag $info['fileid'] = $file->getId(); $info['etag'] = $file->getEtag(); - // Allow these ony for logged in users - if (null !== $this->userSession->getUser()) { - // Get list of tags for this file - if ($tags) { - $info['tags'] = $this->getTags($file->getId()); - } - - // Get latest exif data if requested - if ($current) { - $info['current'] = Exif::getExifFromFile($file); - } - } - // Inject permissions and convert to string $info['permissions'] = \OCA\Memories\Util::permissionsToStr($file->getPermissions()); @@ -213,6 +201,31 @@ class ImageController extends GenericApiController $info['size'] = $file->getSize(); $info['basename'] = $file->getName(); + // Allow these ony for logged in users + if (null !== $this->userSession->getUser()) { + // Get list of tags for this file + if ($tags) { + $info['tags'] = $this->getTags($id); + } + + // Get latest exif data if requested + if ($current) { + $info['current'] = Exif::getExifFromFile($file); + } + + // Get clusters for this file + if ($clusters) { + $clist = []; + foreach (explode(',', $clusters) as $type) { + $backend = \OC::$server->get(\OCA\Memories\ClustersBackend\Manager::class)->get($type); + if ($backend->isEnabled()) { + $clist[$type] = $backend->getClusters($id); + } + } + $info['clusters'] = $clist; + } + } + return new JSONResponse($info, Http::STATUS_OK); }); } diff --git a/src/components/Metadata.vue b/src/components/Metadata.vue index 0d8a9359..ac860e6d 100644 --- a/src/components/Metadata.vue +++ b/src/components/Metadata.vue @@ -110,28 +110,23 @@ export default defineComponent({ mixins: [UserConfig], data: () => ({ - // Basic info and metadata fileid: null as number | null, filename: '', exif: {} as { [prop: string]: any }, baseInfo: {} as IImageInfo, - // Cluster lists - albums: [] as IAlbum[], - people: [] as IFace[], - loading: 0, state: 0, }), mounted() { subscribe('files:file:updated', this.handleFileUpdated); - subscribe('memories:albums:update', this.refreshAlbums); + subscribe('memories:albums:update', this.refresh); }, beforeDestroy() { unsubscribe('files:file:updated', this.handleFileUpdated); - unsubscribe('memories:albums:update', this.refreshAlbums); + unsubscribe('memories:albums:update', this.refresh); }, computed: { @@ -351,6 +346,14 @@ export default defineComponent({ mapFullUrl(): string { return `https://www.openstreetmap.org/?mlat=${this.lat}&mlon=${this.lon}#map=18/${this.lat}/${this.lon}`; }, + + albums(): IAlbum[] { + return this.baseInfo?.clusters?.albums ?? []; + }, + + people(): IFace[] { + return this.baseInfo?.clusters?.recognize ?? []; + }, }, methods: { @@ -359,35 +362,34 @@ export default defineComponent({ this.loading = 0; this.fileid = null; this.exif = {}; - this.albums = []; - this.people = []; - const url = API.Q(utils.getImageInfoUrl(photo), { tags: 1 }); + // which clusters to get + const clusters = [ + this.config.albums_enabled ? 'albums' : null, + this.config.recognize_enabled ? 'recognize' : null, + ] + .filter((c) => c) + .join(','); + + // get tags if enabled + const tags = this.config.systemtags_enabled ? 1 : 0; + + // get image info + const url = API.Q(utils.getImageInfoUrl(photo), { tags, clusters }); const res = await this.guardState(axios.get(url)); if (!res) return null; + // unwrap basic info this.fileid = res.data.fileid; this.filename = res.data.basename; this.exif = res.data.exif || {}; this.baseInfo = res.data; - // trigger other refreshes - if (!utils.isLocalPhoto(photo)) { - this.refreshAlbums(); - this.refreshPeople(); - } - return this.baseInfo; }, - async refreshAlbums(): Promise { - if (!this.config.albums_enabled) return; - this.albums = (await this.guardState(dav.getAlbums(1, this.fileid!))) ?? this.albums; - }, - - async refreshPeople(): Promise { - if (!this.config.recognize_enabled) return; - this.people = (await this.guardState(dav.getFaceList('recognize', this.fileid!))) ?? this.people; + async refresh() { + if (this.fileid) await this.update(this.fileid); }, async guardState(promise: Promise): Promise { @@ -406,7 +408,7 @@ export default defineComponent({ handleFileUpdated({ fileid }: { fileid: number }) { if (fileid && this.fileid === fileid) { - this.update(this.fileid); + this.refresh(); } }, }, diff --git a/src/services/API.ts b/src/services/API.ts index b07d1423..ece4689a 100644 --- a/src/services/API.ts +++ b/src/services/API.ts @@ -84,8 +84,8 @@ export class API { return tok(gen(`${BASE}/folders/sub`)); } - static ALBUM_LIST(fileid?: number) { - return API.Q(gen(`${BASE}/clusters/albums`), { fileid }); + static ALBUM_LIST() { + return gen(`${BASE}/clusters/albums`); } static ALBUM_DOWNLOAD(user: string, name: string) { @@ -106,8 +106,8 @@ export class API { return gen(`${BASE}/tags/set/{fileid}`, { fileid }); } - static FACE_LIST(app: 'recognize' | 'facerecognition', fileid?: number) { - return API.Q(gen(`${BASE}/clusters/${app}`), { fileid }); + static FACE_LIST(app: 'recognize' | 'facerecognition') { + return gen(`${BASE}/clusters/${app}`); } static CLUSTER_PREVIEW(backend: ClusterTypes, name: string | number) { diff --git a/src/services/dav/albums.ts b/src/services/dav/albums.ts index 0efedb8c..a677f464 100644 --- a/src/services/dav/albums.ts +++ b/src/services/dav/albums.ts @@ -26,7 +26,9 @@ export function getAlbumPath(user: string, name: string) { * @param fileid Optional file ID to get albums for */ export async function getAlbums(sort: 1 | 2 = 1, fileid?: number) { - const data = (await axios.get(API.ALBUM_LIST(fileid))).data; + const url = API.Q(API.ALBUM_LIST(), { fileid }); + const res = await axios.get(url); + const data = res.data; // Sort the response switch (sort) { diff --git a/src/services/dav/face.ts b/src/services/dav/face.ts index ed40082d..b87ebc93 100644 --- a/src/services/dav/face.ts +++ b/src/services/dav/face.ts @@ -11,10 +11,9 @@ import * as base from './base'; /** * Get list of faces * @param app Backend app to use - * @param fileid File to filter by (optional) */ -export async function getFaceList(app: 'recognize' | 'facerecognition', fileid?: number) { - return (await axios.get(API.FACE_LIST(app, fileid))).data; +export async function getFaceList(app: 'recognize' | 'facerecognition') { + return (await axios.get(API.FACE_LIST(app))).data; } /** diff --git a/src/types.ts b/src/types.ts index 89863a50..39944611 100644 --- a/src/types.ts +++ b/src/types.ts @@ -105,6 +105,11 @@ export interface IImageInfo { Description?: string; [other: string]: unknown; }; + + clusters?: { + albums?: IAlbum[]; + recognize?: IFace[]; + }; } export interface IFolder extends IPhoto {