Преглед изворни кода

:art: Improve search result highlight and positioning https://github.com/siyuan-note/siyuan/issues/8274

Liang Ding пре 2 година
родитељ
комит
0f90105527
3 измењених фајлова са 55 додато и 8 уклоњено
  1. 18 5
      kernel/api/filetree.go
  2. 7 3
      kernel/model/file.go
  3. 30 0
      kernel/model/search.go

+ 18 - 5
kernel/api/filetree.go

@@ -675,11 +675,24 @@ func getDoc(c *gin.Context) {
 	if nil != idx {
 	if nil != idx {
 		index = int(idx.(float64))
 		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:加载末尾
 	m := arg["mode"] // 0: 仅当前 ID,1:向上 2:向下,3:上下都加载,4:加载末尾
 	mode := 0
 	mode := 0
 	if nil != m {
 	if nil != m {
@@ -705,7 +718,7 @@ func getDoc(c *gin.Context) {
 		isBacklink = isBacklinkArg.(bool)
 		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 {
 	if model.ErrBlockNotFound == err {
 		ret.Code = 3
 		ret.Code = 3
 		return
 		return

+ 7 - 3
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)
 	//os.MkdirAll("pprof", 0755)
 	//cpuProfile, _ := os.Create("pprof/GetDoc")
 	//cpuProfile, _ := os.Create("pprof/GetDoc")
 	//pprof.StartCPUProfile(cpuProfile)
 	//pprof.StartCPUProfile(cpuProfile)
@@ -671,8 +671,12 @@ func GetDoc(startID, endID, id string, index int, keyword string, mode int, size
 	virtualBlockRefKeywords := getBlockVirtualRefKeywords(tree.Root)
 	virtualBlockRefKeywords := getBlockVirtualRefKeywords(tree.Root)
 
 
 	subTree := &parse.Tree{ID: rootID, Root: &ast.Node{Type: ast.NodeDocument}, Marks: tree.Marks}
 	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 {
 	for _, n := range nodes {
 		var unlinks []*ast.Node
 		var unlinks []*ast.Node

+ 30 - 0
kernel/model/search.go

@@ -782,6 +782,35 @@ func fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy strin
 	return
 	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) {
 func fullTextSearchCount(query, boxFilter, pathFilter, typeFilter string) (matchedBlockCount, matchedRootCount int) {
 	query = gulu.Str.RemoveInvisible(query)
 	query = gulu.Str.RemoveInvisible(query)
 	if ast.IsNodeIDPattern(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 {
 	} else if ast.NodeTextMark == n.Type {
 		// 搜索结果高亮支持大部分行级元素 https://github.com/siyuan-note/siyuan/issues/6745
 		// 搜索结果高亮支持大部分行级元素 https://github.com/siyuan-note/siyuan/issues/6745
+
 		if n.IsTextMarkType("inline-math") || n.IsTextMarkType("inline-memo") {
 		if n.IsTextMarkType("inline-math") || n.IsTextMarkType("inline-memo") {
 			return false
 			return false
 		}
 		}