mirror of https://github.com/stashapp/stash.git
Use inner join when getting images in a gallery (#2083)
* Added joinType to join struct * Added addInnerJoin function to perform INNER JOIN type of joins * Added innerJoin function to perform INNER JOIN type of joins * Use inner joins when querying images in a gallery * Renamed addJoin to addLeftJoin
This commit is contained in:
parent
2460664dc3
commit
70d9a05580
|
@ -39,6 +39,7 @@ type join struct {
|
|||
table string
|
||||
as string
|
||||
onClause string
|
||||
joinType string
|
||||
}
|
||||
|
||||
// equals returns true if the other join alias/table is equal to this one
|
||||
|
@ -57,11 +58,15 @@ func (j join) alias() string {
|
|||
|
||||
func (j join) toSQL() string {
|
||||
asStr := ""
|
||||
joinStr := j.joinType
|
||||
if j.as != "" && j.as != j.table {
|
||||
asStr = " AS " + j.as
|
||||
}
|
||||
if j.joinType == "" {
|
||||
joinStr = "LEFT"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("LEFT JOIN %s%s ON %s", j.table, asStr, j.onClause)
|
||||
return fmt.Sprintf("%s JOIN %s%s ON %s", joinStr, j.table, asStr, j.onClause)
|
||||
}
|
||||
|
||||
type joins []join
|
||||
|
@ -154,16 +159,33 @@ func (f *filterBuilder) not(n *filterBuilder) {
|
|||
f.subFilterOp = notOp
|
||||
}
|
||||
|
||||
// addJoin adds a join to the filter. The join is expressed in SQL as:
|
||||
// addLeftJoin adds a left join to the filter. The join is expressed in SQL as:
|
||||
// LEFT JOIN <table> [AS <as>] ON <onClause>
|
||||
// The AS is omitted if as is empty.
|
||||
// This method does not add a join if it its alias/table name is already
|
||||
// present in another existing join.
|
||||
func (f *filterBuilder) addJoin(table, as, onClause string) {
|
||||
func (f *filterBuilder) addLeftJoin(table, as, onClause string) {
|
||||
newJoin := join{
|
||||
table: table,
|
||||
as: as,
|
||||
onClause: onClause,
|
||||
joinType: "LEFT",
|
||||
}
|
||||
|
||||
f.joins.add(newJoin)
|
||||
}
|
||||
|
||||
// addInnerJoin adds an inner join to the filter. The join is expressed in SQL as:
|
||||
// INNER JOIN <table> [AS <as>] ON <onClause>
|
||||
// The AS is omitted if as is empty.
|
||||
// This method does not add a join if it its alias/table name is already
|
||||
// present in another existing join.
|
||||
func (f *filterBuilder) addInnerJoin(table, as, onClause string) {
|
||||
newJoin := join{
|
||||
table: table,
|
||||
as: as,
|
||||
onClause: onClause,
|
||||
joinType: "INNER",
|
||||
}
|
||||
|
||||
f.joins.add(newJoin)
|
||||
|
@ -505,7 +527,7 @@ func (m *multiCriterionHandlerBuilder) handler(criterion *models.MultiCriterionI
|
|||
table := m.primaryTable
|
||||
if m.joinTable != "" {
|
||||
table = m.joinTable
|
||||
f.addJoin(table, "", fmt.Sprintf("%s.%s = %s.id", table, m.primaryFK, m.primaryTable))
|
||||
f.addLeftJoin(table, "", fmt.Sprintf("%s.%s = %s.id", table, m.primaryFK, m.primaryTable))
|
||||
}
|
||||
|
||||
f.addWhere(fmt.Sprintf("%s.%s IS %s NULL", table, m.foreignFK, notClause))
|
||||
|
@ -698,7 +720,7 @@ func (m *hierarchicalMultiCriterionHandlerBuilder) handler(criterion *models.Hie
|
|||
|
||||
valuesClause := getHierarchicalValues(m.tx, criterion.Value, m.foreignTable, m.relationsTable, m.parentFK, criterion.Depth)
|
||||
|
||||
f.addJoin("(SELECT column1 AS root_id, column2 AS item_id FROM ("+valuesClause+"))", m.derivedTable, fmt.Sprintf("%s.item_id = %s.%s", m.derivedTable, m.primaryTable, m.foreignFK))
|
||||
f.addLeftJoin("(SELECT column1 AS root_id, column2 AS item_id FROM ("+valuesClause+"))", m.derivedTable, fmt.Sprintf("%s.item_id = %s.%s", m.derivedTable, m.primaryTable, m.foreignFK))
|
||||
|
||||
addHierarchicalConditionClauses(f, criterion, m.derivedTable, "root_id")
|
||||
}
|
||||
|
@ -731,7 +753,7 @@ func (m *joinedHierarchicalMultiCriterionHandlerBuilder) handler(criterion *mode
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin(m.joinTable, joinAlias, fmt.Sprintf("%s.%s = %s.id", joinAlias, m.primaryFK, m.primaryTable))
|
||||
f.addLeftJoin(m.joinTable, joinAlias, fmt.Sprintf("%s.%s = %s.id", joinAlias, m.primaryFK, m.primaryTable))
|
||||
|
||||
f.addWhere(utils.StrFormat("{table}.{column} IS {not} NULL", utils.StrFormatMap{
|
||||
"table": joinAlias,
|
||||
|
@ -757,7 +779,7 @@ func (m *joinedHierarchicalMultiCriterionHandlerBuilder) handler(criterion *mode
|
|||
"valuesClause": valuesClause,
|
||||
})
|
||||
|
||||
f.addJoin(joinTable, joinAlias, fmt.Sprintf("%s.%s = %s.id", joinAlias, m.primaryFK, m.primaryTable))
|
||||
f.addLeftJoin(joinTable, joinAlias, fmt.Sprintf("%s.%s = %s.id", joinAlias, m.primaryFK, m.primaryTable))
|
||||
|
||||
addHierarchicalConditionClauses(f, criterion, joinAlias, "root_id")
|
||||
}
|
||||
|
|
|
@ -152,36 +152,36 @@ func TestAddJoin(t *testing.T) {
|
|||
onClause = "onClause1"
|
||||
)
|
||||
|
||||
f.addJoin(table1Name, as1Name, onClause)
|
||||
f.addLeftJoin(table1Name, as1Name, onClause)
|
||||
|
||||
// ensure join is added
|
||||
assert.Len(f.joins, 1)
|
||||
assert.Equal(fmt.Sprintf("LEFT JOIN %s AS %s ON %s", table1Name, as1Name, onClause), f.joins[0].toSQL())
|
||||
|
||||
// ensure join with same as is not added
|
||||
f.addJoin(table2Name, as1Name, onClause)
|
||||
f.addLeftJoin(table2Name, as1Name, onClause)
|
||||
assert.Len(f.joins, 1)
|
||||
|
||||
// ensure same table with different alias can be added
|
||||
f.addJoin(table1Name, as2Name, onClause)
|
||||
f.addLeftJoin(table1Name, as2Name, onClause)
|
||||
assert.Len(f.joins, 2)
|
||||
assert.Equal(fmt.Sprintf("LEFT JOIN %s AS %s ON %s", table1Name, as2Name, onClause), f.joins[1].toSQL())
|
||||
|
||||
// ensure table without alias can be added if tableName != existing alias/tableName
|
||||
f.addJoin(table1Name, "", onClause)
|
||||
f.addLeftJoin(table1Name, "", onClause)
|
||||
assert.Len(f.joins, 3)
|
||||
assert.Equal(fmt.Sprintf("LEFT JOIN %s ON %s", table1Name, onClause), f.joins[2].toSQL())
|
||||
|
||||
// ensure table with alias == table name of a join without alias is not added
|
||||
f.addJoin(table2Name, table1Name, onClause)
|
||||
f.addLeftJoin(table2Name, table1Name, onClause)
|
||||
assert.Len(f.joins, 3)
|
||||
|
||||
// ensure table without alias cannot be added if tableName == existing alias
|
||||
f.addJoin(as2Name, "", onClause)
|
||||
f.addLeftJoin(as2Name, "", onClause)
|
||||
assert.Len(f.joins, 3)
|
||||
|
||||
// ensure AS is not used if same as table name
|
||||
f.addJoin(table2Name, table2Name, onClause)
|
||||
f.addLeftJoin(table2Name, table2Name, onClause)
|
||||
assert.Len(f.joins, 4)
|
||||
assert.Equal(fmt.Sprintf("LEFT JOIN %s ON %s", table2Name, onClause), f.joins[3].toSQL())
|
||||
}
|
||||
|
@ -407,7 +407,7 @@ func TestGetAllJoins(t *testing.T) {
|
|||
onClause = "onClause1"
|
||||
)
|
||||
|
||||
f.addJoin(table1Name, as1Name, onClause)
|
||||
f.addLeftJoin(table1Name, as1Name, onClause)
|
||||
|
||||
// ensure join is returned
|
||||
joins := f.getAllJoins()
|
||||
|
@ -417,14 +417,14 @@ func TestGetAllJoins(t *testing.T) {
|
|||
// ensure joins in sub-filter are returned
|
||||
subFilter := &filterBuilder{}
|
||||
f.and(subFilter)
|
||||
subFilter.addJoin(table2Name, as2Name, onClause)
|
||||
subFilter.addLeftJoin(table2Name, as2Name, onClause)
|
||||
|
||||
joins = f.getAllJoins()
|
||||
assert.Len(joins, 2)
|
||||
assert.Equal(fmt.Sprintf("LEFT JOIN %s AS %s ON %s", table2Name, as2Name, onClause), joins[1].toSQL())
|
||||
|
||||
// ensure redundant joins are not returned
|
||||
subFilter.addJoin(as1Name, "", onClause)
|
||||
subFilter.addLeftJoin(as1Name, "", onClause)
|
||||
joins = f.getAllJoins()
|
||||
assert.Len(joins, 2)
|
||||
}
|
||||
|
|
|
@ -290,7 +290,7 @@ func galleryIsMissingCriterionHandler(qb *galleryQueryBuilder, isMissing *string
|
|||
if isMissing != nil && *isMissing != "" {
|
||||
switch *isMissing {
|
||||
case "scenes":
|
||||
f.addJoin("scenes_galleries", "scenes_join", "scenes_join.gallery_id = galleries.id")
|
||||
f.addLeftJoin("scenes_galleries", "scenes_join", "scenes_join.gallery_id = galleries.id")
|
||||
f.addWhere("scenes_join.gallery_id IS NULL")
|
||||
case "studio":
|
||||
f.addWhere("galleries.studio_id IS NULL")
|
||||
|
@ -395,8 +395,8 @@ func galleryPerformerTagsCriterionHandler(qb *galleryQueryBuilder, tags *models.
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("performers_galleries", "", "galleries.id = performers_galleries.gallery_id")
|
||||
f.addJoin("performers_tags", "", "performers_galleries.performer_id = performers_tags.performer_id")
|
||||
f.addLeftJoin("performers_galleries", "", "galleries.id = performers_galleries.gallery_id")
|
||||
f.addLeftJoin("performers_tags", "", "performers_galleries.performer_id = performers_tags.performer_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("performers_tags.tag_id IS %s NULL", notClause))
|
||||
return
|
||||
|
@ -414,7 +414,7 @@ INNER JOIN performers_tags pt ON pt.performer_id = pg.performer_id
|
|||
INNER JOIN (` + valuesClause + `) t ON t.column2 = pt.tag_id
|
||||
)`)
|
||||
|
||||
f.addJoin("performer_tags", "", "performer_tags.gallery_id = galleries.id")
|
||||
f.addLeftJoin("performer_tags", "", "performer_tags.gallery_id = galleries.id")
|
||||
|
||||
addHierarchicalConditionClauses(f, tags, "performer_tags", "root_tag_id")
|
||||
}
|
||||
|
@ -425,7 +425,7 @@ func galleryAverageResolutionCriterionHandler(qb *galleryQueryBuilder, resolutio
|
|||
return func(f *filterBuilder) {
|
||||
if resolution != nil && resolution.Value.IsValid() {
|
||||
qb.imagesRepository().join(f, "images_join", "galleries.id")
|
||||
f.addJoin("images", "", "images_join.image_id = images.id")
|
||||
f.addLeftJoin("images", "", "images_join.image_id = images.id")
|
||||
|
||||
min := resolution.Value.GetMinResolution()
|
||||
max := resolution.Value.GetMaxResolution()
|
||||
|
|
|
@ -14,7 +14,7 @@ const performersImagesTable = "performers_images"
|
|||
const imagesTagsTable = "images_tags"
|
||||
|
||||
var imagesForGalleryQuery = selectAll(imageTable) + `
|
||||
LEFT JOIN galleries_images as galleries_join on galleries_join.image_id = images.id
|
||||
INNER JOIN galleries_images as galleries_join on galleries_join.image_id = images.id
|
||||
WHERE galleries_join.gallery_id = ?
|
||||
GROUP BY images.id
|
||||
`
|
||||
|
@ -360,7 +360,7 @@ func imageIsMissingCriterionHandler(qb *imageQueryBuilder, isMissing *string) cr
|
|||
qb.performersRepository().join(f, "performers_join", "images.id")
|
||||
f.addWhere("performers_join.image_id IS NULL")
|
||||
case "galleries":
|
||||
qb.galleriesRepository().join(f, "galleries_join", "images.id")
|
||||
qb.galleriesRepository().innerJoin(f, "galleries_join", "images.id")
|
||||
f.addWhere("galleries_join.image_id IS NULL")
|
||||
case "tags":
|
||||
qb.tagsRepository().join(f, "tags_join", "images.id")
|
||||
|
@ -412,8 +412,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.addJoin(galleryTable, "", "galleries_join.gallery_id = galleries.id")
|
||||
qb.galleriesRepository().innerJoin(f, "galleries_join", "images.id")
|
||||
f.addInnerJoin(galleryTable, "", "galleries_join.gallery_id = galleries.id")
|
||||
}
|
||||
h := qb.getMultiCriterionHandlerBuilder(galleryTable, galleriesImagesTable, galleryIDColumn, addJoinsFunc)
|
||||
|
||||
|
@ -469,8 +469,8 @@ func imagePerformerTagsCriterionHandler(qb *imageQueryBuilder, tags *models.Hier
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("performers_images", "", "images.id = performers_images.image_id")
|
||||
f.addJoin("performers_tags", "", "performers_images.performer_id = performers_tags.performer_id")
|
||||
f.addLeftJoin("performers_images", "", "images.id = performers_images.image_id")
|
||||
f.addLeftJoin("performers_tags", "", "performers_images.performer_id = performers_tags.performer_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("performers_tags.tag_id IS %s NULL", notClause))
|
||||
return
|
||||
|
@ -488,7 +488,7 @@ INNER JOIN performers_tags pt ON pt.performer_id = pi.performer_id
|
|||
INNER JOIN (` + valuesClause + `) t ON t.column2 = pt.tag_id
|
||||
)`)
|
||||
|
||||
f.addJoin("performer_tags", "", "performer_tags.image_id = images.id")
|
||||
f.addLeftJoin("performer_tags", "", "performer_tags.image_id = images.id")
|
||||
|
||||
addHierarchicalConditionClauses(f, tags, "performer_tags", "root_tag_id")
|
||||
}
|
||||
|
|
|
@ -69,6 +69,32 @@ func TestImageFindByPath(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestImageFindByGalleryID(t *testing.T) {
|
||||
withTxn(func(r models.Repository) error {
|
||||
sqb := r.Image()
|
||||
|
||||
images, err := sqb.FindByGalleryID(galleryIDs[galleryIdxWithTwoImages])
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error finding images: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Len(t, images, 2)
|
||||
assert.Equal(t, imageIDs[imageIdx1WithGallery], images[0].ID)
|
||||
assert.Equal(t, imageIDs[imageIdx2WithGallery], images[1].ID)
|
||||
|
||||
images, err = sqb.FindByGalleryID(galleryIDs[galleryIdxWithScene])
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error finding images: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Len(t, images, 0)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestImageQueryQ(t *testing.T) {
|
||||
withTxn(func(r models.Repository) error {
|
||||
const imageIdx = 2
|
||||
|
|
|
@ -176,13 +176,13 @@ func movieIsMissingCriterionHandler(qb *movieQueryBuilder, isMissing *string) cr
|
|||
if isMissing != nil && *isMissing != "" {
|
||||
switch *isMissing {
|
||||
case "front_image":
|
||||
f.addJoin("movies_images", "", "movies_images.movie_id = movies.id")
|
||||
f.addLeftJoin("movies_images", "", "movies_images.movie_id = movies.id")
|
||||
f.addWhere("movies_images.front_image IS NULL")
|
||||
case "back_image":
|
||||
f.addJoin("movies_images", "", "movies_images.movie_id = movies.id")
|
||||
f.addLeftJoin("movies_images", "", "movies_images.movie_id = movies.id")
|
||||
f.addWhere("movies_images.back_image IS NULL")
|
||||
case "scenes":
|
||||
f.addJoin("movies_scenes", "", "movies_scenes.movie_id = movies.id")
|
||||
f.addLeftJoin("movies_scenes", "", "movies_scenes.movie_id = movies.id")
|
||||
f.addWhere("movies_scenes.scene_id IS NULL")
|
||||
default:
|
||||
f.addWhere("(movies." + *isMissing + " IS NULL OR TRIM(movies." + *isMissing + ") = '')")
|
||||
|
@ -214,8 +214,8 @@ func moviePerformersCriterionHandler(qb *movieQueryBuilder, performers *models.M
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("movies_scenes", "", "movies.id = movies_scenes.movie_id")
|
||||
f.addJoin("performers_scenes", "", "movies_scenes.scene_id = performers_scenes.scene_id")
|
||||
f.addLeftJoin("movies_scenes", "", "movies.id = movies_scenes.movie_id")
|
||||
f.addLeftJoin("performers_scenes", "", "movies_scenes.scene_id = performers_scenes.scene_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("performers_scenes.performer_id IS %s NULL", notClause))
|
||||
return
|
||||
|
@ -237,7 +237,7 @@ func moviePerformersCriterionHandler(qb *movieQueryBuilder, performers *models.M
|
|||
INNER JOIN performers_scenes ON movies_scenes.scene_id = performers_scenes.scene_id
|
||||
WHERE performers_scenes.performer_id IN`+getInBinding(len(performers.Value))+`
|
||||
)`, args...)
|
||||
f.addJoin("movies_performers", "", "movies.id = movies_performers.movie_id")
|
||||
f.addLeftJoin("movies_performers", "", "movies.id = movies_performers.movie_id")
|
||||
|
||||
switch performers.Modifier {
|
||||
case models.CriterionModifierIncludes:
|
||||
|
|
|
@ -341,10 +341,10 @@ func performerIsMissingCriterionHandler(qb *performerQueryBuilder, isMissing *st
|
|||
if isMissing != nil && *isMissing != "" {
|
||||
switch *isMissing {
|
||||
case "scenes": // Deprecated: use `scene_count == 0` filter instead
|
||||
f.addJoin(performersScenesTable, "scenes_join", "scenes_join.performer_id = performers.id")
|
||||
f.addLeftJoin(performersScenesTable, "scenes_join", "scenes_join.performer_id = performers.id")
|
||||
f.addWhere("scenes_join.scene_id IS NULL")
|
||||
case "image":
|
||||
f.addJoin(performersImageTable, "image_join", "image_join.performer_id = performers.id")
|
||||
f.addLeftJoin(performersImageTable, "image_join", "image_join.performer_id = performers.id")
|
||||
f.addWhere("image_join.performer_id IS NULL")
|
||||
case "stash_id":
|
||||
qb.stashIDRepository().join(f, "performer_stash_ids", "performers.id")
|
||||
|
@ -463,8 +463,8 @@ func performerStudiosCriterionHandler(qb *performerQueryBuilder, studios *models
|
|||
|
||||
var conditions []string
|
||||
for _, c := range formatMaps {
|
||||
f.addJoin(c["joinTable"].(string), "", fmt.Sprintf("%s.performer_id = performers.id", c["joinTable"]))
|
||||
f.addJoin(c["primaryTable"].(string), "", fmt.Sprintf("%s.%s = %s.id", c["joinTable"], c["primaryFK"], c["primaryTable"]))
|
||||
f.addLeftJoin(c["joinTable"].(string), "", fmt.Sprintf("%s.performer_id = performers.id", c["joinTable"]))
|
||||
f.addLeftJoin(c["primaryTable"].(string), "", fmt.Sprintf("%s.%s = %s.id", c["joinTable"], c["primaryFK"], c["primaryTable"]))
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("%s.studio_id IS NULL", c["primaryTable"]))
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ func performerStudiosCriterionHandler(qb *performerQueryBuilder, studios *models
|
|||
|
||||
f.addWith(fmt.Sprintf("%s AS (%s)", derivedPerformerStudioTable, strings.Join(unions, " UNION ")))
|
||||
|
||||
f.addJoin(derivedPerformerStudioTable, "", fmt.Sprintf("performers.id = %s.performer_id", derivedPerformerStudioTable))
|
||||
f.addLeftJoin(derivedPerformerStudioTable, "", fmt.Sprintf("performers.id = %s.performer_id", derivedPerformerStudioTable))
|
||||
f.addWhere(fmt.Sprintf("%s.performer_id IS %s NULL", derivedPerformerStudioTable, clauseCondition))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ func (qb *queryBuilder) join(table, as, onClause string) {
|
|||
table: table,
|
||||
as: as,
|
||||
onClause: onClause,
|
||||
joinType: "LEFT",
|
||||
}
|
||||
|
||||
qb.joins.add(newJoin)
|
||||
|
|
|
@ -294,11 +294,20 @@ func (r *repository) join(j joiner, as string, parentIDCol string) {
|
|||
if as != "" {
|
||||
t = as
|
||||
}
|
||||
j.addJoin(r.tableName, as, fmt.Sprintf("%s.%s = %s", t, r.idColumn, parentIDCol))
|
||||
j.addLeftJoin(r.tableName, as, fmt.Sprintf("%s.%s = %s", t, r.idColumn, parentIDCol))
|
||||
}
|
||||
|
||||
func (r *repository) innerJoin(j joiner, as string, parentIDCol string) {
|
||||
t := r.tableName
|
||||
if as != "" {
|
||||
t = as
|
||||
}
|
||||
j.addInnerJoin(r.tableName, as, fmt.Sprintf("%s.%s = %s", t, r.idColumn, parentIDCol))
|
||||
}
|
||||
|
||||
type joiner interface {
|
||||
addJoin(table, as, onClause string)
|
||||
addLeftJoin(table, as, onClause string)
|
||||
addInnerJoin(table, as, onClause string)
|
||||
}
|
||||
|
||||
type joinRepository struct {
|
||||
|
|
|
@ -537,7 +537,7 @@ func resolutionCriterionHandler(resolution *models.ResolutionCriterionInput, hei
|
|||
func hasMarkersCriterionHandler(hasMarkers *string) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if hasMarkers != nil {
|
||||
f.addJoin("scene_markers", "", "scene_markers.scene_id = scenes.id")
|
||||
f.addLeftJoin("scene_markers", "", "scene_markers.scene_id = scenes.id")
|
||||
if *hasMarkers == "true" {
|
||||
f.addHaving("count(scene_markers.scene_id) > 0")
|
||||
} else {
|
||||
|
@ -658,7 +658,7 @@ 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.addJoin("movies", "", "movies_join.movie_id = movies.id")
|
||||
f.addLeftJoin("movies", "", "movies_join.movie_id = movies.id")
|
||||
}
|
||||
h := qb.getMultiCriterionHandlerBuilder(movieTable, moviesScenesTable, "movie_id", addJoinsFunc)
|
||||
return h.handler(movies)
|
||||
|
@ -673,8 +673,8 @@ func scenePerformerTagsCriterionHandler(qb *sceneQueryBuilder, tags *models.Hier
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("performers_scenes", "", "scenes.id = performers_scenes.scene_id")
|
||||
f.addJoin("performers_tags", "", "performers_scenes.performer_id = performers_tags.performer_id")
|
||||
f.addLeftJoin("performers_scenes", "", "scenes.id = performers_scenes.scene_id")
|
||||
f.addLeftJoin("performers_tags", "", "performers_scenes.performer_id = performers_tags.performer_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("performers_tags.tag_id IS %s NULL", notClause))
|
||||
return
|
||||
|
@ -692,7 +692,7 @@ INNER JOIN performers_tags pt ON pt.performer_id = ps.performer_id
|
|||
INNER JOIN (` + valuesClause + `) t ON t.column2 = pt.tag_id
|
||||
)`)
|
||||
|
||||
f.addJoin("performer_tags", "", "performer_tags.scene_id = scenes.id")
|
||||
f.addLeftJoin("performer_tags", "", "performer_tags.scene_id = scenes.id")
|
||||
|
||||
addHierarchicalConditionClauses(f, tags, "performer_tags", "root_tag_id")
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ func (qb *sceneMarkerQueryBuilder) Query(sceneMarkerFilter *models.SceneMarkerFi
|
|||
func sceneMarkerTagIDCriterionHandler(qb *sceneMarkerQueryBuilder, tagID *string) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if tagID != nil {
|
||||
f.addJoin("scene_markers_tags", "", "scene_markers_tags.scene_marker_id = scene_markers.id")
|
||||
f.addLeftJoin("scene_markers_tags", "", "scene_markers_tags.scene_marker_id = scene_markers.id")
|
||||
|
||||
f.addWhere("(scene_markers.primary_tag_id = ? OR scene_markers_tags.tag_id = ?)", *tagID, *tagID)
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ func sceneMarkerTagsCriterionHandler(qb *sceneMarkerQueryBuilder, tags *models.H
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("scene_markers_tags", "", "scene_markers.id = scene_markers_tags.scene_marker_id")
|
||||
f.addLeftJoin("scene_markers_tags", "", "scene_markers.id = scene_markers_tags.scene_marker_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("%s scene_markers_tags.tag_id IS NULL", notClause))
|
||||
return
|
||||
|
@ -215,7 +215,7 @@ SELECT m.id, t.column1 FROM scene_markers m
|
|||
INNER JOIN (` + valuesClause + `) t ON t.column2 = m.primary_tag_id
|
||||
)`)
|
||||
|
||||
f.addJoin("marker_tags", "", "marker_tags.scene_marker_id = scene_markers.id")
|
||||
f.addLeftJoin("marker_tags", "", "marker_tags.scene_marker_id = scene_markers.id")
|
||||
|
||||
addHierarchicalConditionClauses(f, tags, "marker_tags", "root_tag_id")
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ func sceneMarkerSceneTagsCriterionHandler(qb *sceneMarkerQueryBuilder, tags *mod
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("scenes_tags", "", "scene_markers.scene_id = scenes_tags.scene_id")
|
||||
f.addLeftJoin("scenes_tags", "", "scene_markers.scene_id = scenes_tags.scene_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("scenes_tags.tag_id IS %s NULL", notClause))
|
||||
return
|
||||
|
@ -248,7 +248,7 @@ SELECT st.scene_id, t.column1 AS root_tag_id FROM scenes_tags st
|
|||
INNER JOIN (` + valuesClause + `) t ON t.column2 = st.tag_id
|
||||
)`)
|
||||
|
||||
f.addJoin("scene_tags", "", "scene_tags.scene_id = scene_markers.scene_id")
|
||||
f.addLeftJoin("scene_tags", "", "scene_tags.scene_id = scene_markers.scene_id")
|
||||
|
||||
addHierarchicalConditionClauses(f, tags, "scene_tags", "root_tag_id")
|
||||
}
|
||||
|
@ -264,14 +264,14 @@ func sceneMarkerPerformersCriterionHandler(qb *sceneMarkerQueryBuilder, performe
|
|||
foreignFK: performerIDColumn,
|
||||
|
||||
addJoinTable: func(f *filterBuilder) {
|
||||
f.addJoin(performersScenesTable, "performers_join", "performers_join.scene_id = scene_markers.scene_id")
|
||||
f.addLeftJoin(performersScenesTable, "performers_join", "performers_join.scene_id = scene_markers.scene_id")
|
||||
},
|
||||
}
|
||||
|
||||
handler := h.handler(performers)
|
||||
return func(f *filterBuilder) {
|
||||
// Make sure scenes is included, otherwise excludes filter fails
|
||||
f.addJoin(sceneTable, "", "scenes.id = scene_markers.scene_id")
|
||||
f.addLeftJoin(sceneTable, "", "scenes.id = scene_markers.scene_id")
|
||||
handler(f)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,7 +278,7 @@ func studioIsMissingCriterionHandler(qb *studioQueryBuilder, isMissing *string)
|
|||
if isMissing != nil && *isMissing != "" {
|
||||
switch *isMissing {
|
||||
case "image":
|
||||
f.addJoin("studios_image", "", "studios_image.studio_id = studios.id")
|
||||
f.addLeftJoin("studios_image", "", "studios_image.studio_id = studios.id")
|
||||
f.addWhere("studios_image.studio_id IS NULL")
|
||||
case "stash_id":
|
||||
qb.stashIDRepository().join(f, "studio_stash_ids", "studios.id")
|
||||
|
@ -293,7 +293,7 @@ func studioIsMissingCriterionHandler(qb *studioQueryBuilder, isMissing *string)
|
|||
func studioSceneCountCriterionHandler(qb *studioQueryBuilder, sceneCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if sceneCount != nil {
|
||||
f.addJoin("scenes", "", "scenes.studio_id = studios.id")
|
||||
f.addLeftJoin("scenes", "", "scenes.studio_id = studios.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct scenes.id)", *sceneCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -304,7 +304,7 @@ func studioSceneCountCriterionHandler(qb *studioQueryBuilder, sceneCount *models
|
|||
func studioImageCountCriterionHandler(qb *studioQueryBuilder, imageCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if imageCount != nil {
|
||||
f.addJoin("images", "", "images.studio_id = studios.id")
|
||||
f.addLeftJoin("images", "", "images.studio_id = studios.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct images.id)", *imageCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -315,7 +315,7 @@ func studioImageCountCriterionHandler(qb *studioQueryBuilder, imageCount *models
|
|||
func studioGalleryCountCriterionHandler(qb *studioQueryBuilder, galleryCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if galleryCount != nil {
|
||||
f.addJoin("galleries", "", "galleries.studio_id = studios.id")
|
||||
f.addLeftJoin("galleries", "", "galleries.studio_id = studios.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct galleries.id)", *galleryCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -325,7 +325,7 @@ func studioGalleryCountCriterionHandler(qb *studioQueryBuilder, galleryCount *mo
|
|||
|
||||
func studioParentCriterionHandler(qb *studioQueryBuilder, parents *models.MultiCriterionInput) criterionHandlerFunc {
|
||||
addJoinsFunc := func(f *filterBuilder) {
|
||||
f.addJoin("studios", "parent_studio", "parent_studio.id = studios.parent_id")
|
||||
f.addLeftJoin("studios", "parent_studio", "parent_studio.id = studios.parent_id")
|
||||
}
|
||||
h := multiCriterionHandlerBuilder{
|
||||
primaryTable: studioTable,
|
||||
|
|
|
@ -386,7 +386,7 @@ func tagIsMissingCriterionHandler(qb *tagQueryBuilder, isMissing *string) criter
|
|||
func tagSceneCountCriterionHandler(qb *tagQueryBuilder, sceneCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if sceneCount != nil {
|
||||
f.addJoin("scenes_tags", "", "scenes_tags.tag_id = tags.id")
|
||||
f.addLeftJoin("scenes_tags", "", "scenes_tags.tag_id = tags.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct scenes_tags.scene_id)", *sceneCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -397,7 +397,7 @@ func tagSceneCountCriterionHandler(qb *tagQueryBuilder, sceneCount *models.IntCr
|
|||
func tagImageCountCriterionHandler(qb *tagQueryBuilder, imageCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if imageCount != nil {
|
||||
f.addJoin("images_tags", "", "images_tags.tag_id = tags.id")
|
||||
f.addLeftJoin("images_tags", "", "images_tags.tag_id = tags.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct images_tags.image_id)", *imageCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -408,7 +408,7 @@ func tagImageCountCriterionHandler(qb *tagQueryBuilder, imageCount *models.IntCr
|
|||
func tagGalleryCountCriterionHandler(qb *tagQueryBuilder, galleryCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if galleryCount != nil {
|
||||
f.addJoin("galleries_tags", "", "galleries_tags.tag_id = tags.id")
|
||||
f.addLeftJoin("galleries_tags", "", "galleries_tags.tag_id = tags.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct galleries_tags.gallery_id)", *galleryCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -419,7 +419,7 @@ func tagGalleryCountCriterionHandler(qb *tagQueryBuilder, galleryCount *models.I
|
|||
func tagPerformerCountCriterionHandler(qb *tagQueryBuilder, performerCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if performerCount != nil {
|
||||
f.addJoin("performers_tags", "", "performers_tags.tag_id = tags.id")
|
||||
f.addLeftJoin("performers_tags", "", "performers_tags.tag_id = tags.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct performers_tags.performer_id)", *performerCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -430,8 +430,8 @@ func tagPerformerCountCriterionHandler(qb *tagQueryBuilder, performerCount *mode
|
|||
func tagMarkerCountCriterionHandler(qb *tagQueryBuilder, markerCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if markerCount != nil {
|
||||
f.addJoin("scene_markers_tags", "", "scene_markers_tags.tag_id = tags.id")
|
||||
f.addJoin("scene_markers", "", "scene_markers_tags.scene_marker_id = scene_markers.id OR scene_markers.primary_tag_id = tags.id")
|
||||
f.addLeftJoin("scene_markers_tags", "", "scene_markers_tags.tag_id = tags.id")
|
||||
f.addLeftJoin("scene_markers", "", "scene_markers_tags.scene_marker_id = scene_markers.id OR scene_markers.primary_tag_id = tags.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct scene_markers.id)", *markerCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -448,7 +448,7 @@ func tagParentsCriterionHandler(qb *tagQueryBuilder, tags *models.HierarchicalMu
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("tags_relations", "parent_relations", "tags.id = parent_relations.child_id")
|
||||
f.addLeftJoin("tags_relations", "parent_relations", "tags.id = parent_relations.child_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("parent_relations.parent_id IS %s NULL", notClause))
|
||||
return
|
||||
|
@ -481,7 +481,7 @@ func tagParentsCriterionHandler(qb *tagQueryBuilder, tags *models.HierarchicalMu
|
|||
|
||||
f.addRecursiveWith(query, args...)
|
||||
|
||||
f.addJoin("parents", "", "parents.item_id = tags.id")
|
||||
f.addLeftJoin("parents", "", "parents.item_id = tags.id")
|
||||
|
||||
addHierarchicalConditionClauses(f, tags, "parents", "root_id")
|
||||
}
|
||||
|
@ -497,7 +497,7 @@ func tagChildrenCriterionHandler(qb *tagQueryBuilder, tags *models.HierarchicalM
|
|||
notClause = "NOT"
|
||||
}
|
||||
|
||||
f.addJoin("tags_relations", "child_relations", "tags.id = child_relations.parent_id")
|
||||
f.addLeftJoin("tags_relations", "child_relations", "tags.id = child_relations.parent_id")
|
||||
|
||||
f.addWhere(fmt.Sprintf("child_relations.child_id IS %s NULL", notClause))
|
||||
return
|
||||
|
@ -530,7 +530,7 @@ func tagChildrenCriterionHandler(qb *tagQueryBuilder, tags *models.HierarchicalM
|
|||
|
||||
f.addRecursiveWith(query, args...)
|
||||
|
||||
f.addJoin("children", "", "children.item_id = tags.id")
|
||||
f.addLeftJoin("children", "", "children.item_id = tags.id")
|
||||
|
||||
addHierarchicalConditionClauses(f, tags, "children", "root_id")
|
||||
}
|
||||
|
@ -540,7 +540,7 @@ func tagChildrenCriterionHandler(qb *tagQueryBuilder, tags *models.HierarchicalM
|
|||
func tagParentCountCriterionHandler(qb *tagQueryBuilder, parentCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if parentCount != nil {
|
||||
f.addJoin("tags_relations", "parents_count", "parents_count.child_id = tags.id")
|
||||
f.addLeftJoin("tags_relations", "parents_count", "parents_count.child_id = tags.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct parents_count.parent_id)", *parentCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
@ -551,7 +551,7 @@ func tagParentCountCriterionHandler(qb *tagQueryBuilder, parentCount *models.Int
|
|||
func tagChildCountCriterionHandler(qb *tagQueryBuilder, childCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if childCount != nil {
|
||||
f.addJoin("tags_relations", "children_count", "children_count.parent_id = tags.id")
|
||||
f.addLeftJoin("tags_relations", "children_count", "children_count.parent_id = tags.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct children_count.child_id)", *childCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
|
|
Loading…
Reference in New Issue