filebrowser/backend/files/search.go

184 lines
4.6 KiB
Go
Raw Permalink Normal View History

package files
2023-06-15 01:08:09 +00:00
import (
"path/filepath"
2023-06-18 15:04:31 +00:00
"sort"
2023-06-15 01:08:09 +00:00
"strings"
2023-06-16 17:29:43 +00:00
"sync"
2024-11-21 00:15:30 +00:00
2025-01-21 14:02:43 +00:00
"github.com/gtsteffaniak/filebrowser/backend/cache"
2024-12-17 00:01:55 +00:00
"github.com/gtsteffaniak/filebrowser/backend/utils"
2023-06-15 01:08:09 +00:00
)
2023-06-16 17:29:43 +00:00
var (
sessionInProgress sync.Map
maxSearchResults = 100
2023-06-16 17:29:43 +00:00
)
2023-06-15 01:08:09 +00:00
2024-12-17 00:01:55 +00:00
type SearchResult struct {
2024-11-21 00:15:30 +00:00
Path string `json:"path"`
Type string `json:"type"`
Size int64 `json:"size"`
}
2025-01-05 19:05:33 +00:00
func (idx *Index) Search(search string, scope string, sourceSession string) []SearchResult {
// Remove slashes
2025-01-05 19:05:33 +00:00
scope = idx.makeIndexPath(scope)
2025-01-21 14:02:43 +00:00
runningHash := utils.InsecureRandomIdentifier(4)
sessionInProgress.Store(sourceSession, runningHash) // Store the value in the sync.Map
2023-06-18 15:04:31 +00:00
searchOptions := ParseSearch(search)
2024-12-17 00:01:55 +00:00
results := make(map[string]SearchResult, 0)
count := 0
2024-11-26 17:21:41 +00:00
var directories []string
2025-01-21 14:02:43 +00:00
cachedDirs, ok := cache.SearchResults.Get(idx.Source.Path + scope).([]string)
2024-11-26 17:21:41 +00:00
if ok {
directories = cachedDirs
} else {
2025-01-05 19:05:33 +00:00
directories = idx.getDirsInScope(scope)
2025-01-21 14:02:43 +00:00
cache.SearchResults.Set(idx.Source.Path+scope, directories)
2024-11-26 17:21:41 +00:00
}
2023-06-18 15:04:31 +00:00
for _, searchTerm := range searchOptions.Terms {
2023-07-04 23:55:15 +00:00
if searchTerm == "" {
continue
}
2024-11-21 00:15:30 +00:00
if count > maxSearchResults {
break
}
2025-01-05 19:05:33 +00:00
idx.mu.Lock()
2024-11-21 00:15:30 +00:00
for _, dirName := range directories {
2024-11-26 17:21:41 +00:00
scopedPath := strings.TrimPrefix(strings.TrimPrefix(dirName, scope), "/") + "/"
2025-01-05 19:05:33 +00:00
idx.mu.Unlock()
dir, found := idx.GetReducedMetadata(dirName, true)
idx.mu.Lock()
2024-11-21 00:15:30 +00:00
if !found {
continue
}
if count > maxSearchResults {
break
}
2024-11-26 17:21:41 +00:00
reducedDir := ItemInfo{
2024-11-21 00:15:30 +00:00
Name: filepath.Base(dirName),
Type: "directory",
Size: dir.Size,
}
2024-11-21 00:15:30 +00:00
matches := reducedDir.containsSearchTerm(searchTerm, searchOptions)
if matches {
2024-12-17 00:01:55 +00:00
results[scopedPath] = SearchResult{Path: scopedPath, Type: "directory", Size: dir.Size}
count++
}
2024-11-21 00:15:30 +00:00
// search files first
2024-11-26 17:21:41 +00:00
for _, item := range dir.Files {
2024-11-21 00:15:30 +00:00
fullPath := dirName + "/" + item.Name
2024-11-26 17:21:41 +00:00
scopedPath := strings.TrimPrefix(strings.TrimPrefix(fullPath, scope), "/")
2024-11-21 00:15:30 +00:00
if item.Type == "directory" {
2024-11-26 17:21:41 +00:00
scopedPath += "/"
}
2023-08-17 21:46:49 +00:00
value, found := sessionInProgress.Load(sourceSession)
if !found || value != runningHash {
2025-01-05 19:05:33 +00:00
idx.mu.Unlock()
2024-12-17 00:01:55 +00:00
return []SearchResult{}
2023-08-17 21:46:49 +00:00
}
if count > maxSearchResults {
2023-08-17 21:46:49 +00:00
break
}
2024-11-21 00:15:30 +00:00
matches := item.containsSearchTerm(searchTerm, searchOptions)
if matches {
2024-12-17 00:01:55 +00:00
results[scopedPath] = SearchResult{Path: scopedPath, Type: item.Type, Size: item.Size}
2024-11-21 00:15:30 +00:00
count++
}
2023-07-13 02:23:29 +00:00
}
2023-06-15 01:08:09 +00:00
}
2025-01-05 19:05:33 +00:00
idx.mu.Unlock()
2023-06-15 01:08:09 +00:00
}
2024-11-21 00:15:30 +00:00
// Sort keys based on the number of elements in the path after splitting by "/"
2024-12-17 00:01:55 +00:00
sortedKeys := make([]SearchResult, 0, len(results))
2024-11-21 00:15:30 +00:00
for _, v := range results {
sortedKeys = append(sortedKeys, v)
}
2023-06-16 17:29:43 +00:00
// Sort the strings based on the number of elements after splitting by "/"
2024-11-21 00:15:30 +00:00
sort.Slice(sortedKeys, func(i, j int) bool {
parts1 := strings.Split(sortedKeys[i].Path, "/")
parts2 := strings.Split(sortedKeys[j].Path, "/")
2023-06-16 17:29:43 +00:00
return len(parts1) < len(parts2)
})
2024-11-21 00:15:30 +00:00
return sortedKeys
2023-06-15 01:08:09 +00:00
}
2024-11-21 00:15:30 +00:00
// returns true if the file name contains the search term
// returns file type if the file name contains the search term
// returns size of file/dir if the file name contains the search term
2024-11-26 17:21:41 +00:00
func (fi ItemInfo) containsSearchTerm(searchTerm string, options SearchOptions) bool {
2024-11-21 00:15:30 +00:00
fileTypes := map[string]bool{}
2024-10-07 22:44:53 +00:00
largerThan := int64(options.LargerThan) * 1024 * 1024
smallerThan := int64(options.SmallerThan) * 1024 * 1024
conditions := options.Conditions
2024-11-21 00:15:30 +00:00
lowerFileName := strings.ToLower(fi.Name)
2024-10-07 22:44:53 +00:00
// Convert to lowercase if not exact match
2023-08-12 16:30:41 +00:00
if !conditions["exact"] {
searchTerm = strings.ToLower(searchTerm)
2023-08-12 16:30:41 +00:00
}
2024-10-07 22:44:53 +00:00
// Check if the file name contains the search term
2024-11-21 00:15:30 +00:00
if !strings.Contains(lowerFileName, searchTerm) {
return false
2024-10-07 22:44:53 +00:00
}
// Initialize file size and fileTypes map
var fileSize int64
2024-11-21 00:15:30 +00:00
extension := filepath.Ext(lowerFileName)
2024-10-07 22:44:53 +00:00
// Collect file types
for _, k := range AllFiletypeOptions {
if IsMatchingType(extension, k) {
fileTypes[k] = true
}
2024-10-07 22:44:53 +00:00
}
2024-11-21 00:15:30 +00:00
isDir := fi.Type == "directory"
2024-10-07 22:44:53 +00:00
fileTypes["dir"] = isDir
2024-11-21 00:15:30 +00:00
fileSize = fi.Size
2024-10-07 22:44:53 +00:00
// Evaluate all conditions
for t, v := range conditions {
if t == "exact" {
continue
}
switch t {
case "larger":
if largerThan > 0 {
if fileSize <= largerThan {
2024-11-21 00:15:30 +00:00
return false
}
2024-10-07 22:44:53 +00:00
}
case "smaller":
if smallerThan > 0 {
if fileSize >= smallerThan {
2024-11-21 00:15:30 +00:00
return false
}
}
2024-10-07 22:44:53 +00:00
default:
// Handle other file type conditions
notMatchType := v != fileTypes[t]
if notMatchType {
2024-11-21 00:15:30 +00:00
return false
2023-07-31 22:20:14 +00:00
}
}
}
2024-11-21 00:15:30 +00:00
return true
2023-06-18 15:04:31 +00:00
}
2025-01-05 19:05:33 +00:00
func (idx *Index) getDirsInScope(scope string) []string {
2024-11-21 00:15:30 +00:00
newList := []string{}
2025-01-05 19:05:33 +00:00
idx.mu.Lock()
defer idx.mu.Unlock()
for k := range idx.Directories {
2024-11-21 00:15:30 +00:00
if strings.HasPrefix(k, scope) || scope == "" {
newList = append(newList, k)
}
}
2024-11-21 00:15:30 +00:00
return newList
}