2023-12-01 23:47:00 +00:00
|
|
|
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 (
|
2023-09-30 14:18:21 +00:00
|
|
|
sessionInProgress sync.Map
|
2023-12-01 23:47:00 +00:00
|
|
|
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 {
|
2023-12-20 20:44:25 +00:00
|
|
|
// 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)
|
2023-07-21 22:41:24 +00:00
|
|
|
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)
|
2023-12-01 23:47:00 +00:00
|
|
|
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
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
|
|
|
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,
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
2024-11-21 00:15:30 +00:00
|
|
|
matches := reducedDir.containsSearchTerm(searchTerm, searchOptions)
|
2023-12-01 23:47:00 +00:00
|
|
|
if matches {
|
2024-12-17 00:01:55 +00:00
|
|
|
results[scopedPath] = SearchResult{Path: scopedPath, Type: "directory", Size: dir.Size}
|
2023-12-01 23:47:00 +00:00
|
|
|
count++
|
2023-09-30 14:18:21 +00:00
|
|
|
}
|
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-12-01 23:47:00 +00:00
|
|
|
}
|
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
|
|
|
}
|
2023-09-30 14:18:21 +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-21 22:41:24 +00:00
|
|
|
}
|
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 {
|
2023-06-15 15:30:49 +00:00
|
|
|
|
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
|
2023-07-21 22:41:24 +00:00
|
|
|
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"] {
|
2023-10-09 22:24:48 +00:00
|
|
|
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
|
2023-10-09 22:24:48 +00:00
|
|
|
}
|
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
|
2023-10-09 22:24:48 +00:00
|
|
|
}
|
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
|
2023-10-09 22:24:48 +00:00
|
|
|
}
|
2023-07-21 22:41:24 +00:00
|
|
|
}
|
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
|
|
|
}
|
2023-07-21 22:41:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-21 00:15:30 +00:00
|
|
|
return true
|
2023-06-18 15:04:31 +00:00
|
|
|
}
|
2023-07-21 22:41:24 +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)
|
|
|
|
}
|
2023-07-21 22:41:24 +00:00
|
|
|
}
|
2024-11-21 00:15:30 +00:00
|
|
|
return newList
|
2023-07-21 22:41:24 +00:00
|
|
|
}
|