mirror of https://github.com/stashapp/stash.git
Fix path filters (#3041)
* Fix path filters * Replace getPathSearchClause * Remove incorrect tests
This commit is contained in:
parent
a60afc162f
commit
1c0042c4c2
|
@ -796,7 +796,8 @@ func (qb *FileStore) Query(ctx context.Context, options models.FileQueryOptions)
|
|||
distinctIDs(&query, fileTable)
|
||||
|
||||
if q := findFilter.Q; q != nil && *q != "" {
|
||||
searchColumns := []string{"folders.path", "files.basename"}
|
||||
filepathColumn := "folders.path || '" + string(filepath.Separator) + "' || files.basename"
|
||||
searchColumns := []string{filepathColumn}
|
||||
query.parseQueryString(searchColumns, *q)
|
||||
}
|
||||
|
||||
|
|
|
@ -454,17 +454,19 @@ func pathCriterionHandler(c *models.StringCriterionInput, pathColumn string, bas
|
|||
f.setError(err)
|
||||
return
|
||||
}
|
||||
f.addWhere(fmt.Sprintf("%s IS NOT NULL AND %s IS NOT NULL AND %[1]s || '%[3]s' || %[2]s regexp ?", pathColumn, basenameColumn, string(filepath.Separator)), c.Value)
|
||||
filepathColumn := fmt.Sprintf("%s || '%s' || %s", pathColumn, string(filepath.Separator), basenameColumn)
|
||||
f.addWhere(fmt.Sprintf("%s IS NOT NULL AND %s IS NOT NULL AND %s regexp ?", pathColumn, basenameColumn, filepathColumn), c.Value)
|
||||
case models.CriterionModifierNotMatchesRegex:
|
||||
if _, err := regexp.Compile(c.Value); err != nil {
|
||||
f.setError(err)
|
||||
return
|
||||
}
|
||||
f.addWhere(fmt.Sprintf("%s IS NULL OR %s IS NULL OR %[1]s || '%[3]s' || %[2]s NOT regexp ?", pathColumn, basenameColumn, string(filepath.Separator)), c.Value)
|
||||
filepathColumn := fmt.Sprintf("%s || '%s' || %s", pathColumn, string(filepath.Separator), basenameColumn)
|
||||
f.addWhere(fmt.Sprintf("%s IS NULL OR %s IS NULL OR %s NOT regexp ?", pathColumn, basenameColumn, filepathColumn), c.Value)
|
||||
case models.CriterionModifierIsNull:
|
||||
f.addWhere(fmt.Sprintf("(%s IS NULL OR TRIM(%[1]s) = '' OR %s IS NULL OR TRIM(%[2]s) = '')", pathColumn, basenameColumn))
|
||||
f.addWhere(fmt.Sprintf("%s IS NULL OR TRIM(%[1]s) = '' OR %s IS NULL OR TRIM(%[2]s) = ''", pathColumn, basenameColumn))
|
||||
case models.CriterionModifierNotNull:
|
||||
f.addWhere(fmt.Sprintf("(%s IS NOT NULL AND TRIM(%[1]s) != '' AND %s IS NOT NULL AND TRIM(%[2]s) != '')", pathColumn, basenameColumn))
|
||||
f.addWhere(fmt.Sprintf("%s IS NOT NULL AND TRIM(%[1]s) != '' AND %s IS NOT NULL AND TRIM(%[2]s) != ''", pathColumn, basenameColumn))
|
||||
default:
|
||||
panic("unsupported string filter modifier")
|
||||
}
|
||||
|
@ -474,46 +476,12 @@ func pathCriterionHandler(c *models.StringCriterionInput, pathColumn string, bas
|
|||
}
|
||||
|
||||
func getPathSearchClause(pathColumn, basenameColumn, p string, addWildcards, not bool) sqlClause {
|
||||
// if path value has slashes, then we're potentially searching directory only or
|
||||
// directory plus basename
|
||||
hasSlashes := strings.Contains(p, string(filepath.Separator))
|
||||
trailingSlash := hasSlashes && p[len(p)-1] == filepath.Separator
|
||||
const emptyDir = string(filepath.Separator)
|
||||
|
||||
// possible values:
|
||||
// dir/basename
|
||||
// dir1/subdir
|
||||
// dir/
|
||||
// /basename
|
||||
// dirOrBasename
|
||||
|
||||
basename := filepath.Base(p)
|
||||
dir := filepath.Dir(p)
|
||||
|
||||
if addWildcards {
|
||||
p = "%" + p + "%"
|
||||
basename += "%"
|
||||
dir = "%" + dir
|
||||
}
|
||||
|
||||
var ret sqlClause
|
||||
|
||||
switch {
|
||||
case !hasSlashes:
|
||||
// dir or basename
|
||||
ret = makeClause(fmt.Sprintf("%s LIKE ? OR %s LIKE ?", pathColumn, basenameColumn), p, p)
|
||||
case dir != emptyDir && !trailingSlash:
|
||||
// (path like %dir AND basename like basename%) OR path like %p%
|
||||
c1 := makeClause(fmt.Sprintf("%s LIKE ? AND %s LIKE ?", pathColumn, basenameColumn), dir, basename)
|
||||
c2 := makeClause(fmt.Sprintf("%s LIKE ?", pathColumn), p)
|
||||
ret = orClauses(c1, c2)
|
||||
case dir == emptyDir && !trailingSlash:
|
||||
// path like %p% OR basename like basename%
|
||||
ret = makeClause(fmt.Sprintf("%s LIKE ? OR %s LIKE ?", pathColumn, basenameColumn), p, basename)
|
||||
case dir != emptyDir && trailingSlash:
|
||||
// path like %p% OR path like %dir
|
||||
ret = makeClause(fmt.Sprintf("%s LIKE ? OR %[1]s LIKE ?", pathColumn), p, dir)
|
||||
}
|
||||
filepathColumn := fmt.Sprintf("%s || '%s' || %s", pathColumn, string(filepath.Separator), basenameColumn)
|
||||
ret := makeClause(fmt.Sprintf("%s LIKE ?", filepathColumn), p)
|
||||
|
||||
if not {
|
||||
ret = ret.not()
|
||||
|
|
|
@ -719,7 +719,8 @@ func (qb *GalleryStore) makeQuery(ctx context.Context, galleryFilter *models.Gal
|
|||
)
|
||||
|
||||
// add joins for files and checksum
|
||||
searchColumns := []string{"galleries.title", "gallery_folder.path", "folders.path", "files.basename", "files_fingerprints.fingerprint"}
|
||||
filepathColumn := "folders.path || '" + string(filepath.Separator) + "' || files.basename"
|
||||
searchColumns := []string{"galleries.title", "gallery_folder.path", filepathColumn, "files_fingerprints.fingerprint"}
|
||||
query.parseQueryString(searchColumns, *q)
|
||||
}
|
||||
|
||||
|
@ -785,12 +786,12 @@ func (qb *GalleryStore) galleryPathCriterionHandler(c *models.StringCriterionInp
|
|||
if modifier := c.Modifier; c.Modifier.IsValid() {
|
||||
switch modifier {
|
||||
case models.CriterionModifierIncludes:
|
||||
clause := getPathSearchClause(pathColumn, basenameColumn, c.Value, addWildcards, not)
|
||||
clause := getPathSearchClauseMany(pathColumn, basenameColumn, c.Value, addWildcards, not)
|
||||
clause2 := getStringSearchClause([]string{folderPathColumn}, c.Value, false)
|
||||
f.whereClauses = append(f.whereClauses, orClauses(clause, clause2))
|
||||
case models.CriterionModifierExcludes:
|
||||
not = true
|
||||
clause := getPathSearchClause(pathColumn, basenameColumn, c.Value, addWildcards, not)
|
||||
clause := getPathSearchClauseMany(pathColumn, basenameColumn, c.Value, addWildcards, not)
|
||||
clause2 := getStringSearchClause([]string{folderPathColumn}, c.Value, true)
|
||||
f.whereClauses = append(f.whereClauses, orClauses(clause, clause2))
|
||||
case models.CriterionModifierEquals:
|
||||
|
@ -809,22 +810,24 @@ func (qb *GalleryStore) galleryPathCriterionHandler(c *models.StringCriterionInp
|
|||
f.setError(err)
|
||||
return
|
||||
}
|
||||
clause := makeClause(fmt.Sprintf("(%s IS NOT NULL AND %[1]s regexp ?) OR (%s IS NOT NULL AND %[2]s regexp ?)", pathColumn, basenameColumn), c.Value, c.Value)
|
||||
clause2 := makeClause(fmt.Sprintf("(%s IS NOT NULL AND %[1]s regexp ?)", folderPathColumn), c.Value)
|
||||
filepathColumn := fmt.Sprintf("%s || '%s' || %s", pathColumn, string(filepath.Separator), basenameColumn)
|
||||
clause := makeClause(fmt.Sprintf("%s IS NOT NULL AND %s IS NOT NULL AND %s regexp ?", pathColumn, basenameColumn, filepathColumn), c.Value)
|
||||
clause2 := makeClause(fmt.Sprintf("%s IS NOT NULL AND %[1]s regexp ?", folderPathColumn), c.Value)
|
||||
f.whereClauses = append(f.whereClauses, orClauses(clause, clause2))
|
||||
case models.CriterionModifierNotMatchesRegex:
|
||||
if _, err := regexp.Compile(c.Value); err != nil {
|
||||
f.setError(err)
|
||||
return
|
||||
}
|
||||
f.addWhere(fmt.Sprintf("(%s IS NULL OR %[1]s NOT regexp ?) AND (%s IS NULL OR %[2]s NOT regexp ?)", pathColumn, basenameColumn), c.Value, c.Value)
|
||||
f.addWhere(fmt.Sprintf("(%s IS NULL OR %[1]s NOT regexp ?)", folderPathColumn), c.Value)
|
||||
filepathColumn := fmt.Sprintf("%s || '%s' || %s", pathColumn, string(filepath.Separator), basenameColumn)
|
||||
f.addWhere(fmt.Sprintf("%s IS NULL OR %s IS NULL OR %s NOT regexp ?", pathColumn, basenameColumn, filepathColumn), c.Value)
|
||||
f.addWhere(fmt.Sprintf("%s IS NULL OR %[1]s NOT regexp ?", folderPathColumn), c.Value)
|
||||
case models.CriterionModifierIsNull:
|
||||
f.whereClauses = append(f.whereClauses, makeClause(fmt.Sprintf("(%s IS NULL OR TRIM(%[1]s) = '' OR %s IS NULL OR TRIM(%[2]s) = '')", pathColumn, basenameColumn)))
|
||||
f.whereClauses = append(f.whereClauses, makeClause(fmt.Sprintf("(%s IS NULL OR TRIM(%[1]s) = '')", folderPathColumn)))
|
||||
f.addWhere(fmt.Sprintf("%s IS NULL OR TRIM(%[1]s) = '' OR %s IS NULL OR TRIM(%[2]s) = ''", pathColumn, basenameColumn))
|
||||
f.addWhere(fmt.Sprintf("%s IS NULL OR TRIM(%[1]s) = ''", folderPathColumn))
|
||||
case models.CriterionModifierNotNull:
|
||||
clause := makeClause(fmt.Sprintf("(%s IS NOT NULL AND TRIM(%[1]s) != '' AND %s IS NOT NULL AND TRIM(%[2]s) != '')", pathColumn, basenameColumn))
|
||||
clause2 := makeClause(fmt.Sprintf("(%s IS NOT NULL AND TRIM(%[1]s) != '')", folderPathColumn))
|
||||
clause := makeClause(fmt.Sprintf("%s IS NOT NULL AND TRIM(%[1]s) != '' AND %s IS NOT NULL AND TRIM(%[2]s) != ''", pathColumn, basenameColumn))
|
||||
clause2 := makeClause(fmt.Sprintf("%s IS NOT NULL AND TRIM(%[1]s) != ''", folderPathColumn))
|
||||
f.whereClauses = append(f.whereClauses, orClauses(clause, clause2))
|
||||
default:
|
||||
panic("unsupported string filter modifier")
|
||||
|
|
|
@ -700,7 +700,8 @@ func (qb *ImageStore) makeQuery(ctx context.Context, imageFilter *models.ImageFi
|
|||
},
|
||||
)
|
||||
|
||||
searchColumns := []string{"images.title", "folders.path", "files.basename", "files_fingerprints.fingerprint"}
|
||||
filepathColumn := "folders.path || '" + string(filepath.Separator) + "' || files.basename"
|
||||
searchColumns := []string{"images.title", filepathColumn, "files_fingerprints.fingerprint"}
|
||||
query.parseQueryString(searchColumns, *q)
|
||||
}
|
||||
|
||||
|
|
|
@ -933,7 +933,8 @@ func (qb *SceneStore) Query(ctx context.Context, options models.SceneQueryOption
|
|||
},
|
||||
)
|
||||
|
||||
searchColumns := []string{"scenes.title", "scenes.details", "folders.path", "files.basename", "files_fingerprints.fingerprint", "scene_markers.title"}
|
||||
filepathColumn := "folders.path || '" + string(filepath.Separator) + "' || files.basename"
|
||||
searchColumns := []string{"scenes.title", "scenes.details", filepathColumn, "files_fingerprints.fingerprint", "scene_markers.title"}
|
||||
query.parseQueryString(searchColumns, *q)
|
||||
}
|
||||
|
||||
|
|
|
@ -2097,42 +2097,6 @@ func TestSceneQueryPath(t *testing.T) {
|
|||
[]int{sceneIdx},
|
||||
[]int{otherSceneIdx},
|
||||
},
|
||||
{
|
||||
"equals folder name",
|
||||
models.StringCriterionInput{
|
||||
Value: folder,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
[]int{sceneIdx},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"equals folder name trailing slash",
|
||||
models.StringCriterionInput{
|
||||
Value: folder + string(filepath.Separator),
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
[]int{sceneIdx},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"equals base name",
|
||||
models.StringCriterionInput{
|
||||
Value: basename,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
[]int{sceneIdx},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"equals base name leading slash",
|
||||
models.StringCriterionInput{
|
||||
Value: string(filepath.Separator) + basename,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
[]int{sceneIdx},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"equals full path wildcard",
|
||||
models.StringCriterionInput{
|
||||
|
@ -2151,24 +2115,6 @@ func TestSceneQueryPath(t *testing.T) {
|
|||
[]int{otherSceneIdx},
|
||||
[]int{sceneIdx},
|
||||
},
|
||||
{
|
||||
"not equals folder name",
|
||||
models.StringCriterionInput{
|
||||
Value: folder,
|
||||
Modifier: models.CriterionModifierNotEquals,
|
||||
},
|
||||
nil,
|
||||
[]int{sceneIdx},
|
||||
},
|
||||
{
|
||||
"not equals basename",
|
||||
models.StringCriterionInput{
|
||||
Value: basename,
|
||||
Modifier: models.CriterionModifierNotEquals,
|
||||
},
|
||||
nil,
|
||||
[]int{sceneIdx},
|
||||
},
|
||||
{
|
||||
"includes folder name",
|
||||
models.StringCriterionInput{
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
### ✨ New Features
|
||||
* Added tag description filter criterion. ([#3011](https://github.com/stashapp/stash/pull/3011))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
* Fix path filter behaviour to be consistent with previous behaviour. ([#3041](https://github.com/stashapp/stash/pull/3041))
|
Loading…
Reference in New Issue