feature (search): fallback search when full text search isn't active

This commit is contained in:
Mickael Kerjean
2020-07-01 00:23:17 +10:00
parent 4cbeb2c095
commit 86dd9da853
5 changed files with 161 additions and 14 deletions

View File

@ -139,7 +139,7 @@ export class Submenu extends React.Component {
</DropdownList>
</Dropdown>
<div className="view list-grid" onClick={this.onViewChange.bind(this)}><Icon name={this.props.view === "grid" ? "list" : "grid"}/></div>
<NgIf cond={window.CONFIG.enable_search === true} className="view">
<div className="view">
<form onSubmit={(e) => this.onSearchKeypress(this.state.search_keyword, false, e)}>
<label className="view search" onClick={this.onSearchToggle.bind(this, null)}>
<NgIf cond={this.state.search_input_visible !== true}>
@ -154,7 +154,7 @@ export class Submenu extends React.Component {
<label htmlFor="search" className="hidden">{ t("search") }</label>
</NgIf>
</form>
</NgIf>
</div>
</div>
</Container>
</div>

View File

@ -354,7 +354,6 @@ func (this Configuration) Export() interface{} {
RememberMe bool `json:"remember_me"`
UploadButton bool `json:"upload_button"`
Connections interface{} `json:"connections"`
EnableSearch bool `json:"enable_search"`
EnableShare bool `json:"enable_share"`
MimeTypes map[string]string `json:"mime"`
}{
@ -366,7 +365,6 @@ func (this Configuration) Export() interface{} {
RememberMe: this.Get("general.remember_me").Bool(),
UploadButton: this.Get("general.upload_button").Bool(),
Connections: this.Conn,
EnableSearch: this.Get("features.search.enable").Bool(),
EnableShare: this.Get("features.share.enable").Bool(),
MimeTypes: AllMimeTypes(),
}

View File

@ -8,11 +8,6 @@ import (
)
func FileSearch(ctx App, res http.ResponseWriter, req *http.Request) {
if Config.Get("features.search.enable").Bool() == false {
SendErrorResult(res, ErrNotAllowed)
return
}
path, err := PathBuilder(ctx, req.URL.Query().Get("path"))
if err != nil {
path = "/"
@ -22,10 +17,20 @@ func FileSearch(ctx App, res http.ResponseWriter, req *http.Request) {
SendErrorResult(res, ErrPermissionDenied)
return
}
searchResults := model.Search(&ctx, path, q)
for i:=0; i<len(searchResults); i++ {
var searchResults []File
if Config.Get("features.search.enable").Bool() {
searchResults = model.SearchStateFull(&ctx, path, q)
} else {
searchResults = model.SearchStateLess(&ctx, path, q)
}
if ctx.Session["path"] != "" {
searchResults[i].FPath = "/" + strings.TrimPrefix(searchResults[i].FPath, ctx.Session["path"])
for i:=0; i<len(searchResults); i++ {
searchResults[i].FPath = "/" + strings.TrimPrefix(
searchResults[i].FPath,
ctx.Session["path"],
)
}
}
SendSuccessResults(res, searchResults)

View File

@ -175,7 +175,7 @@ func init(){
}
}
func Search(app *App, path string, keyword string) []File {
func SearchStateFull(app *App, path string, keyword string) []File {
var files []File = make([]File, 0)
// extract our search indexer

View File

@ -0,0 +1,144 @@
package model
import (
. "github.com/mickael-kerjean/filestash/server/common"
"os"
"path/filepath"
"strings"
"time"
)
type PathQuandidate struct {
Path string
Score int
}
func scoreBoostForPath(p string) int {
b := strings.ToLower(filepath.Base(p))
// some path are garbage we don't want to explore unless there's nothing else to do
if b == "node_modules" {
return -100
} else if strings.HasPrefix(b, ".") {
return -10
}
// not all path are equally interesting, we bump the score of what we thing is interesting
score := 0
if strings.Contains(b, "document") {
score += 3
} else if strings.Contains(b, "project") {
score += 3
} else if strings.Contains(b, "home") {
score += 3
} else if strings.Contains(b, "note") {
score += 3
}
return score
}
func scoreBoostForFilesInDirectory(f []os.FileInfo) int {
s := 0
for i:=0; i<len(f); i++ {
name := f[i].Name()
if f[i].IsDir() == false {
if strings.HasSuffix(name, ".org") {
s += 2
} else if strings.HasSuffix(name, ".pdf") {
s += 1
} else if strings.HasSuffix(name, ".doc") || strings.HasSuffix(name, ".docx") {
s += 1
} else if strings.HasSuffix(name, ".md") {
s += 1
} else if strings.HasSuffix(name, ".pdf") {
s += 1
}
}
if s > 4 {
return 4
}
}
return s
}
func scoreBoostOnDepth(p string) int {
return - strings.Count(p, "/")
}
func SearchStateLess(app *App, path string, keyword string) []File {
files := make([]File, 0)
toVisit := []PathQuandidate{PathQuandidate{path, 0}}
MAX_SEARCH_TIME := 300 * time.Millisecond
for start := time.Now() ; time.Since(start) < MAX_SEARCH_TIME; {
if len(toVisit) == 0 {
return files
}
currentPath := toVisit[0]
if len(toVisit) == 0 {
toVisit = make([]PathQuandidate, 0)
} else {
toVisit = toVisit[1:]
}
// Ls on the directory
f, err := app.Backend.Ls(currentPath.Path)
if err != nil {
continue
}
score1 := scoreBoostForFilesInDirectory(f)
for i:=0; i<len(f); i++ {
name := f[i].Name()
// keyword matching
isAMatch := true
for _, key := range strings.Split(keyword, " "){
if strings.Contains(strings.ToLower(name), strings.ToLower(key)) == false {
isAMatch = false
}
}
if isAMatch {
files = append(files, File{
FName: name,
FType: func() string {
if f[i].IsDir() {
return "directory"
}
return "file"
}(),
FSize: f[i].Size(),
FTime: f[i].ModTime().Unix() * 1000,
FPath: currentPath.Path + name,
})
}
// follow directories
fullpath := currentPath.Path + name + "/"
relativePath := strings.ToLower(strings.TrimSuffix(strings.TrimPrefix(fullpath, path), "/"))
score2 := scoreBoostOnDepth(relativePath) * 2
if f[i].IsDir() {
score := scoreBoostForPath(relativePath)
if score < 0 {
continue
}
score += score1
score += score2
score += currentPath.Score
t := make([]PathQuandidate, len(toVisit) + 1)
k := 0
for k=0; k<len(toVisit); k++{
if score > toVisit[k].Score {
break
}
t[k] = toVisit[k]
}
t[k] = PathQuandidate{fullpath, score}
for k=k+1; k<len(toVisit) + 1; k++ {
t[k] = toVisit[k - 1]
}
toVisit = t
}
}
}
return files
}