diff --git a/kernel/cache/asset.go b/kernel/cache/asset.go new file mode 100644 index 000000000..d66fd6a95 --- /dev/null +++ b/kernel/cache/asset.go @@ -0,0 +1,59 @@ +// SiYuan - Build Your Eternal Digital Garden +// Copyright (c) 2020-present, b3log.org +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cache + +import ( + "io/fs" + "path/filepath" + "strings" + "sync" + + "github.com/siyuan-note/siyuan/kernel/util" +) + +type Asset struct { + HName string `json:"hName"` + Path string `json:"path"` + Updated int64 `json:"updated"` +} + +var Assets = sync.Map{} + +func LoadAssets() { + Assets = sync.Map{} + assets := filepath.Join(util.DataDir, "assets") + filepath.Walk(assets, func(path string, info fs.FileInfo, err error) error { + if info.IsDir() { + if strings.HasPrefix(info.Name(), ".") { + return filepath.SkipDir + } + return nil + } + if strings.HasSuffix(info.Name(), ".sya") { + return nil + } + + hName := util.RemoveID(info.Name()) + path = filepath.ToSlash(strings.TrimPrefix(path, util.DataDir))[1:] + Assets.Store(path, &Asset{ + HName: hName, + Path: path, + Updated: info.ModTime().UnixMilli(), + }) + return nil + }) +} diff --git a/kernel/main.go b/kernel/main.go index 493bd2e1f..3f39a34ae 100644 --- a/kernel/main.go +++ b/kernel/main.go @@ -19,6 +19,7 @@ package main import ( + "github.com/siyuan-note/siyuan/kernel/cache" "github.com/siyuan-note/siyuan/kernel/model" "github.com/siyuan-note/siyuan/kernel/server" "github.com/siyuan-note/siyuan/kernel/sql" @@ -48,6 +49,7 @@ func main() { go model.AutoFlushTx() go sql.AutoFlushTreeQueue() go treenode.AutoFlushBlockTree() + go cache.LoadAssets() model.WatchAssets() model.HandleSignal() } diff --git a/kernel/mobile/kernel.go b/kernel/mobile/kernel.go index b93e7731c..edfc00dfb 100644 --- a/kernel/mobile/kernel.go +++ b/kernel/mobile/kernel.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "github.com/siyuan-note/siyuan/kernel/cache" "github.com/siyuan-note/siyuan/kernel/model" "github.com/siyuan-note/siyuan/kernel/server" "github.com/siyuan-note/siyuan/kernel/sql" @@ -61,6 +62,7 @@ func StartKernel(container, appDir, workspaceDir, nativeLibDir, privateDataDir, go model.AutoFlushTx() go sql.AutoFlushTreeQueue() go treenode.AutoFlushBlockTree() + go cache.LoadAssets() }() } diff --git a/kernel/model/assets.go b/kernel/model/assets.go index 0d0bd2bbe..05474f7b1 100644 --- a/kernel/model/assets.go +++ b/kernel/model/assets.go @@ -37,6 +37,7 @@ import ( "github.com/gabriel-vasile/mimetype" "github.com/siyuan-note/filelock" "github.com/siyuan-note/httpclient" + "github.com/siyuan-note/siyuan/kernel/cache" "github.com/siyuan-note/siyuan/kernel/search" "github.com/siyuan-note/siyuan/kernel/sql" "github.com/siyuan-note/siyuan/kernel/treenode" @@ -161,25 +162,32 @@ func NetImg2LocalAssets(rootID string) (err error) { return } -type Asset struct { - HName string `json:"hName"` - Name string `json:"name"` - Path string `json:"path"` -} +func SearchAssetsByName(keyword string) (ret []*cache.Asset) { + ret = []*cache.Asset{} -func SearchAssetsByName(keyword string) (ret []*Asset) { - ret = []*Asset{} - sqlAssets := sql.QueryAssetsByName(keyword) - for _, sqlAsset := range sqlAssets { - hName := util.RemoveID(sqlAsset.Name) - _, hName = search.MarkText(hName, keyword, 64, Conf.Search.CaseSensitive) - asset := &Asset{ - HName: hName, - Name: sqlAsset.Name, - Path: sqlAsset.Path, + count := 0 + cache.Assets.Range(func(k, v interface{}) bool { + asset := v.(*cache.Asset) + if !strings.Contains(strings.ToLower(asset.HName), strings.ToLower(keyword)) { + return true } - ret = append(ret, asset) - } + + _, hName := search.MarkText(asset.HName, keyword, 64, Conf.Search.CaseSensitive) + ret = append(ret, &cache.Asset{ + HName: hName, + Path: asset.Path, + Updated: asset.Updated, + }) + count++ + if Conf.Search.Limit <= count { + return false + } + return true + }) + + sort.Slice(ret, func(i, j int) bool { + return ret[i].Updated > ret[j].Updated + }) return } diff --git a/kernel/model/assets_watcher.go b/kernel/model/assets_watcher.go index 01fff5209..a1163971e 100644 --- a/kernel/model/assets_watcher.go +++ b/kernel/model/assets_watcher.go @@ -23,6 +23,7 @@ import ( "time" "github.com/fsnotify/fsnotify" + "github.com/siyuan-note/siyuan/kernel/cache" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -78,6 +79,9 @@ func watchAssets() { // 外部修改已有资源文件后纳入云端同步 https://github.com/siyuan-note/siyuan/issues/4694 IncSync() } + + // 重新缓存资源文件,以便使用 /资源 搜索 + cache.LoadAssets() } } }() diff --git a/kernel/model/assets_watcher_darwin.go b/kernel/model/assets_watcher_darwin.go index 706d3ffc0..8ad177e8e 100644 --- a/kernel/model/assets_watcher_darwin.go +++ b/kernel/model/assets_watcher_darwin.go @@ -23,6 +23,7 @@ import ( "time" "github.com/radovskyb/watcher" + "github.com/siyuan-note/siyuan/kernel/cache" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -54,6 +55,9 @@ func watchAssets() { if watcher.Write == event.Op { IncSync() } + + // 重新缓存资源文件,以便使用 /资源 搜索 + cache.LoadAssets() case err, ok := <-assetsWatcher.Error: if !ok { return diff --git a/kernel/sql/aseet.go b/kernel/sql/aseet.go index 1d5daadd9..c15db8058 100644 --- a/kernel/sql/aseet.go +++ b/kernel/sql/aseet.go @@ -93,22 +93,6 @@ func docTitleImgAsset(root *ast.Node) *Asset { return nil } -func QueryAssetsByName(name string) (ret []*Asset) { - ret = []*Asset{} - sqlStmt := "SELECT * FROM assets WHERE name LIKE ? GROUP BY id ORDER BY id DESC LIMIT 32" - rows, err := query(sqlStmt, "%"+name+"%") - if nil != err { - util.LogErrorf("sql query [%s] failed: %s", sqlStmt, err) - return - } - defer rows.Close() - for rows.Next() { - asset := scanAssetRows(rows) - ret = append(ret, asset) - } - return -} - func QueryAssetByHash(hash string) (ret *Asset) { sqlStmt := "SELECT * FROM assets WHERE hash = ?" row := queryRow(sqlStmt, hash)