mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-20 16:40:13 +01:00
🎨 Highlight regular expression search results https://github.com/siyuan-note/siyuan/issues/11112
This commit is contained in:
parent
783c8a092b
commit
eb84c1f90f
3 changed files with 77 additions and 5 deletions
|
|
@ -804,12 +804,16 @@ func GetDoc(startID, endID, id string, index int, query string, queryTypes map[s
|
||||||
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}
|
||||||
|
|
||||||
var keywords []string
|
var keywords []string
|
||||||
if "" != query && (0 == queryMethod || 1 == queryMethod) { // 只有关键字搜索和查询语法搜索才支持高亮
|
if "" != query && (0 == queryMethod || 1 == queryMethod || 3 == queryMethod) { // 只有关键字、查询语法和正则表达式搜索支持高亮
|
||||||
if 0 == queryMethod {
|
if 0 == queryMethod {
|
||||||
query = stringQuery(query)
|
query = stringQuery(query)
|
||||||
}
|
}
|
||||||
typeFilter := buildTypeFilter(queryTypes)
|
typeFilter := buildTypeFilter(queryTypes)
|
||||||
keywords = highlightByQuery(query, typeFilter, rootID)
|
if 0 == queryMethod || 1 == queryMethod {
|
||||||
|
keywords = highlightByFTS(query, typeFilter, rootID)
|
||||||
|
} else {
|
||||||
|
keywords = highlightByRegexp(query, typeFilter, rootID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
|
|
|
||||||
|
|
@ -1290,8 +1290,8 @@ func fullTextSearchByRegexp(exp, boxFilter, pathFilter, typeFilter, orderBy stri
|
||||||
stmt := "SELECT * FROM `blocks` WHERE " + fieldFilter + " AND type IN " + typeFilter
|
stmt := "SELECT * FROM `blocks` WHERE " + fieldFilter + " AND type IN " + typeFilter
|
||||||
stmt += boxFilter + pathFilter
|
stmt += boxFilter + pathFilter
|
||||||
stmt += " " + orderBy
|
stmt += " " + orderBy
|
||||||
stmt += " LIMIT " + strconv.Itoa(pageSize) + " OFFSET " + strconv.Itoa((page-1)*pageSize)
|
regex := regexp.MustCompile(exp)
|
||||||
blocks := sql.SelectBlocksRawStmtNoParse(stmt, Conf.Search.Limit)
|
blocks := sql.SelectBlocksRegex(stmt, regex, Conf.Search.Name, Conf.Search.Alias, Conf.Search.Memo, Conf.Search.IAL, page, pageSize)
|
||||||
ret = fromSQLBlocks(&blocks, "", beforeLen)
|
ret = fromSQLBlocks(&blocks, "", beforeLen)
|
||||||
if 1 > len(ret) {
|
if 1 > len(ret) {
|
||||||
ret = []*Block{}
|
ret = []*Block{}
|
||||||
|
|
@ -1354,7 +1354,7 @@ func fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, orderBy strin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func highlightByQuery(query, typeFilter, id string) (ret []string) {
|
func highlightByFTS(query, typeFilter, id string) (ret []string) {
|
||||||
const limit = 256
|
const limit = 256
|
||||||
table := "blocks_fts"
|
table := "blocks_fts"
|
||||||
if !Conf.Search.CaseSensitive {
|
if !Conf.Search.CaseSensitive {
|
||||||
|
|
@ -1383,6 +1383,22 @@ func highlightByQuery(query, typeFilter, id string) (ret []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func highlightByRegexp(query, typeFilter, id string) (ret []string) {
|
||||||
|
fieldFilter := fieldRegexp(query)
|
||||||
|
stmt := "SELECT * FROM `blocks` WHERE " + fieldFilter + " AND type IN " + typeFilter
|
||||||
|
stmt += " AND root_id = '" + id + "'"
|
||||||
|
regex := regexp.MustCompile(query)
|
||||||
|
sqlBlocks := sql.SelectBlocksRegex(stmt, regex, Conf.Search.Name, Conf.Search.Alias, Conf.Search.Memo, Conf.Search.IAL, 1, 256)
|
||||||
|
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 = filterQueryInvisibleChars(query)
|
query = filterQueryInvisibleChars(query)
|
||||||
if ast.IsNodeIDPattern(query) {
|
if ast.IsNodeIDPattern(query) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"math"
|
"math"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -619,6 +620,57 @@ func SelectBlocksRawStmt(stmt string, page, limit int) (ret []*Block) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SelectBlocksRegex(stmt string, exp *regexp.Regexp, name, alias, memo, ial bool, page, pageSize int) (ret []*Block) {
|
||||||
|
rows, err := query(stmt)
|
||||||
|
if err != nil {
|
||||||
|
logging.LogErrorf("sql query [%s] failed: %s", stmt, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
count := 0
|
||||||
|
for rows.Next() {
|
||||||
|
count++
|
||||||
|
if count <= (page-1)*pageSize {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var block Block
|
||||||
|
if err := rows.Scan(&block.ID, &block.ParentID, &block.RootID, &block.Hash, &block.Box, &block.Path, &block.HPath, &block.Name, &block.Alias, &block.Memo, &block.Tag, &block.Content, &block.FContent, &block.Markdown, &block.Length, &block.Type, &block.SubType, &block.IAL, &block.Sort, &block.Created, &block.Updated); err != nil {
|
||||||
|
logging.LogErrorf("query scan field failed: %s\n%s", err, logging.ShortStack())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hitContent := exp.MatchString(block.Content)
|
||||||
|
hitName := name && exp.MatchString(block.Name)
|
||||||
|
hitAlias := alias && exp.MatchString(block.Alias)
|
||||||
|
hitMemo := memo && exp.MatchString(block.Memo)
|
||||||
|
hitIAL := ial && exp.MatchString(block.IAL)
|
||||||
|
if hitContent || hitName || hitAlias || hitMemo || hitIAL {
|
||||||
|
if hitContent {
|
||||||
|
block.Content = exp.ReplaceAllString(block.Content, "__@mark__${0}__mark@__")
|
||||||
|
}
|
||||||
|
if hitName {
|
||||||
|
block.Name = exp.ReplaceAllString(block.Name, "__@mark__${0}__mark@__")
|
||||||
|
}
|
||||||
|
if hitAlias {
|
||||||
|
block.Alias = exp.ReplaceAllString(block.Alias, "__@mark__${0}__mark@__")
|
||||||
|
}
|
||||||
|
if hitMemo {
|
||||||
|
block.Memo = exp.ReplaceAllString(block.Memo, "__@mark__${0}__mark@__")
|
||||||
|
}
|
||||||
|
if hitIAL {
|
||||||
|
block.IAL = exp.ReplaceAllString(block.IAL, "__@mark__${0}__mark@__")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, &block)
|
||||||
|
if len(ret) >= pageSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func selectBlocksRawStmt(stmt string, limit int) (ret []*Block) {
|
func selectBlocksRawStmt(stmt string, limit int) (ret []*Block) {
|
||||||
rows, err := query(stmt)
|
rows, err := query(stmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue