filebrowser/backend/files/search.go

183 lines
4.5 KiB
Go
Raw 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
"github.com/gtsteffaniak/filebrowser/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-11-21 00:15:30 +00:00
type searchResult struct {
Path string `json:"path"`
Type string `json:"type"`
Size int64 `json:"size"`
}
func (si *Index) Search(search string, scope string, sourceSession string) []searchResult {
// Remove slashes
2024-11-21 00:15:30 +00:00
scope = si.makeIndexPath(scope)
runningHash := utils.GenerateRandomHash(4)
sessionInProgress.Store(sourceSession, runningHash) // Store the value in the sync.Map
2023-06-18 15:04:31 +00:00
searchOptions := ParseSearch(search)
2024-11-21 00:15:30 +00:00
results := make(map[string]searchResult, 0)
count := 0
2024-11-26 17:21:41 +00:00
var directories []string
cachedDirs, ok := utils.SearchResultsCache.Get(si.Root + scope).([]string)
if ok {
directories = cachedDirs
} else {
directories = si.getDirsInScope(scope)
utils.SearchResultsCache.Set(si.Root+scope, directories)
}
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
}
si.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), "/") + "/"
2024-11-21 00:15:30 +00:00
si.mu.Unlock()
dir, found := si.GetReducedMetadata(dirName, true)
si.mu.Lock()
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-11-21 00:15:30 +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 {
2024-11-21 00:15:30 +00:00
si.mu.Unlock()
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 {
results[scopedPath] = searchResult{Path: scopedPath, Type: item.Type, Size: item.Size}
count++
}
2023-07-13 02:23:29 +00:00
}
2023-06-15 01:08:09 +00:00
}
2024-10-07 22:44:53 +00:00
si.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 "/"
sortedKeys := make([]searchResult, 0, len(results))
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
}
2024-11-21 00:15:30 +00:00
func (si *Index) getDirsInScope(scope string) []string {
newList := []string{}
si.mu.Lock()
defer si.mu.Unlock()
for k := range si.Directories {
if strings.HasPrefix(k, scope) || scope == "" {
newList = append(newList, k)
}
}
2024-11-21 00:15:30 +00:00
return newList
}