mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-07-03 14:09:06 +02:00
36f4e3720e
* ✨ 搜索支持区分繁简选项 Add Simplified/Traditional Chinese sensitivity option for search 新增搜索配置 hanSensitive(区分繁简,默认开启 = 现状行为,命名与语义 向 caseSensitive 对齐)。关闭后简体与繁体可互相检索。 - FTS 层:关闭区分繁简时,blocks_fts / blocks_fts_case_insensitive 建表 追加 han_insensitive 分词器参数(依赖 88250/go-sqlite3 配套补丁), 繁体在分词时按单字折叠为简体,token 偏移仍指向原文; - 切换选项复用区分大小写现有的 FullReindex 流程; - 高亮:EncloseHighlighting 在关闭区分繁简时将关键字逐字符展开为 繁简等价字符类(如 诗经 -> [诗詩][经經]),与分词器使用同一份 OpenCC TSCharacters 映射数据; - 兼容:旧版本 conf.json 缺 hanSensitive 字段时按既往行为置为开启; setSearch 请求未携带该字段(现行前端/第三方调用)时保持当前值, 避免被零值意外翻转并触发重建索引; - 遵照配置界面重写计划(#17601),本次不改动设置界面与 i18n,仅添加 配置数据结构字段与前端类型定义(config.d.ts)。 范围说明:本次仅覆盖块全文搜索(关键字/查询语法)的命中与高亮; 历史/资源文件搜索、数据库属性过滤、虚拟引用、查找替换的替换动作 不在本次范围内(替换保持字节精确以避免误改原文)。 Fixes #13304 * 🐛 add missing util.SearchHanSensitive referenced by highlighting and sql.SetHanSensitive Copilot 评审指出 util.SearchHanSensitive 未定义导致无法编译:补上该全局 变量(默认 true,与既往行为一致,由 sql.SetHanSensitive 维护),并修正 conf/search.go 的 gofmt 对齐。已在本地以 dev 当前 go.mod(已合并的 88250/go-sqlite3 分词器)完成 go build -tags fts5 与 kernel/search 单测验证。
262 lines
6.9 KiB
Go
262 lines
6.9 KiB
Go
// SiYuan - Refactor your thinking
|
|
// Copyright (c) 2020-present, b3log.org
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package conf
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
|
|
"github.com/88250/lute/ast"
|
|
"github.com/siyuan-note/siyuan/kernel/treenode"
|
|
)
|
|
|
|
type Search struct {
|
|
Document bool `json:"document"`
|
|
Heading bool `json:"heading"`
|
|
List bool `json:"list"`
|
|
ListItem bool `json:"listItem"`
|
|
CodeBlock bool `json:"codeBlock"`
|
|
MathBlock bool `json:"mathBlock"`
|
|
Table bool `json:"table"`
|
|
Blockquote bool `json:"blockquote"`
|
|
SuperBlock bool `json:"superBlock"`
|
|
Paragraph bool `json:"paragraph"`
|
|
HTMLBlock bool `json:"htmlBlock"`
|
|
EmbedBlock bool `json:"embedBlock"`
|
|
DatabaseBlock bool `json:"databaseBlock"`
|
|
AudioBlock bool `json:"audioBlock"`
|
|
VideoBlock bool `json:"videoBlock"`
|
|
IFrameBlock bool `json:"iframeBlock"`
|
|
WidgetBlock bool `json:"widgetBlock"`
|
|
Callout bool `json:"callout"`
|
|
|
|
Limit int `json:"limit"`
|
|
CaseSensitive bool `json:"caseSensitive"`
|
|
HanSensitive *bool `json:"hanSensitive"` // 区分繁简:默认开启(与既往行为一致);关闭后全文搜索不区分简体/繁体中文字形
|
|
|
|
Name bool `json:"name"`
|
|
Alias bool `json:"alias"`
|
|
Memo bool `json:"memo"`
|
|
IAL bool `json:"ial"`
|
|
|
|
IndexAssetPath bool `json:"indexAssetPath"`
|
|
|
|
BacklinkMentionName bool `json:"backlinkMentionName"`
|
|
BacklinkMentionAlias bool `json:"backlinkMentionAlias"`
|
|
BacklinkMentionAnchor bool `json:"backlinkMentionAnchor"`
|
|
BacklinkMentionDoc bool `json:"backlinkMentionDoc"`
|
|
BacklinkMentionKeywordsLimit int `json:"backlinkMentionKeywordsLimit"`
|
|
|
|
VirtualRefName bool `json:"virtualRefName"`
|
|
VirtualRefAlias bool `json:"virtualRefAlias"`
|
|
VirtualRefAnchor bool `json:"virtualRefAnchor"`
|
|
VirtualRefDoc bool `json:"virtualRefDoc"`
|
|
}
|
|
|
|
func NewSearch() *Search {
|
|
return &Search{
|
|
Document: true,
|
|
Heading: true,
|
|
List: false,
|
|
ListItem: false,
|
|
CodeBlock: true,
|
|
MathBlock: true,
|
|
Table: true,
|
|
Blockquote: false,
|
|
SuperBlock: false,
|
|
Paragraph: true,
|
|
HTMLBlock: true,
|
|
EmbedBlock: false,
|
|
DatabaseBlock: true,
|
|
AudioBlock: false,
|
|
VideoBlock: false,
|
|
IFrameBlock: false,
|
|
WidgetBlock: false,
|
|
Callout: false,
|
|
|
|
Limit: 64,
|
|
CaseSensitive: false,
|
|
HanSensitive: boolPtr(true),
|
|
|
|
Name: true,
|
|
Alias: true,
|
|
Memo: true,
|
|
IAL: false,
|
|
|
|
IndexAssetPath: true,
|
|
|
|
BacklinkMentionName: true,
|
|
BacklinkMentionAlias: false,
|
|
BacklinkMentionAnchor: true,
|
|
BacklinkMentionDoc: true,
|
|
BacklinkMentionKeywordsLimit: 512,
|
|
|
|
VirtualRefName: true,
|
|
VirtualRefAlias: false,
|
|
VirtualRefAnchor: true,
|
|
VirtualRefDoc: true,
|
|
}
|
|
}
|
|
|
|
func boolPtr(v bool) *bool { return &v }
|
|
|
|
// HanSensitiveVal 返回 HanSensitive 的 bool 值;nil 视为 true(与既往行为一致)。
|
|
func (s *Search) HanSensitiveVal() bool {
|
|
if s.HanSensitive == nil {
|
|
return true
|
|
}
|
|
return *s.HanSensitive
|
|
}
|
|
|
|
// SetHanSensitive 设置 HanSensitive 字段。
|
|
func (s *Search) SetHanSensitive(v bool) {
|
|
s.HanSensitive = boolPtr(v)
|
|
}
|
|
|
|
func (s *Search) NAMFilter(keyword string) string {
|
|
keyword = strings.TrimSpace(keyword)
|
|
buf := bytes.Buffer{}
|
|
if s.Name {
|
|
buf.WriteString(" OR name LIKE '%" + keyword + "%'")
|
|
}
|
|
if s.Alias {
|
|
buf.WriteString(" OR alias LIKE '%" + keyword + "%'")
|
|
}
|
|
if s.Memo {
|
|
buf.WriteString(" OR memo LIKE '%" + keyword + "%'")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (s *Search) TypeFilter() string {
|
|
buf := bytes.Buffer{}
|
|
if s.Document {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeDocument.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.Heading {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeHeading.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.List {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeList.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.ListItem {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeListItem.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.CodeBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeCodeBlock.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.MathBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeMathBlock.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.Table {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeTable.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.Blockquote {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeBlockquote.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.SuperBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeSuperBlock.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.Paragraph {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeParagraph.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.HTMLBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeHTMLBlock.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.EmbedBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeBlockQueryEmbed.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.DatabaseBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeAttributeView.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.AudioBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeAudio.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.VideoBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeVideo.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.IFrameBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeIFrame.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.WidgetBlock {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeWidget.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
if s.Callout {
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(treenode.TypeAbbr(ast.NodeCallout.String()))
|
|
buf.WriteByte('\'')
|
|
buf.WriteString(",")
|
|
}
|
|
|
|
ret := buf.String()
|
|
if "" == ret {
|
|
return ret
|
|
}
|
|
return "(" + ret[:len(ret)-1] + ")"
|
|
}
|