From f58168719850b3525d4b5223b5a3284b7c4f52ec Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:00:51 +1100 Subject: [PATCH] Improve performance of gallery and image queries (#2422) * Revert to use FindByGalleryID in gallery resolver * Sort by path in FindByGalleryID * Optimise queries --- internal/api/resolver_model_gallery.go | 10 +++++++--- pkg/sqlite/gallery.go | 2 +- pkg/sqlite/image.go | 11 ++++++++--- pkg/sqlite/scene.go | 4 ++-- pkg/sqlite/sql.go | 15 ++++++++++++--- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/internal/api/resolver_model_gallery.go b/internal/api/resolver_model_gallery.go index 9e5306cbd..911cb8fe1 100644 --- a/internal/api/resolver_model_gallery.go +++ b/internal/api/resolver_model_gallery.go @@ -28,7 +28,11 @@ func (r *galleryResolver) Images(ctx context.Context, obj *models.Gallery) (ret var err error // #2376 - sort images by path - ret, err = image.FindByGalleryID(repo.Image(), obj.ID, "path", models.SortDirectionEnumAsc) + // doing this via Query is really slow, so stick with FindByGalleryID + ret, err = repo.Image().FindByGalleryID(obj.ID) + if err != nil { + return err + } return err }); err != nil { @@ -40,8 +44,8 @@ func (r *galleryResolver) Images(ctx context.Context, obj *models.Gallery) (ret func (r *galleryResolver) Cover(ctx context.Context, obj *models.Gallery) (ret *models.Image, err error) { if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error { - // #2376 - use first image (sorted by path) if no cover is present - imgs, err := image.FindByGalleryID(repo.Image(), obj.ID, "path", models.SortDirectionEnumAsc) + // doing this via Query is really slow, so stick with FindByGalleryID + imgs, err := repo.Image().FindByGalleryID(obj.ID) if err != nil { return err } diff --git a/pkg/sqlite/gallery.go b/pkg/sqlite/gallery.go index 640006318..18512d5f0 100644 --- a/pkg/sqlite/gallery.go +++ b/pkg/sqlite/gallery.go @@ -135,7 +135,7 @@ func (qb *galleryQueryBuilder) FindBySceneID(sceneID int) ([]*models.Gallery, er func (qb *galleryQueryBuilder) FindByImageID(imageID int) ([]*models.Gallery, error) { query := selectAll(galleryTable) + ` - LEFT JOIN galleries_images as images_join on images_join.gallery_id = galleries.id + INNER JOIN galleries_images as images_join on images_join.gallery_id = galleries.id WHERE images_join.image_id = ? GROUP BY galleries.id ` diff --git a/pkg/sqlite/image.go b/pkg/sqlite/image.go index 6ff57e5a9..d2b3adb8f 100644 --- a/pkg/sqlite/image.go +++ b/pkg/sqlite/image.go @@ -168,7 +168,12 @@ func (qb *imageQueryBuilder) FindByPath(path string) (*models.Image, error) { func (qb *imageQueryBuilder) FindByGalleryID(galleryID int) ([]*models.Image, error) { args := []interface{}{galleryID} - return qb.queryImages(imagesForGalleryQuery+qb.getImageSort(nil), args) + sort := "path" + sortDir := models.SortDirectionEnumAsc + return qb.queryImages(imagesForGalleryQuery+qb.getImageSort(&models.FindFilterType{ + Sort: &sort, + Direction: &sortDir, + }), args) } func (qb *imageQueryBuilder) CountByGalleryID(galleryID int) (int, error) { @@ -413,8 +418,8 @@ func imageTagCountCriterionHandler(qb *imageQueryBuilder, tagCount *models.IntCr func imageGalleriesCriterionHandler(qb *imageQueryBuilder, galleries *models.MultiCriterionInput) criterionHandlerFunc { addJoinsFunc := func(f *filterBuilder) { - qb.galleriesRepository().join(f, "galleries_join", "images.id") - f.addLeftJoin(galleryTable, "", "galleries_join.gallery_id = galleries.id") + qb.galleriesRepository().join(f, "", "images.id") + f.addLeftJoin(galleryTable, "", "galleries_images.gallery_id = galleries.id") } h := qb.getMultiCriterionHandlerBuilder(galleryTable, galleriesImagesTable, galleryIDColumn, addJoinsFunc) diff --git a/pkg/sqlite/scene.go b/pkg/sqlite/scene.go index 6215e9f7b..054e82266 100644 --- a/pkg/sqlite/scene.go +++ b/pkg/sqlite/scene.go @@ -713,8 +713,8 @@ func sceneStudioCriterionHandler(qb *sceneQueryBuilder, studios *models.Hierarch func sceneMoviesCriterionHandler(qb *sceneQueryBuilder, movies *models.MultiCriterionInput) criterionHandlerFunc { addJoinsFunc := func(f *filterBuilder) { - qb.moviesRepository().join(f, "movies_join", "scenes.id") - f.addLeftJoin("movies", "", "movies_join.movie_id = movies.id") + qb.moviesRepository().join(f, "", "scenes.id") + f.addLeftJoin("movies", "", "movies_scenes.movie_id = movies.id") } h := qb.getMultiCriterionHandlerBuilder(movieTable, moviesScenesTable, "movie_id", addJoinsFunc) return h.handler(movies) diff --git a/pkg/sqlite/sql.go b/pkg/sqlite/sql.go index dd7805676..a83612b0b 100644 --- a/pkg/sqlite/sql.go +++ b/pkg/sqlite/sql.go @@ -194,11 +194,20 @@ func getMultiCriterionClause(primaryTable, foreignTable, joinTable, primaryFK, f switch criterion.Modifier { case models.CriterionModifierIncludes: // includes any of the provided ids - whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) + if joinTable != "" { + whereClause = joinTable + "." + foreignFK + " IN " + getInBinding(len(criterion.Value)) + } else { + whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) + } case models.CriterionModifierIncludesAll: // includes all of the provided ids - whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) - havingClause = "count(distinct " + foreignTable + ".id) IS " + strconv.Itoa(len(criterion.Value)) + if joinTable != "" { + whereClause = joinTable + "." + foreignFK + " IN " + getInBinding(len(criterion.Value)) + havingClause = "count(distinct " + joinTable + "." + foreignFK + ") IS " + strconv.Itoa(len(criterion.Value)) + } else { + whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) + havingClause = "count(distinct " + foreignTable + ".id) IS " + strconv.Itoa(len(criterion.Value)) + } case models.CriterionModifierExcludes: // excludes all of the provided ids if joinTable != "" {