Fix path filters (#3041)

* Fix path filters
* Replace getPathSearchClause
* Remove incorrect tests
This commit is contained in:
DingDongSoLong4 2022-10-26 09:48:13 +02:00 committed by GitHub
parent a60afc162f
commit 1c0042c4c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 31 additions and 108 deletions

View File

@ -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)
}

View File

@ -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()

View File

@ -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")

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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{

View File

@ -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))