Omit joins that are only used for sorting when skipping sorting

Should provide some marginal improvement on systems with a lot of items.
This commit is contained in:
WithoutPants 2025-12-05 17:19:43 +11:00
parent 7d96169796
commit ff360ba5b1
7 changed files with 50 additions and 13 deletions

View file

@ -96,6 +96,9 @@ type join struct {
onClause string onClause string
joinType string joinType string
args []interface{} args []interface{}
// if true, indicates this is required for sorting only
sort bool
} }
// equals returns true if the other join alias/table is equal to this one // equals returns true if the other join alias/table is equal to this one
@ -131,9 +134,13 @@ type joins []join
// returns true if added // returns true if added
func (j *joins) addUnique(newJoin join) bool { func (j *joins) addUnique(newJoin join) bool {
found := false found := false
for _, jj := range *j { for i, jj := range *j {
if jj.equals(newJoin) { if jj.equals(newJoin) {
found = true found = true
// if sort is false on the new join, but true on the existing, set the false
if !newJoin.sort && jj.sort {
(*j)[i].sort = false
}
break break
} }
} }
@ -151,13 +158,17 @@ func (j *joins) add(newJoins ...join) {
} }
} }
func (j *joins) toSQL() string { func (j *joins) toSQL(includeSortPagination bool) string {
if len(*j) == 0 { if len(*j) == 0 {
return "" return ""
} }
var ret []string var ret []string
for _, jj := range *j { for _, jj := range *j {
// skip sort-only joins if not including sort/pagination
if !includeSortPagination && jj.sort {
continue
}
ret = append(ret, jj.toSQL()) ret = append(ret, jj.toSQL())
} }

View file

@ -800,10 +800,12 @@ func (qb *GalleryStore) setGallerySort(query *queryBuilder, findFilter *models.F
addFileTable := func() { addFileTable := func() {
query.addJoins( query.addJoins(
join{ join{
sort: true,
table: galleriesFilesTable, table: galleriesFilesTable,
onClause: "galleries_files.gallery_id = galleries.id", onClause: "galleries_files.gallery_id = galleries.id",
}, },
join{ join{
sort: true,
table: fileTable, table: fileTable,
onClause: "galleries_files.file_id = files.id", onClause: "galleries_files.file_id = files.id",
}, },
@ -813,10 +815,12 @@ func (qb *GalleryStore) setGallerySort(query *queryBuilder, findFilter *models.F
addFolderTable := func() { addFolderTable := func() {
query.addJoins( query.addJoins(
join{ join{
sort: true,
table: folderTable, table: folderTable,
onClause: "folders.id = galleries.folder_id", onClause: "folders.id = galleries.folder_id",
}, },
join{ join{
sort: true,
table: folderTable, table: folderTable,
as: "file_folder", as: "file_folder",
onClause: "files.parent_folder_id = file_folder.id", onClause: "files.parent_folder_id = file_folder.id",

View file

@ -518,7 +518,7 @@ func (qb *GroupStore) setGroupSort(query *queryBuilder, findFilter *models.FindF
} else { } else {
// this will give unexpected results if the query is not filtered by a parent group and // this will give unexpected results if the query is not filtered by a parent group and
// the group has multiple parents and order indexes // the group has multiple parents and order indexes
query.join(groupRelationsTable, "", "groups.id = groups_relations.sub_id") query.joinSort(groupRelationsTable, "", "groups.id = groups_relations.sub_id")
query.sortAndPagination += getSort("order_index", direction, groupRelationsTable) query.sortAndPagination += getSort("order_index", direction, groupRelationsTable)
} }
case "tag_count": case "tag_count":

View file

@ -965,10 +965,12 @@ func (qb *ImageStore) setImageSortAndPagination(q *queryBuilder, findFilter *mod
addFilesJoin := func() { addFilesJoin := func() {
q.addJoins( q.addJoins(
join{ join{
sort: true,
table: imagesFilesTable, table: imagesFilesTable,
onClause: "images_files.image_id = images.id", onClause: "images_files.image_id = images.id",
}, },
join{ join{
sort: true,
table: fileTable, table: fileTable,
onClause: "images_files.file_id = files.id", onClause: "images_files.file_id = files.id",
}, },
@ -977,6 +979,7 @@ func (qb *ImageStore) setImageSortAndPagination(q *queryBuilder, findFilter *mod
addFolderJoin := func() { addFolderJoin := func() {
q.addJoins(join{ q.addJoins(join{
sort: true,
table: folderTable, table: folderTable,
onClause: "files.parent_folder_id = folders.id", onClause: "files.parent_folder_id = folders.id",
}) })

View file

@ -24,8 +24,8 @@ type queryBuilder struct {
sortAndPagination string sortAndPagination string
} }
func (qb queryBuilder) body() string { func (qb queryBuilder) body(includeSortPagination bool) string {
return fmt.Sprintf("SELECT %s FROM %s%s", strings.Join(qb.columns, ", "), qb.from, qb.joins.toSQL()) return fmt.Sprintf("SELECT %s FROM %s%s", strings.Join(qb.columns, ", "), qb.from, qb.joins.toSQL(includeSortPagination))
} }
func (qb *queryBuilder) addColumn(column string) { func (qb *queryBuilder) addColumn(column string) {
@ -33,7 +33,7 @@ func (qb *queryBuilder) addColumn(column string) {
} }
func (qb queryBuilder) toSQL(includeSortPagination bool) string { func (qb queryBuilder) toSQL(includeSortPagination bool) string {
body := qb.body() body := qb.body(includeSortPagination)
withClause := "" withClause := ""
if len(qb.withClauses) > 0 { if len(qb.withClauses) > 0 {
@ -59,12 +59,14 @@ func (qb queryBuilder) findIDs(ctx context.Context) ([]int, error) {
} }
func (qb queryBuilder) executeFind(ctx context.Context) ([]int, int, error) { func (qb queryBuilder) executeFind(ctx context.Context) ([]int, int, error) {
body := qb.body() const includeSortPagination = true
body := qb.body(includeSortPagination)
return qb.repository.executeFindQuery(ctx, body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses, qb.withClauses, qb.recursiveWith) return qb.repository.executeFindQuery(ctx, body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses, qb.withClauses, qb.recursiveWith)
} }
func (qb queryBuilder) executeCount(ctx context.Context) (int, error) { func (qb queryBuilder) executeCount(ctx context.Context) (int, error) {
body := qb.body() const includeSortPagination = false
body := qb.body(includeSortPagination)
withClause := "" withClause := ""
if len(qb.withClauses) > 0 { if len(qb.withClauses) > 0 {
@ -131,6 +133,18 @@ func (qb *queryBuilder) join(table, as, onClause string) {
qb.joins.add(newJoin) qb.joins.add(newJoin)
} }
func (qb *queryBuilder) joinSort(table, as, onClause string) {
newJoin := join{
sort: true,
table: table,
as: as,
onClause: onClause,
joinType: "LEFT",
}
qb.joins.add(newJoin)
}
func (qb *queryBuilder) addJoins(joins ...join) { func (qb *queryBuilder) addJoins(joins ...join) {
for _, j := range joins { for _, j := range joins {
if qb.joins.addUnique(j) { if qb.joins.addUnique(j) {

View file

@ -1157,10 +1157,12 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
addFileTable := func() { addFileTable := func() {
query.addJoins( query.addJoins(
join{ join{
sort: true,
table: scenesFilesTable, table: scenesFilesTable,
onClause: "scenes_files.scene_id = scenes.id", onClause: "scenes_files.scene_id = scenes.id",
}, },
join{ join{
sort: true,
table: fileTable, table: fileTable,
onClause: "scenes_files.file_id = files.id", onClause: "scenes_files.file_id = files.id",
}, },
@ -1171,6 +1173,7 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
addFileTable() addFileTable()
query.addJoins( query.addJoins(
join{ join{
sort: true,
table: videoFileTable, table: videoFileTable,
onClause: "video_files.file_id = scenes_files.file_id", onClause: "video_files.file_id = scenes_files.file_id",
}, },
@ -1180,6 +1183,7 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
addFolderTable := func() { addFolderTable := func() {
query.addJoins( query.addJoins(
join{ join{
sort: true,
table: folderTable, table: folderTable,
onClause: "files.parent_folder_id = folders.id", onClause: "files.parent_folder_id = folders.id",
}, },
@ -1189,10 +1193,10 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
direction := findFilter.GetDirection() direction := findFilter.GetDirection()
switch sort { switch sort {
case "movie_scene_number": case "movie_scene_number":
query.join(groupsScenesTable, "", "scenes.id = groups_scenes.scene_id") query.joinSort(groupsScenesTable, "", "scenes.id = groups_scenes.scene_id")
query.sortAndPagination += getSort("scene_index", direction, groupsScenesTable) query.sortAndPagination += getSort("scene_index", direction, groupsScenesTable)
case "group_scene_number": case "group_scene_number":
query.join(groupsScenesTable, "scene_group", "scenes.id = scene_group.scene_id") query.joinSort(groupsScenesTable, "scene_group", "scenes.id = scene_group.scene_id")
query.sortAndPagination += getSort("scene_index", direction, "scene_group") query.sortAndPagination += getSort("scene_index", direction, "scene_group")
case "tag_count": case "tag_count":
query.sortAndPagination += getCountSort(sceneTable, scenesTagsTable, sceneIDColumn, direction) query.sortAndPagination += getCountSort(sceneTable, scenesTagsTable, sceneIDColumn, direction)
@ -1210,6 +1214,7 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
addFileTable() addFileTable()
query.addJoins( query.addJoins(
join{ join{
sort: true,
table: fingerprintTable, table: fingerprintTable,
as: "fingerprints_phash", as: "fingerprints_phash",
onClause: "scenes_files.file_id = fingerprints_phash.file_id AND fingerprints_phash.type = 'phash'", onClause: "scenes_files.file_id = fingerprints_phash.file_id AND fingerprints_phash.type = 'phash'",
@ -1274,7 +1279,7 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
getSortDirection(direction), getSortDirection(direction),
) )
case "studio": case "studio":
query.join(studioTable, "", "scenes.studio_id = studios.id") query.joinSort(studioTable, "", "scenes.studio_id = studios.id")
query.sortAndPagination += getSort("name", direction, studioTable) query.sortAndPagination += getSort("name", direction, studioTable)
default: default:
query.sortAndPagination += getSort(sort, direction, "scenes") query.sortAndPagination += getSort(sort, direction, "scenes")

View file

@ -392,10 +392,10 @@ func (qb *SceneMarkerStore) setSceneMarkerSort(query *queryBuilder, findFilter *
switch sort { switch sort {
case "scenes_updated_at": case "scenes_updated_at":
sort = "updated_at" sort = "updated_at"
query.join(sceneTable, "", "scenes.id = scene_markers.scene_id") query.joinSort(sceneTable, "", "scenes.id = scene_markers.scene_id")
query.sortAndPagination += getSort(sort, direction, sceneTable) query.sortAndPagination += getSort(sort, direction, sceneTable)
case "title": case "title":
query.join(tagTable, "", "scene_markers.primary_tag_id = tags.id") query.joinSort(tagTable, "", "scene_markers.primary_tag_id = tags.id")
query.sortAndPagination += " ORDER BY COALESCE(NULLIF(scene_markers.title,''), tags.name) COLLATE NATURAL_CI " + direction query.sortAndPagination += " ORDER BY COALESCE(NULLIF(scene_markers.title,''), tags.name) COLLATE NATURAL_CI " + direction
case "duration": case "duration":
sort = "(scene_markers.end_seconds - scene_markers.seconds)" sort = "(scene_markers.end_seconds - scene_markers.seconds)"