From ecd4a58d03232d43f4ba49be7bceb9cbfa354f25 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 29 Jun 2023 18:06:04 +0800 Subject: [PATCH 1/4] :art: `Replace All` is no longer affected by pagination https://github.com/siyuan-note/siyuan/issues/8265 --- kernel/api/search.go | 48 ++++++++++++++++++++++++------------------ kernel/model/search.go | 9 +++++++- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/kernel/api/search.go b/kernel/api/search.go index 17f33cb20..f99868a7b 100644 --- a/kernel/api/search.go +++ b/kernel/api/search.go @@ -35,19 +35,16 @@ func findReplace(c *gin.Context) { return } + _, _, paths, boxes, types, method, orderBy, groupBy := parseSearchArgs(arg) + k := arg["k"].(string) r := arg["r"].(string) - methodArg := arg["method"] - var method int // 0:文本,1:查询语法,2:SQL,3:正则表达式 - if nil != methodArg { - method = int(methodArg.(float64)) - } idsArg := arg["ids"].([]interface{}) var ids []string for _, id := range idsArg { ids = append(ids, id.(string)) } - err := model.FindReplace(k, r, ids, method) + err := model.FindReplace(k, r, ids, paths, boxes, types, method, orderBy, groupBy) if nil != err { ret.Code = -1 ret.Msg = err.Error() @@ -218,7 +215,18 @@ func fullTextSearchBlock(c *gin.Context) { return } - page := 1 + page, query, paths, boxes, types, method, orderBy, groupBy := parseSearchArgs(arg) + blocks, matchedBlockCount, matchedRootCount, pageCount := model.FullTextSearchBlock(query, boxes, paths, types, method, orderBy, groupBy, page) + ret.Data = map[string]interface{}{ + "blocks": blocks, + "matchedBlockCount": matchedBlockCount, + "matchedRootCount": matchedRootCount, + "pageCount": pageCount, + } +} + +func parseSearchArgs(arg map[string]interface{}) (page int, query string, paths, boxes []string, types map[string]bool, method, orderBy, groupBy int) { + page = 1 if nil != arg["page"] { page = int(arg["page"].(float64)) } @@ -226,9 +234,12 @@ func fullTextSearchBlock(c *gin.Context) { page = 1 } - query := arg["query"].(string) + queryArg := arg["query"] + if nil != queryArg { + query = queryArg.(string) + } + pathsArg := arg["paths"] - var paths, boxes []string if nil != pathsArg { for _, p := range pathsArg.([]interface{}) { path := p.(string) @@ -244,7 +255,7 @@ func fullTextSearchBlock(c *gin.Context) { paths = gulu.Str.RemoveDuplicatedElem(paths) boxes = gulu.Str.RemoveDuplicatedElem(boxes) } - var types map[string]bool + if nil != arg["types"] { typesArg := arg["types"].(map[string]interface{}) types = map[string]bool{} @@ -252,26 +263,23 @@ func fullTextSearchBlock(c *gin.Context) { types[t] = b.(bool) } } + + // method:0:关键字,1:查询语法,2:SQL,3:正则表达式 methodArg := arg["method"] - var method int // 0:关键字,1:查询语法,2:SQL,3:正则表达式 if nil != methodArg { method = int(methodArg.(float64)) } + + // orderBy:0:按块类型(默认),1:按创建时间升序,2:按创建时间降序,3:按更新时间升序,4:按更新时间降序,5:按内容顺序(仅在按文档分组时),6:按相关度升序,7:按相关度降序 orderByArg := arg["orderBy"] - var orderBy int // 0:按块类型(默认),1:按创建时间升序,2:按创建时间降序,3:按更新时间升序,4:按更新时间降序,5:按内容顺序(仅在按文档分组时),6:按相关度升序,7:按相关度降序 if nil != orderByArg { orderBy = int(orderByArg.(float64)) } + + // groupBy: 0:不分组,1:按文档分组 groupByArg := arg["groupBy"] - var groupBy int // 0:不分组,1:按文档分组 if nil != groupByArg { groupBy = int(groupByArg.(float64)) } - blocks, matchedBlockCount, matchedRootCount, pageCount := model.FullTextSearchBlock(query, boxes, paths, types, method, orderBy, groupBy, page) - ret.Data = map[string]interface{}{ - "blocks": blocks, - "matchedBlockCount": matchedBlockCount, - "matchedRootCount": matchedRootCount, - "pageCount": pageCount, - } + return } diff --git a/kernel/model/search.go b/kernel/model/search.go index b255b8d28..fe213b878 100644 --- a/kernel/model/search.go +++ b/kernel/model/search.go @@ -207,7 +207,7 @@ func SearchRefBlock(id, rootID, keyword string, beforeLen int, isSquareBrackets return } -func FindReplace(keyword, replacement string, ids []string, method int) (err error) { +func FindReplace(keyword, replacement string, ids []string, paths, boxes []string, types map[string]bool, method, orderBy, groupBy int) (err error) { // method:0:文本,1:查询语法,2:SQL,3:正则表达式 if 1 == method || 2 == method { err = errors.New(Conf.Language(132)) @@ -232,6 +232,13 @@ func FindReplace(keyword, replacement string, ids []string, method int) (err err return } + if 1 > len(ids) { + blocks, _, _, _ := FullTextSearchBlock(keyword, boxes, paths, types, method, orderBy, groupBy, 1) + for _, block := range blocks { + ids = append(ids, block.ID) + } + } + for _, id := range ids { bt := treenode.GetBlockTree(id) if nil == bt { From fa8e910bedde03ab4266038c8c666fae57a664a7 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 29 Jun 2023 18:06:18 +0800 Subject: [PATCH 2/4] :art: `Replace All` is no longer affected by pagination https://github.com/siyuan-note/siyuan/issues/8265 --- kernel/model/search.go | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/model/search.go b/kernel/model/search.go index fe213b878..b49e0500b 100644 --- a/kernel/model/search.go +++ b/kernel/model/search.go @@ -233,6 +233,7 @@ func FindReplace(keyword, replacement string, ids []string, paths, boxes []strin } if 1 > len(ids) { + // `Replace All` is no longer affected by pagination https://github.com/siyuan-note/siyuan/issues/8265 blocks, _, _, _ := FullTextSearchBlock(keyword, boxes, paths, types, method, orderBy, groupBy, 1) for _, block := range blocks { ids = append(ids, block.ID) From c49ac8990c1ad8a57eb1b66c6ced7c8e965cc987 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 29 Jun 2023 18:14:36 +0800 Subject: [PATCH 3/4] :art: `Replace All` is no longer affected by pagination https://github.com/siyuan-note/siyuan/issues/8265 --- kernel/api/search.go | 16 ++++++++++++---- kernel/model/search.go | 39 ++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/kernel/api/search.go b/kernel/api/search.go index f99868a7b..2db996a5b 100644 --- a/kernel/api/search.go +++ b/kernel/api/search.go @@ -35,7 +35,7 @@ func findReplace(c *gin.Context) { return } - _, _, paths, boxes, types, method, orderBy, groupBy := parseSearchArgs(arg) + _, _, _, paths, boxes, types, method, orderBy, groupBy := parseSearchArgs(arg) k := arg["k"].(string) r := arg["r"].(string) @@ -215,8 +215,8 @@ func fullTextSearchBlock(c *gin.Context) { return } - page, query, paths, boxes, types, method, orderBy, groupBy := parseSearchArgs(arg) - blocks, matchedBlockCount, matchedRootCount, pageCount := model.FullTextSearchBlock(query, boxes, paths, types, method, orderBy, groupBy, page) + page, pageSize, query, paths, boxes, types, method, orderBy, groupBy := parseSearchArgs(arg) + blocks, matchedBlockCount, matchedRootCount, pageCount := model.FullTextSearchBlock(query, boxes, paths, types, method, orderBy, groupBy, page, pageSize) ret.Data = map[string]interface{}{ "blocks": blocks, "matchedBlockCount": matchedBlockCount, @@ -225,7 +225,7 @@ func fullTextSearchBlock(c *gin.Context) { } } -func parseSearchArgs(arg map[string]interface{}) (page int, query string, paths, boxes []string, types map[string]bool, method, orderBy, groupBy int) { +func parseSearchArgs(arg map[string]interface{}) (page, pageSize int, query string, paths, boxes []string, types map[string]bool, method, orderBy, groupBy int) { page = 1 if nil != arg["page"] { page = int(arg["page"].(float64)) @@ -234,6 +234,14 @@ func parseSearchArgs(arg map[string]interface{}) (page int, query string, paths, page = 1 } + pageSize = 32 + if nil != arg["pageSize"] { + pageSize = int(arg["pageSize"].(float64)) + } + if 0 >= pageSize { + pageSize = 32 + } + queryArg := arg["query"] if nil != queryArg { query = queryArg.(string) diff --git a/kernel/model/search.go b/kernel/model/search.go index b49e0500b..c3ab10748 100644 --- a/kernel/model/search.go +++ b/kernel/model/search.go @@ -20,6 +20,7 @@ import ( "bytes" "errors" "fmt" + "math" "os" "path" "path/filepath" @@ -234,7 +235,7 @@ func FindReplace(keyword, replacement string, ids []string, paths, boxes []strin if 1 > len(ids) { // `Replace All` is no longer affected by pagination https://github.com/siyuan-note/siyuan/issues/8265 - blocks, _, _, _ := FullTextSearchBlock(keyword, boxes, paths, types, method, orderBy, groupBy, 1) + blocks, _, _, _ := FullTextSearchBlock(keyword, boxes, paths, types, method, orderBy, groupBy, 1, math.MaxInt) for _, block := range blocks { ids = append(ids, block.ID) } @@ -277,7 +278,7 @@ func FindReplace(keyword, replacement string, ids []string, paths, boxes []strin } indexHistoryDir(filepath.Base(historyDir), util.NewLute()) - for _, id := range ids { + for i, id := range ids { bt := treenode.GetBlockTree(id) if nil == bt { continue @@ -387,6 +388,8 @@ func FindReplace(keyword, replacement string, ids []string, paths, boxes []strin if err = writeJSONQueue(tree); nil != err { return } + + util.PushEndlessProgress(fmt.Sprintf(Conf.Language(70), fmt.Sprintf("%d/%d", i, len(ids)))) } for _, renameRoot := range renameRoots { @@ -404,14 +407,12 @@ func FindReplace(keyword, replacement string, ids []string, paths, boxes []strin return } -const pageSize = 32 - // FullTextSearchBlock 搜索内容块。 // // method:0:关键字,1:查询语法,2:SQL,3:正则表达式 // orderBy: 0:按块类型(默认),1:按创建时间升序,2:按创建时间降序,3:按更新时间升序,4:按更新时间降序,5:按内容顺序(仅在按文档分组时),6:按相关度升序,7:按相关度降序 // groupBy:0:不分组,1:按文档分组 -func FullTextSearchBlock(query string, boxes, paths []string, types map[string]bool, method, orderBy, groupBy, page int) (ret []*Block, matchedBlockCount, matchedRootCount, pageCount int) { +func FullTextSearchBlock(query string, boxes, paths []string, types map[string]bool, method, orderBy, groupBy, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount, pageCount int) { query = strings.TrimSpace(query) beforeLen := 36 var blocks []*Block @@ -421,19 +422,19 @@ func FullTextSearchBlock(query string, boxes, paths []string, types map[string]b filter := buildTypeFilter(types) boxFilter := buildBoxesFilter(boxes) pathFilter := buildPathsFilter(paths) - blocks, matchedBlockCount, matchedRootCount = fullTextSearchByQuerySyntax(query, boxFilter, pathFilter, filter, orderByClause, beforeLen, page) + blocks, matchedBlockCount, matchedRootCount = fullTextSearchByQuerySyntax(query, boxFilter, pathFilter, filter, orderByClause, beforeLen, page, pageSize) case 2: // SQL - blocks, matchedBlockCount, matchedRootCount = searchBySQL(query, beforeLen, page) + blocks, matchedBlockCount, matchedRootCount = searchBySQL(query, beforeLen, page, pageSize) case 3: // 正则表达式 typeFilter := buildTypeFilter(types) boxFilter := buildBoxesFilter(boxes) pathFilter := buildPathsFilter(paths) - blocks, matchedBlockCount, matchedRootCount = fullTextSearchByRegexp(query, boxFilter, pathFilter, typeFilter, orderByClause, beforeLen, page) + blocks, matchedBlockCount, matchedRootCount = fullTextSearchByRegexp(query, boxFilter, pathFilter, typeFilter, orderByClause, beforeLen, page, pageSize) default: // 关键字 filter := buildTypeFilter(types) boxFilter := buildBoxesFilter(boxes) pathFilter := buildPathsFilter(paths) - blocks, matchedBlockCount, matchedRootCount = fullTextSearchByKeyword(query, boxFilter, pathFilter, filter, orderByClause, beforeLen, page) + blocks, matchedBlockCount, matchedRootCount = fullTextSearchByKeyword(query, boxFilter, pathFilter, filter, orderByClause, beforeLen, page, pageSize) } pageCount = (matchedBlockCount + pageSize - 1) / pageSize @@ -615,7 +616,7 @@ func buildTypeFilter(types map[string]bool) string { return s.TypeFilter() } -func searchBySQL(stmt string, beforeLen, page int) (ret []*Block, matchedBlockCount, matchedRootCount int) { +func searchBySQL(stmt string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) { stmt = gulu.Str.RemoveInvisible(stmt) stmt = strings.TrimSpace(stmt) blocks := sql.SelectBlocksRawStmt(stmt, page, pageSize) @@ -663,7 +664,7 @@ func fullTextSearchRefBlock(keyword string, beforeLen int, onlyDoc bool) (ret [] keyword = gulu.Str.RemoveInvisible(keyword) if ast.IsNodeIDPattern(keyword) { - ret, _, _ = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+keyword+"'", 36, 1) + ret, _, _ = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+keyword+"'", 36, 1, 32) return } @@ -712,26 +713,26 @@ func fullTextSearchRefBlock(keyword string, beforeLen int, onlyDoc bool) (ret [] return } -func fullTextSearchByQuerySyntax(query, boxFilter, pathFilter, typeFilter, orderBy string, beforeLen, page int) (ret []*Block, matchedBlockCount, matchedRootCount int) { +func fullTextSearchByQuerySyntax(query, boxFilter, pathFilter, typeFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) { query = gulu.Str.RemoveInvisible(query) if ast.IsNodeIDPattern(query) { - ret, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page) + ret, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page, pageSize) return } - return fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy, beforeLen, page) + return fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy, beforeLen, page, pageSize) } -func fullTextSearchByKeyword(query, boxFilter, pathFilter, typeFilter string, orderBy string, beforeLen, page int) (ret []*Block, matchedBlockCount, matchedRootCount int) { +func fullTextSearchByKeyword(query, boxFilter, pathFilter, typeFilter string, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) { query = gulu.Str.RemoveInvisible(query) if ast.IsNodeIDPattern(query) { - ret, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page) + ret, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page, pageSize) return } query = stringQuery(query) - return fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy, beforeLen, page) + return fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy, beforeLen, page, pageSize) } -func fullTextSearchByRegexp(exp, boxFilter, pathFilter, typeFilter, orderBy string, beforeLen, page int) (ret []*Block, matchedBlockCount, matchedRootCount int) { +func fullTextSearchByRegexp(exp, boxFilter, pathFilter, typeFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) { exp = gulu.Str.RemoveInvisible(exp) fieldFilter := fieldRegexp(exp) @@ -762,7 +763,7 @@ func fullTextSearchCountByRegexp(exp, boxFilter, pathFilter, typeFilter string) return } -func fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy string, beforeLen, page int) (ret []*Block, matchedBlockCount, matchedRootCount int) { +func fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) { table := "blocks_fts" // 大小写敏感 if !Conf.Search.CaseSensitive { table = "blocks_fts_case_insensitive" From d6e750e2a4189e6ea323aa04d7ffded8f32080f4 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 29 Jun 2023 18:44:45 +0800 Subject: [PATCH 4/4] :art: `Replace All` is no longer affected by pagination https://github.com/siyuan-note/siyuan/issues/8265 --- app/appearance/langs/en_US.json | 4 +++- app/appearance/langs/es_ES.json | 4 +++- app/appearance/langs/fr_FR.json | 4 +++- app/appearance/langs/zh_CHT.json | 4 +++- app/appearance/langs/zh_CN.json | 4 +++- kernel/model/search.go | 6 ++++-- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index 21631071d..b0bd0d6c4 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1171,6 +1171,8 @@ "202": "Cleaning data repo...", "203": "The data repo is purged, [%d] snapshots and [%d] data objects have been deleted, and a total of [%s] disk space has been released", "204": "The doc in the user guide does not support sharing to the community", - "205": "The plugin is not supported in the current environment" + "205": "The plugin is not supported in the current environment", + "206": "Executing content replacement [%d/%d]", + "207": "Executing path replacement [%d/%d]" } } diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index 91ee1e5d7..cb29c5afe 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1171,6 +1171,8 @@ "202": "Limpiando repositorio de datos...", "203": "El repositorio de datos se purgó, [%d] instantáneas y [%d] objetos de datos se eliminaron y se liberó un total de [%s] espacio en disco", "204": "La documentación en la guía del usuario no permite compartir con la comunidad", - "205": "El complemento no es compatible con el entorno actual" + "205": "El complemento no es compatible con el entorno actual", + "206": "Ejecutando reemplazo de contenido [%d/%d]", + "207": "Ejecutando reemplazo de ruta [%d/%d]" } } diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index b9d338a9b..699c9c31e 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1171,6 +1171,8 @@ "202": "Nettoyage du référentiel de données...", "203": "Le référentiel de données est purgé, [%d] instantanés et [%d] objets de données ont été supprimés, et un total de [%s] espace disque a été libéré", "204": "La documentation du guide de l'utilisateur ne prend pas en charge le partage avec la communauté", - "205": "Le plugin n'est pas pris en charge dans l'environnement actuel" + "205": "Le plugin n'est pas pris en charge dans l'environnement actuel", + "206": "Exécution du remplacement de contenu [%d/%d]", + "207": "Exécution du remplacement de chemin [%d/%d]" } } diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index b33d360ec..1fd2f8ec5 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1171,6 +1171,8 @@ "202": "正在清理數據倉庫...", "203": "數據倉庫清理完畢,已刪除 [%d] 個快照和 [%d] 個數據對象,共釋放 [%s] 磁盤空間", "204": "用戶指南中的文檔不支持分享到社區", - "205": "該插件不支持在當前環境下使用" + "205": "該插件不支持在當前環境下使用", + "206": "正在執行內容替換 [%d/%d]", + "207": "正在執行路徑替換 [%d/%d]" } } diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index abe2e0cfd..a2561dab9 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1173,6 +1173,8 @@ "202": "正在清理数据仓库...", "203": "数据仓库清理完毕,已删除 [%d] 个快照和 [%d] 个数据对象,共释放 [%s] 磁盘空间", "204": "用户指南中的文档不支持分享到社区", - "205": "该插件不支持在当前环境下使用" + "205": "该插件不支持在当前环境下使用", + "206": "正在执行内容替换 [%d/%d]", + "207": "正在执行路径替换 [%d/%d]" } } diff --git a/kernel/model/search.go b/kernel/model/search.go index c3ab10748..61879b916 100644 --- a/kernel/model/search.go +++ b/kernel/model/search.go @@ -389,12 +389,14 @@ func FindReplace(keyword, replacement string, ids []string, paths, boxes []strin return } - util.PushEndlessProgress(fmt.Sprintf(Conf.Language(70), fmt.Sprintf("%d/%d", i, len(ids)))) + util.PushEndlessProgress(fmt.Sprintf(Conf.Language(206), i+1, len(ids))) } - for _, renameRoot := range renameRoots { + for i, renameRoot := range renameRoots { newTitle := renameRootTitles[renameRoot.ID] RenameDoc(renameRoot.Box, renameRoot.Path, newTitle) + + util.PushEndlessProgress(fmt.Sprintf(Conf.Language(207), i+1, len(ids))) } WaitForWritingFiles()