mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-20 16:40:13 +01:00
⚡ Return document blocks when search hits different block content https://github.com/siyuan-note/siyuan/issues/10584
This commit is contained in:
parent
a86829515b
commit
14057d6714
1 changed files with 53 additions and 55 deletions
|
|
@ -1383,82 +1383,80 @@ func fullTextSearchCountByFTS(query, boxFilter, pathFilter, typeFilter, ignoreFi
|
||||||
func fullTextSearchByFTSWithRoot(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) {
|
func fullTextSearchByFTSWithRoot(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
table := "blocks_fts" // 大小写敏感
|
|
||||||
if !Conf.Search.CaseSensitive {
|
|
||||||
table = "blocks_fts_case_insensitive"
|
|
||||||
}
|
|
||||||
|
|
||||||
query = strings.ReplaceAll(query, "'", "''")
|
query = strings.ReplaceAll(query, "'", "''")
|
||||||
query = strings.ReplaceAll(query, "\"", "\"\"")
|
query = strings.ReplaceAll(query, "\"", "\"\"")
|
||||||
keywords := strings.Split(query, " ")
|
keywords := strings.Split(query, " ")
|
||||||
|
contentField := "content||tag||name||alias||memo"
|
||||||
var likeFilter string
|
var likeFilter string
|
||||||
|
orderByLike := "("
|
||||||
for i, keyword := range keywords {
|
for i, keyword := range keywords {
|
||||||
likeFilter += "docContent LIKE '%" + keyword + "%'"
|
likeFilter += "GROUP_CONCAT(" + contentField + ") LIKE '%" + keyword + "%'"
|
||||||
|
orderByLike += "(docContent LIKE '%" + keyword + "%')"
|
||||||
if i < len(keywords)-1 {
|
if i < len(keywords)-1 {
|
||||||
likeFilter += " AND "
|
likeFilter += " AND "
|
||||||
|
orderByLike += " + "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bMatchStmt := "SELECT id FROM " + table + " WHERE " + strings.ReplaceAll(likeFilter, "docContent LIKE ", "content LIKE ")
|
orderByLike += ")"
|
||||||
bMatchStmt += " AND type IN " + typeFilter + boxFilter + pathFilter + ignoreFilter
|
dMatchStmt := "SELECT root_id, MAX(CASE WHEN type = 'd' THEN (" + contentField + ") END) AS docContent" +
|
||||||
dMatchStmt := "SELECT root_id, GROUP_CONCAT(content || tag || name || alias || memo) AS docContent" +
|
" FROM blocks WHERE type IN " + typeFilter + boxFilter + pathFilter + ignoreFilter +
|
||||||
" FROM " + table + " WHERE type IN " + typeFilter + boxFilter + pathFilter + ignoreFilter +
|
" GROUP BY root_id HAVING " + likeFilter + "ORDER BY " + orderByLike + " DESC, MAX(updated) DESC"
|
||||||
" GROUP BY root_id HAVING " + likeFilter
|
cteStmt := "WITH docBlocks AS (" + dMatchStmt + ")"
|
||||||
cteStmt := "WITH docBlocks AS (" + dMatchStmt + "), nonDocBlocks AS (" + bMatchStmt + ")"
|
pagingStmt := " LIMIT " + strconv.Itoa(pageSize) + " OFFSET " + strconv.Itoa((page-1)*pageSize)
|
||||||
wheheClause := " WHERE id IN (SELECT id FROM nonDocBlocks) OR id IN (SELECT root_id FROM docBlocks)"
|
likeFilter = strings.ReplaceAll(likeFilter, "GROUP_CONCAT("+contentField+")", "concatContent")
|
||||||
selectStmt := cteStmt + "\nSELECT * FROM " + table + wheheClause
|
selectStmt := cteStmt + "\nSELECT *, " +
|
||||||
countStmt := cteStmt + "\nSELECT COUNT(id) AS `matches`, COUNT(DISTINCT(root_id)) AS `docs` FROM " + table + wheheClause
|
"(content || tag || name || alias || memo) AS concatContent, " +
|
||||||
selectStmt += orderBy + " LIMIT " + strconv.Itoa(pageSize) + " OFFSET " + strconv.Itoa((page-1)*pageSize)
|
"(SELECT COUNT(root_id) FROM docBlocks) AS `docs` FROM blocks" +
|
||||||
resultBlocks := sql.SelectBlocksRawStmtNoParse(selectStmt, -1)
|
" WHERE type IN " + typeFilter + boxFilter + pathFilter + ignoreFilter +
|
||||||
|
" AND (id IN (SELECT root_id FROM docBlocks" + pagingStmt + ") OR" +
|
||||||
|
" (root_id IN (SELECT root_id FROM docBlocks" + pagingStmt + ") AND (" + likeFilter + ")))"
|
||||||
|
selectStmt += " " + orderBy + " LIMIT 10240000"
|
||||||
|
result, _ := sql.Query(selectStmt, -1)
|
||||||
|
var resultBlocks []*sql.Block
|
||||||
|
for _, row := range result {
|
||||||
|
b := &sql.Block{
|
||||||
|
ID: row["id"].(string),
|
||||||
|
ParentID: row["parent_id"].(string),
|
||||||
|
RootID: row["root_id"].(string),
|
||||||
|
Hash: row["hash"].(string),
|
||||||
|
Box: row["box"].(string),
|
||||||
|
Path: row["path"].(string),
|
||||||
|
HPath: row["hpath"].(string),
|
||||||
|
Name: row["name"].(string),
|
||||||
|
Alias: row["alias"].(string),
|
||||||
|
Memo: row["memo"].(string),
|
||||||
|
Tag: row["tag"].(string),
|
||||||
|
Content: row["content"].(string),
|
||||||
|
FContent: row["fcontent"].(string),
|
||||||
|
Markdown: row["markdown"].(string),
|
||||||
|
Length: int(row["length"].(int64)),
|
||||||
|
Type: row["type"].(string),
|
||||||
|
SubType: row["subtype"].(string),
|
||||||
|
IAL: row["ial"].(string),
|
||||||
|
Sort: int(row["sort"].(int64)),
|
||||||
|
Created: row["created"].(string),
|
||||||
|
Updated: row["updated"].(string),
|
||||||
|
}
|
||||||
|
resultBlocks = append(resultBlocks, b)
|
||||||
|
}
|
||||||
|
if 0 < len(resultBlocks) {
|
||||||
|
matchedRootCount = int(result[0]["docs"].(int64))
|
||||||
|
}
|
||||||
logging.LogInfof("time cost [main search]: %v", time.Since(start))
|
logging.LogInfof("time cost [main search]: %v", time.Since(start))
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// FTS 高亮
|
keywords = gulu.Str.RemoveDuplicatedElem(keywords)
|
||||||
projections := "id, parent_id, root_id, hash, box, path, " +
|
terms := strings.Join(keywords, search.TermSep)
|
||||||
// Search result content snippet returns more text https://github.com/siyuan-note/siyuan/issues/10707
|
ret = fromSQLBlocks(&resultBlocks, terms, beforeLen)
|
||||||
"snippet(" + table + ", 6, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "', '...', 512) AS hpath, " +
|
|
||||||
"snippet(" + table + ", 7, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "', '...', 512) AS name, " +
|
|
||||||
"snippet(" + table + ", 8, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "', '...', 512) AS alias, " +
|
|
||||||
"snippet(" + table + ", 9, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "', '...', 512) AS memo, " +
|
|
||||||
"snippet(" + table + ", 10, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "', '...', 64) AS tag, " +
|
|
||||||
"snippet(" + table + ", 11, '" + search.SearchMarkLeft + "', '" + search.SearchMarkRight + "', '...', 512) AS content, " +
|
|
||||||
"fcontent, markdown, length, type, subtype, ial, sort, created, updated"
|
|
||||||
stmt := "SELECT " + projections + " FROM " + table + " WHERE (`" + table + "` MATCH '" + columnFilter() + ":(" + stringQuery(query) + ")'"
|
|
||||||
stmt += ") AND type IN " + typeFilter + boxFilter + pathFilter + ignoreFilter + orderBy + " LIMIT " + strconv.Itoa(pageSize) + " OFFSET " + strconv.Itoa((page-1)*pageSize)
|
|
||||||
blocks := sql.SelectBlocksRawStmt(stmt, page, pageSize)
|
|
||||||
for i, resultBlock := range resultBlocks {
|
|
||||||
for j, block := range blocks {
|
|
||||||
if resultBlock.ID == block.ID {
|
|
||||||
resultBlocks[i] = block
|
|
||||||
blocks = append(blocks[:j], blocks[j+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = fromSQLBlocks(&resultBlocks, "", beforeLen)
|
|
||||||
if 1 > len(ret) {
|
if 1 > len(ret) {
|
||||||
ret = []*Block{}
|
ret = []*Block{}
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.LogInfof("time cost [highlight search]: %v", time.Since(now))
|
logging.LogInfof("time cost [highlight search]: %v", time.Since(now))
|
||||||
now = time.Now()
|
|
||||||
matchedBlockCount, matchedRootCount = fullTextSearchCountByStmt(countStmt)
|
|
||||||
logging.LogInfof("time cost [count search]: %v", time.Since(now))
|
|
||||||
logging.LogInfof("time cost [all]: %v", time.Since(start))
|
logging.LogInfof("time cost [all]: %v", time.Since(start))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fullTextSearchCountByStmt(stmt string) (matchedBlockCount, matchedRootCount int) {
|
|
||||||
result, _ := sql.QueryNoLimit(stmt)
|
|
||||||
if 1 > len(result) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
matchedBlockCount = int(result[0]["matches"].(int64))
|
|
||||||
matchedRootCount = int(result[0]["docs"].(int64))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func highlightByFTS(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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue