2023-06-24 20:39:55 +08:00
|
|
|
|
// SiYuan - Refactor your thinking
|
2023-01-26 12:34:57 +08:00
|
|
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
package model
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
2024-11-21 10:59:29 +08:00
|
|
|
|
"io/fs"
|
2023-01-26 12:34:57 +08:00
|
|
|
|
"os"
|
|
|
|
|
|
"path"
|
|
|
|
|
|
"path/filepath"
|
2023-02-04 14:58:35 +08:00
|
|
|
|
"runtime/debug"
|
2023-01-26 12:34:57 +08:00
|
|
|
|
"strings"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2023-01-26 13:47:12 +08:00
|
|
|
|
"github.com/88250/gulu"
|
2023-02-10 14:28:10 +08:00
|
|
|
|
"github.com/88250/lute"
|
2023-01-26 12:34:57 +08:00
|
|
|
|
"github.com/88250/lute/ast"
|
|
|
|
|
|
"github.com/88250/lute/html"
|
|
|
|
|
|
"github.com/88250/lute/parse"
|
2024-01-21 21:27:50 +08:00
|
|
|
|
"github.com/siyuan-note/filelock"
|
2023-01-26 12:34:57 +08:00
|
|
|
|
"github.com/siyuan-note/logging"
|
2023-02-10 14:28:10 +08:00
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/filesys"
|
2023-01-26 12:34:57 +08:00
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/sql"
|
|
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/task"
|
|
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/treenode"
|
|
|
|
|
|
"github.com/siyuan-note/siyuan/kernel/util"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2024-03-27 09:48:06 +08:00
|
|
|
|
var (
|
2024-05-20 10:15:50 +08:00
|
|
|
|
checkIndexOnce = sync.Once{}
|
2024-03-27 09:48:06 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// checkIndex 自动校验数据库索引,仅在数据同步执行完成后执行一次。
|
|
|
|
|
|
func checkIndex() {
|
2024-05-20 10:15:50 +08:00
|
|
|
|
checkIndexOnce.Do(func() {
|
2024-12-23 17:19:49 +08:00
|
|
|
|
if util.ContainerAndroid == util.Container || util.ContainerIOS == util.Container || util.ContainerHarmony == util.Container {
|
|
|
|
|
|
// 移动端不执行校验 https://ld246.com/article/1734939896061
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-20 10:15:50 +08:00
|
|
|
|
logging.LogInfof("start checking index...")
|
2024-03-27 09:48:06 +08:00
|
|
|
|
|
2024-11-17 09:35:48 +08:00
|
|
|
|
removeDuplicateDatabaseIndex()
|
2024-10-14 20:39:16 +08:00
|
|
|
|
sql.FlushQueue()
|
2023-02-07 10:05:20 +08:00
|
|
|
|
|
2024-11-17 09:35:48 +08:00
|
|
|
|
resetDuplicateBlocksOnFileSys()
|
|
|
|
|
|
sql.FlushQueue()
|
2023-02-11 23:51:32 +08:00
|
|
|
|
|
2024-11-17 09:35:48 +08:00
|
|
|
|
fixBlockTreeByFileSys()
|
2024-10-14 20:39:16 +08:00
|
|
|
|
sql.FlushQueue()
|
2023-02-07 09:58:13 +08:00
|
|
|
|
|
2024-11-17 09:35:48 +08:00
|
|
|
|
fixDatabaseIndexByBlockTree()
|
2024-10-14 20:39:16 +08:00
|
|
|
|
sql.FlushQueue()
|
2023-02-07 10:05:20 +08:00
|
|
|
|
|
2024-11-17 09:35:48 +08:00
|
|
|
|
removeDuplicateDatabaseRefs()
|
2023-11-10 11:01:06 +08:00
|
|
|
|
|
2024-05-20 10:15:50 +08:00
|
|
|
|
// 后面要加任务的话记得修改推送任务栏的进度 util.PushStatusBar(fmt.Sprintf(Conf.Language(58), 1, 5))
|
2023-11-24 21:18:09 +08:00
|
|
|
|
|
2024-05-20 10:15:50 +08:00
|
|
|
|
debug.FreeOSMemory()
|
2024-11-17 09:35:48 +08:00
|
|
|
|
util.PushStatusBar(Conf.Language(185))
|
2024-05-20 10:15:50 +08:00
|
|
|
|
logging.LogInfof("finish checking index")
|
2023-11-24 21:18:09 +08:00
|
|
|
|
})
|
2023-01-26 12:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 11:01:06 +08:00
|
|
|
|
// removeDuplicateDatabaseRefs 删除重复的数据库引用关系。
|
|
|
|
|
|
func removeDuplicateDatabaseRefs() {
|
|
|
|
|
|
defer logging.Recover()
|
|
|
|
|
|
|
2023-11-24 21:18:09 +08:00
|
|
|
|
util.PushStatusBar(fmt.Sprintf(Conf.Language(58), 5, 5))
|
2023-11-10 11:01:06 +08:00
|
|
|
|
duplicatedRootIDs := sql.GetRefDuplicatedDefRootIDs()
|
|
|
|
|
|
for _, rootID := range duplicatedRootIDs {
|
|
|
|
|
|
refreshRefsByDefID(rootID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-16 20:34:50 +08:00
|
|
|
|
for _, rootID := range duplicatedRootIDs {
|
|
|
|
|
|
logging.LogWarnf("exist more than one ref duplicated [%s], reindex it", rootID)
|
2023-11-10 11:01:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-07 09:58:13 +08:00
|
|
|
|
// removeDuplicateDatabaseIndex 删除重复的数据库索引。
|
|
|
|
|
|
func removeDuplicateDatabaseIndex() {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
defer logging.Recover()
|
|
|
|
|
|
|
2023-11-24 21:18:09 +08:00
|
|
|
|
util.PushStatusBar(fmt.Sprintf(Conf.Language(58), 1, 5))
|
2023-01-26 13:47:12 +08:00
|
|
|
|
duplicatedRootIDs := sql.GetDuplicatedRootIDs("blocks")
|
|
|
|
|
|
if 1 > len(duplicatedRootIDs) {
|
|
|
|
|
|
duplicatedRootIDs = sql.GetDuplicatedRootIDs("blocks_fts")
|
|
|
|
|
|
if 1 > len(duplicatedRootIDs) && !Conf.Search.CaseSensitive {
|
|
|
|
|
|
duplicatedRootIDs = sql.GetDuplicatedRootIDs("blocks_fts_case_insensitive")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
roots := sql.GetBlocks(duplicatedRootIDs)
|
|
|
|
|
|
rootMap := map[string]*sql.Block{}
|
|
|
|
|
|
for _, root := range roots {
|
2023-07-21 18:38:12 +08:00
|
|
|
|
if nil == root {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2023-01-26 13:47:12 +08:00
|
|
|
|
rootMap[root.ID] = root
|
|
|
|
|
|
}
|
2023-01-26 19:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
var toRemoveRootIDs []string
|
2023-01-26 17:50:21 +08:00
|
|
|
|
var deletes int
|
2023-01-26 13:47:12 +08:00
|
|
|
|
for _, rootID := range duplicatedRootIDs {
|
|
|
|
|
|
root := rootMap[rootID]
|
|
|
|
|
|
if nil == root {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2023-01-26 17:50:21 +08:00
|
|
|
|
deletes++
|
2023-01-26 19:50:56 +08:00
|
|
|
|
toRemoveRootIDs = append(toRemoveRootIDs, rootID)
|
2023-12-08 13:05:50 +08:00
|
|
|
|
if util.IsExiting.Load() {
|
2023-01-26 13:47:12 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-26 19:50:56 +08:00
|
|
|
|
toRemoveRootIDs = gulu.Str.RemoveDuplicatedElem(toRemoveRootIDs)
|
|
|
|
|
|
sql.BatchRemoveTreeQueue(toRemoveRootIDs)
|
|
|
|
|
|
|
2023-01-26 17:50:21 +08:00
|
|
|
|
if 0 < deletes {
|
|
|
|
|
|
logging.LogWarnf("exist more than one tree duplicated [%d], reindex it", deletes)
|
2023-01-26 13:47:12 +08:00
|
|
|
|
}
|
2023-02-07 09:58:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-13 16:04:36 +08:00
|
|
|
|
// resetDuplicateBlocksOnFileSys 重置重复 ID 的块。 https://github.com/siyuan-note/siyuan/issues/7357
|
|
|
|
|
|
func resetDuplicateBlocksOnFileSys() {
|
2023-02-11 23:51:32 +08:00
|
|
|
|
defer logging.Recover()
|
|
|
|
|
|
|
2023-11-24 21:18:09 +08:00
|
|
|
|
util.PushStatusBar(fmt.Sprintf(Conf.Language(58), 2, 5))
|
2023-02-11 23:51:32 +08:00
|
|
|
|
boxes := Conf.GetBoxes()
|
|
|
|
|
|
luteEngine := lute.New()
|
2023-02-13 16:04:36 +08:00
|
|
|
|
blockIDs := map[string]bool{}
|
2023-02-14 09:27:20 +08:00
|
|
|
|
needRefreshUI := false
|
2023-02-11 23:51:32 +08:00
|
|
|
|
for _, box := range boxes {
|
2023-02-15 09:35:46 +08:00
|
|
|
|
// 校验索引阶段自动删除历史遗留的笔记本 history 文件夹
|
|
|
|
|
|
legacyHistory := filepath.Join(util.DataDir, box.ID, ".siyuan", "history")
|
|
|
|
|
|
if gulu.File.IsDir(legacyHistory) {
|
|
|
|
|
|
if removeErr := os.RemoveAll(legacyHistory); nil != removeErr {
|
|
|
|
|
|
logging.LogErrorf("remove legacy history failed: %s", removeErr)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logging.LogInfof("removed legacy history [%s]", legacyHistory)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-11 23:51:32 +08:00
|
|
|
|
boxPath := filepath.Join(util.DataDir, box.ID)
|
2023-02-17 21:27:05 +08:00
|
|
|
|
var duplicatedTrees []*parse.Tree
|
2024-11-21 10:59:29 +08:00
|
|
|
|
filelock.Walk(boxPath, func(path string, d fs.DirEntry, err error) error {
|
|
|
|
|
|
if err != nil || nil == d {
|
2024-01-20 20:56:14 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-21 10:59:29 +08:00
|
|
|
|
if d.IsDir() {
|
2023-02-16 09:29:10 +08:00
|
|
|
|
if boxPath == path {
|
2024-02-06 14:42:40 +08:00
|
|
|
|
// 跳过笔记本文件夹
|
2023-02-16 09:29:10 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-21 10:59:29 +08:00
|
|
|
|
if strings.HasPrefix(d.Name(), ".") {
|
2023-02-15 09:35:46 +08:00
|
|
|
|
return filepath.SkipDir
|
|
|
|
|
|
}
|
2023-02-16 09:29:10 +08:00
|
|
|
|
|
2024-11-21 10:59:29 +08:00
|
|
|
|
if !ast.IsNodeIDPattern(d.Name()) {
|
2023-02-16 09:29:10 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2023-02-15 09:35:46 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if filepath.Ext(path) != ".sy" || strings.Contains(filepath.ToSlash(path), "/assets/") {
|
2023-02-13 16:04:36 +08:00
|
|
|
|
return nil
|
2023-02-11 23:51:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-21 10:59:29 +08:00
|
|
|
|
if !ast.IsNodeIDPattern(strings.TrimSuffix(d.Name(), ".sy")) {
|
2023-02-13 16:04:36 +08:00
|
|
|
|
logging.LogWarnf("invalid .sy file name [%s]", path)
|
|
|
|
|
|
box.moveCorruptedData(path)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2023-02-11 23:51:32 +08:00
|
|
|
|
|
2023-02-13 16:04:36 +08:00
|
|
|
|
p := path[len(boxPath):]
|
|
|
|
|
|
p = filepath.ToSlash(p)
|
|
|
|
|
|
tree, loadErr := filesys.LoadTree(box.ID, p, luteEngine)
|
|
|
|
|
|
if nil != loadErr {
|
|
|
|
|
|
logging.LogErrorf("load tree [%s] failed: %s", p, loadErr)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2023-02-11 23:51:32 +08:00
|
|
|
|
|
2023-02-13 16:04:36 +08:00
|
|
|
|
needOverwrite := false
|
|
|
|
|
|
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
|
|
|
|
|
if !entering || !n.IsBlock() {
|
|
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if "" == n.ID {
|
|
|
|
|
|
needOverwrite = true
|
2025-03-15 12:43:38 +08:00
|
|
|
|
treenode.ResetNodeID(n)
|
2023-02-13 16:04:36 +08:00
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if !blockIDs[n.ID] {
|
|
|
|
|
|
blockIDs[n.ID] = true
|
|
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 存在重复的块 ID
|
|
|
|
|
|
|
|
|
|
|
|
if ast.NodeDocument == n.Type {
|
2023-02-17 21:27:05 +08:00
|
|
|
|
// 如果是文档根节点,则重置这颗树
|
|
|
|
|
|
// 这里不能在迭代中重置,因为如果这个文档存在子文档的话,重置时会重命名子文档文件夹,后续迭代可能会导致子文档 ID 重复
|
|
|
|
|
|
duplicatedTrees = append(duplicatedTrees, tree)
|
2023-02-13 16:04:36 +08:00
|
|
|
|
return ast.WalkStop
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 其他情况,重置节点 ID
|
|
|
|
|
|
needOverwrite = true
|
2025-03-15 12:43:38 +08:00
|
|
|
|
treenode.ResetNodeID(n)
|
2023-02-14 09:27:20 +08:00
|
|
|
|
needRefreshUI = true
|
2023-02-13 16:04:36 +08:00
|
|
|
|
return ast.WalkContinue
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if needOverwrite {
|
|
|
|
|
|
logging.LogWarnf("exist more than one node with the same id in tree [%s], reset it", box.ID+p)
|
2024-09-28 17:38:50 +08:00
|
|
|
|
if _, writeErr := filesys.WriteTree(tree); nil != writeErr {
|
2023-02-13 16:04:36 +08:00
|
|
|
|
logging.LogErrorf("write tree [%s] failed: %s", p, writeErr)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
})
|
2023-02-17 21:27:05 +08:00
|
|
|
|
|
|
|
|
|
|
for _, tree := range duplicatedTrees {
|
|
|
|
|
|
absPath := filepath.Join(boxPath, tree.Path)
|
|
|
|
|
|
logging.LogWarnf("exist more than one tree with the same id [%s], reset it", absPath)
|
|
|
|
|
|
recreateTree(tree, absPath)
|
|
|
|
|
|
needRefreshUI = true
|
|
|
|
|
|
}
|
2023-02-12 00:23:53 +08:00
|
|
|
|
}
|
2023-02-14 09:27:20 +08:00
|
|
|
|
|
|
|
|
|
|
if needRefreshUI {
|
|
|
|
|
|
util.ReloadUI()
|
2024-09-05 18:07:04 +08:00
|
|
|
|
task.AppendAsyncTaskWithDelay(task.PushMsg, 3*time.Second, util.PushMsg, Conf.Language(190), 5000)
|
2023-02-14 09:27:20 +08:00
|
|
|
|
}
|
2023-02-13 16:04:36 +08:00
|
|
|
|
}
|
2023-02-11 23:51:32 +08:00
|
|
|
|
|
2023-02-13 16:04:36 +08:00
|
|
|
|
func recreateTree(tree *parse.Tree, absPath string) {
|
2023-02-17 21:27:05 +08:00
|
|
|
|
// 删除关于该树的所有块树数据,后面会调用 fixBlockTreeByFileSys() 进行订正补全
|
|
|
|
|
|
treenode.RemoveBlockTreesByPathPrefix(strings.TrimSuffix(tree.Path, ".sy"))
|
|
|
|
|
|
treenode.RemoveBlockTreesByRootID(tree.ID)
|
|
|
|
|
|
|
2024-07-24 11:23:21 +08:00
|
|
|
|
resetTree(tree, "", true)
|
2024-09-28 17:38:50 +08:00
|
|
|
|
if _, err := filesys.WriteTree(tree); err != nil {
|
2023-02-17 21:27:05 +08:00
|
|
|
|
logging.LogWarnf("write tree [%s] failed: %s", tree.Path, err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-13 16:04:36 +08:00
|
|
|
|
if gulu.File.IsDir(strings.TrimSuffix(absPath, ".sy")) {
|
|
|
|
|
|
// 重命名子文档文件夹
|
2023-02-17 21:27:05 +08:00
|
|
|
|
from := strings.TrimSuffix(absPath, ".sy")
|
|
|
|
|
|
to := filepath.Join(filepath.Dir(absPath), tree.ID)
|
|
|
|
|
|
if renameErr := os.Rename(from, to); nil != renameErr {
|
|
|
|
|
|
logging.LogWarnf("rename [%s] failed: %s", from, renameErr)
|
2023-02-13 16:04:36 +08:00
|
|
|
|
return
|
2023-02-11 23:51:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-02-17 21:27:05 +08:00
|
|
|
|
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err := filelock.Remove(absPath); err != nil {
|
2023-02-17 21:27:05 +08:00
|
|
|
|
logging.LogWarnf("remove [%s] failed: %s", absPath, err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-02-11 23:51:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-07 09:58:13 +08:00
|
|
|
|
// fixBlockTreeByFileSys 通过文件系统订正块树。
|
|
|
|
|
|
func fixBlockTreeByFileSys() {
|
|
|
|
|
|
defer logging.Recover()
|
|
|
|
|
|
|
2023-11-24 21:18:09 +08:00
|
|
|
|
util.PushStatusBar(fmt.Sprintf(Conf.Language(58), 3, 5))
|
2023-01-26 12:34:57 +08:00
|
|
|
|
boxes := Conf.GetOpenedBoxes()
|
2023-02-10 14:28:10 +08:00
|
|
|
|
luteEngine := lute.New()
|
2023-01-26 12:34:57 +08:00
|
|
|
|
for _, box := range boxes {
|
|
|
|
|
|
boxPath := filepath.Join(util.DataDir, box.ID)
|
|
|
|
|
|
var paths []string
|
2024-11-21 10:59:29 +08:00
|
|
|
|
filelock.Walk(boxPath, func(path string, d fs.DirEntry, err error) error {
|
|
|
|
|
|
if nil != err || nil == d {
|
2023-02-16 09:29:10 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-21 10:59:29 +08:00
|
|
|
|
if boxPath == path {
|
|
|
|
|
|
// 跳过根路径(笔记本文件夹)
|
2024-01-20 20:59:25 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-21 10:59:29 +08:00
|
|
|
|
if d.IsDir() {
|
|
|
|
|
|
if strings.HasPrefix(d.Name(), ".") {
|
2023-02-15 09:35:46 +08:00
|
|
|
|
return filepath.SkipDir
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if filepath.Ext(path) != ".sy" || strings.Contains(filepath.ToSlash(path), "/assets/") {
|
|
|
|
|
|
return nil
|
2023-01-26 12:34:57 +08:00
|
|
|
|
}
|
2023-02-15 09:35:46 +08:00
|
|
|
|
|
|
|
|
|
|
p := path[len(boxPath):]
|
|
|
|
|
|
p = filepath.ToSlash(p)
|
|
|
|
|
|
paths = append(paths, p)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
size := len(paths)
|
|
|
|
|
|
|
2023-02-01 15:22:09 +08:00
|
|
|
|
// 清理块树中的冗余数据
|
|
|
|
|
|
treenode.ClearRedundantBlockTrees(box.ID, paths)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
|
2023-02-01 15:22:09 +08:00
|
|
|
|
// 重新索引缺失的块树
|
2023-01-26 12:34:57 +08:00
|
|
|
|
missingPaths := treenode.GetNotExistPaths(box.ID, paths)
|
|
|
|
|
|
for i, p := range missingPaths {
|
|
|
|
|
|
id := path.Base(p)
|
|
|
|
|
|
id = strings.TrimSuffix(id, ".sy")
|
|
|
|
|
|
if !ast.IsNodeIDPattern(id) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-10 14:28:10 +08:00
|
|
|
|
reindexTreeByPath(box.ID, p, i, size, luteEngine)
|
2023-12-08 13:05:50 +08:00
|
|
|
|
if util.IsExiting.Load() {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-08 13:05:50 +08:00
|
|
|
|
if util.IsExiting.Load() {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理已关闭的笔记本块树
|
|
|
|
|
|
boxes = Conf.GetClosedBoxes()
|
|
|
|
|
|
for _, box := range boxes {
|
|
|
|
|
|
treenode.RemoveBlockTreesByBoxID(box.ID)
|
|
|
|
|
|
}
|
2023-02-07 09:58:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// fixDatabaseIndexByBlockTree 通过块树订正数据库索引。
|
|
|
|
|
|
func fixDatabaseIndexByBlockTree() {
|
|
|
|
|
|
defer logging.Recover()
|
2023-01-26 12:34:57 +08:00
|
|
|
|
|
2023-11-24 21:18:09 +08:00
|
|
|
|
util.PushStatusBar(fmt.Sprintf(Conf.Language(58), 4, 5))
|
2023-01-26 12:34:57 +08:00
|
|
|
|
rootUpdatedMap := treenode.GetRootUpdated()
|
2023-01-26 13:47:12 +08:00
|
|
|
|
dbRootUpdatedMap, err := sql.GetRootUpdated()
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err == nil {
|
2023-01-26 13:47:12 +08:00
|
|
|
|
reindexTreeByUpdated(rootUpdatedMap, dbRootUpdatedMap)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-26 13:47:12 +08:00
|
|
|
|
func reindexTreeByUpdated(rootUpdatedMap, dbRootUpdatedMap map[string]string) {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
i := -1
|
|
|
|
|
|
size := len(rootUpdatedMap)
|
2023-02-10 14:28:10 +08:00
|
|
|
|
luteEngine := util.NewLute()
|
2023-01-26 12:34:57 +08:00
|
|
|
|
for rootID, updated := range rootUpdatedMap {
|
|
|
|
|
|
i++
|
|
|
|
|
|
|
2023-12-08 13:05:50 +08:00
|
|
|
|
if util.IsExiting.Load() {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rootUpdated := dbRootUpdatedMap[rootID]
|
|
|
|
|
|
if "" == rootUpdated {
|
|
|
|
|
|
//logging.LogWarnf("not found tree [%s] in database, reindex it", rootID)
|
2023-02-10 14:28:10 +08:00
|
|
|
|
reindexTree(rootID, i, size, luteEngine)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if "" == updated {
|
|
|
|
|
|
// BlockTree 迁移,v2.6.3 之前没有 updated 字段
|
2023-02-10 14:28:10 +08:00
|
|
|
|
reindexTree(rootID, i, size, luteEngine)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
btUpdated, _ := time.Parse("20060102150405", updated)
|
|
|
|
|
|
dbUpdated, _ := time.Parse("20060102150405", rootUpdated)
|
|
|
|
|
|
if dbUpdated.Before(btUpdated.Add(-10 * time.Minute)) {
|
|
|
|
|
|
logging.LogWarnf("tree [%s] is not up to date, reindex it", rootID)
|
2023-02-10 14:28:10 +08:00
|
|
|
|
reindexTree(rootID, i, size, luteEngine)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-08 13:05:50 +08:00
|
|
|
|
if util.IsExiting.Load() {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-26 13:47:12 +08:00
|
|
|
|
var rootIDs []string
|
2024-02-06 14:23:17 +08:00
|
|
|
|
for rootID := range dbRootUpdatedMap {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
if _, ok := rootUpdatedMap[rootID]; !ok {
|
2023-01-26 13:47:12 +08:00
|
|
|
|
rootIDs = append(rootIDs, rootID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-08 13:05:50 +08:00
|
|
|
|
if util.IsExiting.Load() {
|
2023-01-26 13:47:12 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
rootIDs = gulu.Str.RemoveDuplicatedElem(rootIDs)
|
|
|
|
|
|
roots := map[string]*sql.Block{}
|
|
|
|
|
|
blocks := sql.GetBlocks(rootIDs)
|
|
|
|
|
|
for _, block := range blocks {
|
|
|
|
|
|
roots[block.RootID] = block
|
|
|
|
|
|
}
|
2023-01-26 19:50:56 +08:00
|
|
|
|
var toRemoveRootIDs []string
|
2023-01-26 13:47:12 +08:00
|
|
|
|
for id, root := range roots {
|
|
|
|
|
|
if nil == root {
|
|
|
|
|
|
continue
|
2023-01-26 12:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-26 19:50:56 +08:00
|
|
|
|
toRemoveRootIDs = append(toRemoveRootIDs, id)
|
2023-12-08 13:05:50 +08:00
|
|
|
|
if util.IsExiting.Load() {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-26 19:50:56 +08:00
|
|
|
|
toRemoveRootIDs = gulu.Str.RemoveDuplicatedElem(toRemoveRootIDs)
|
|
|
|
|
|
//logging.LogWarnf("tree [%s] is not in block tree, remove it from [%s]", id, root.Box)
|
|
|
|
|
|
sql.BatchRemoveTreeQueue(toRemoveRootIDs)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-10 14:28:10 +08:00
|
|
|
|
func reindexTreeByPath(box, p string, i, size int, luteEngine *lute.Lute) {
|
|
|
|
|
|
tree, err := filesys.LoadTree(box, p, luteEngine)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reindexTree0(tree, i, size)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-10 14:28:10 +08:00
|
|
|
|
func reindexTree(rootID string, i, size int, luteEngine *lute.Lute) {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
root := treenode.GetBlockTree(rootID)
|
|
|
|
|
|
if nil == root {
|
2023-02-01 16:51:46 +08:00
|
|
|
|
logging.LogWarnf("root block [%s] not found", rootID)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-10 14:28:10 +08:00
|
|
|
|
tree, err := filesys.LoadTree(root.BoxID, root.Path, luteEngine)
|
2024-09-04 04:40:50 +03:00
|
|
|
|
if err != nil {
|
2023-01-26 12:34:57 +08:00
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
|
// 文件系统上没有找到该 .sy 文件,则订正块树
|
|
|
|
|
|
treenode.RemoveBlockTreesByRootID(rootID)
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reindexTree0(tree, i, size)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func reindexTree0(tree *parse.Tree, i, size int) {
|
|
|
|
|
|
updated := tree.Root.IALAttr("updated")
|
|
|
|
|
|
if "" == updated {
|
|
|
|
|
|
updated = util.TimeFromID(tree.Root.ID)
|
|
|
|
|
|
tree.Root.SetIALAttr("updated", updated)
|
2024-04-11 21:54:34 +08:00
|
|
|
|
indexWriteTreeUpsertQueue(tree)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
} else {
|
2024-06-20 22:53:27 +08:00
|
|
|
|
treenode.UpsertBlockTree(tree)
|
2024-03-15 22:53:37 +08:00
|
|
|
|
sql.IndexTreeQueue(tree)
|
2023-01-26 12:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if 0 == i%64 {
|
2023-05-15 14:56:12 +08:00
|
|
|
|
util.PushStatusBar(fmt.Sprintf(Conf.Language(183), i, size, html.EscapeString(path.Base(tree.HPath))))
|
2023-01-26 12:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|