mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-28 04:05:21 +08:00
feature (search): fallback search when full text search isn't active
This commit is contained in:
@ -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>
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
144
server/model/search_stateless.go
Normal file
144
server/model/search_stateless.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user