From 0f90105527c6f8f8ead201c52cfe303d4eed3f75 Mon Sep 17 00:00:00 2001 From: Liang Ding Date: Fri, 19 May 2023 10:18:25 +0800 Subject: [PATCH] :art: Improve search result highlight and positioning https://github.com/siyuan-note/siyuan/issues/8274 --- kernel/api/filetree.go | 23 ++++++++++++++++++----- kernel/model/file.go | 10 +++++++--- kernel/model/search.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/kernel/api/filetree.go b/kernel/api/filetree.go index 40a683b92..4c43db32b 100644 --- a/kernel/api/filetree.go +++ b/kernel/api/filetree.go @@ -675,11 +675,24 @@ func getDoc(c *gin.Context) { if nil != idx { index = int(idx.(float64)) } - k := arg["k"] - var keyword string - if nil != k { - keyword = k.(string) + + var query string + if queryArg := arg["query"]; nil != queryArg { + query = queryArg.(string) } + var queryMethod int + if queryMethodArg := arg["queryMethod"]; nil != queryMethodArg { + queryMethod = int(queryMethodArg.(float64)) + } + var queryTypes map[string]bool + if queryTypesArg := arg["queryTypes"]; nil != queryTypesArg { + typesArg := queryTypesArg.(map[string]interface{}) + queryTypes = map[string]bool{} + for t, b := range typesArg { + queryTypes[t] = b.(bool) + } + } + m := arg["mode"] // 0: 仅当前 ID,1:向上 2:向下,3:上下都加载,4:加载末尾 mode := 0 if nil != m { @@ -705,7 +718,7 @@ func getDoc(c *gin.Context) { isBacklink = isBacklinkArg.(bool) } - blockCount, content, parentID, parent2ID, rootID, typ, eof, scroll, boxID, docPath, isBacklinkExpand, err := model.GetDoc(startID, endID, id, index, keyword, mode, size, isBacklink) + blockCount, content, parentID, parent2ID, rootID, typ, eof, scroll, boxID, docPath, isBacklinkExpand, err := model.GetDoc(startID, endID, id, index, query, queryTypes, queryMethod, mode, size, isBacklink) if model.ErrBlockNotFound == err { ret.Code = 3 return diff --git a/kernel/model/file.go b/kernel/model/file.go index 52fe3dea4..e397c3b4f 100644 --- a/kernel/model/file.go +++ b/kernel/model/file.go @@ -488,7 +488,7 @@ func StatTree(id string) (ret *util.BlockStatResult) { } } -func GetDoc(startID, endID, id string, index int, keyword string, mode int, size int, isBacklink bool) (blockCount int, dom, parentID, parent2ID, rootID, typ string, eof, scroll bool, boxID, docPath string, isBacklinkExpand bool, err error) { +func GetDoc(startID, endID, id string, index int, query string, queryTypes map[string]bool, queryMethod, mode int, size int, isBacklink bool) (blockCount int, dom, parentID, parent2ID, rootID, typ string, eof, scroll bool, boxID, docPath string, isBacklinkExpand bool, err error) { //os.MkdirAll("pprof", 0755) //cpuProfile, _ := os.Create("pprof/GetDoc") //pprof.StartCPUProfile(cpuProfile) @@ -671,8 +671,12 @@ func GetDoc(startID, endID, id string, index int, keyword string, mode int, size virtualBlockRefKeywords := getBlockVirtualRefKeywords(tree.Root) subTree := &parse.Tree{ID: rootID, Root: &ast.Node{Type: ast.NodeDocument}, Marks: tree.Marks} - keyword = strings.Join(strings.Split(keyword, " "), search.TermSep) - keywords := search.SplitKeyword(keyword) + + var keywords []string + if 0 == queryMethod || 1 == queryMethod { // 只有关键字搜索和查询语法搜索才支持高亮 + typeFilter := buildTypeFilter(queryTypes) + keywords = highlightByQuery(query, typeFilter, rootID) + } for _, n := range nodes { var unlinks []*ast.Node diff --git a/kernel/model/search.go b/kernel/model/search.go index 30ce519d4..a6c91654b 100644 --- a/kernel/model/search.go +++ b/kernel/model/search.go @@ -782,6 +782,35 @@ func fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy strin return } +func highlightByQuery(query, typeFilter, id string) (ret []string) { + const limit = 256 + table := "blocks_fts" + if !Conf.Search.CaseSensitive { + table = "blocks_fts_case_insensitive" + } + projections := "id, parent_id, root_id, hash, box, path, " + + "highlight(" + table + ", 6, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "') AS hpath, " + + "highlight(" + table + ", 7, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "') AS name, " + + "highlight(" + table + ", 8, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "') AS alias, " + + "highlight(" + table + ", 9, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "') AS memo, " + + "tag, " + + "highlight(" + table + ", 11, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "') AS content, " + + "fcontent, markdown, length, type, subtype, ial, sort, created, updated" + stmt := "SELECT " + projections + " FROM " + table + " WHERE (`" + table + "` MATCH '" + columnFilter() + ":(" + query + ")'" + stmt += ") AND type IN " + typeFilter + stmt += " AND root_id = '" + id + "'" + stmt += " LIMIT " + strconv.Itoa(limit) + sqlBlocks := sql.SelectBlocksRawStmt(stmt, 1, limit) + for _, block := range sqlBlocks { + keyword := gulu.Str.SubstringsBetween(block.Content, search.SearchMarkLeft, search.SearchMarkRight) + if 0 < len(keyword) { + ret = append(ret, keyword...) + } + } + ret = gulu.Str.RemoveDuplicatedElem(ret) + return +} + func fullTextSearchCount(query, boxFilter, pathFilter, typeFilter string) (matchedBlockCount, matchedRootCount int) { query = gulu.Str.RemoveInvisible(query) if ast.IsNodeIDPattern(query) { @@ -1017,6 +1046,7 @@ func markReplaceSpan(n *ast.Node, unlinks *[]*ast.Node, keywords []string, markS } } else if ast.NodeTextMark == n.Type { // 搜索结果高亮支持大部分行级元素 https://github.com/siyuan-note/siyuan/issues/6745 + if n.IsTextMarkType("inline-math") || n.IsTextMarkType("inline-memo") { return false }