From 6a0c0f8e1eda06b56eaa26ae26154cb93af0c3c8 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Wed, 13 Aug 2025 13:42:14 +0800 Subject: [PATCH 1/3] :art: Improve av https://github.com/siyuan-note/siyuan/issues/15559 --- kernel/av/av.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/kernel/av/av.go b/kernel/av/av.go index c4e939e73..6b7af5ea4 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -23,6 +23,8 @@ import ( "path/filepath" "sort" "strings" + "text/template" + "text/template/parse" "github.com/88250/gulu" "github.com/88250/lute/ast" @@ -747,14 +749,66 @@ func (av *AttributeView) GetTemplateKeyRelevantKeys(templateKey *Key) (ret []*Ke return } + vars, err := getTemplateVars(templateKey.Template) + if nil != err { + return + } + for _, kValues := range av.KeyValues { - if strings.Contains(templateKey.Template, "."+kValues.Key.Name) { + if gulu.Str.Contains(kValues.Key.Name, vars) { ret = append(ret, kValues.Key) } } return } +func getTemplateVars(tplContent string) ([]string, error) { + goTpl := template.New("").Delims(".action{", "}") + tpl, err := goTpl.Parse(tplContent) + if err != nil { + return nil, err + } + vars := make(map[string]struct{}) + collectVars(tpl.Tree.Root, vars) + var result []string + for v := range vars { + result = append(result, v) + } + return result, nil +} + +func collectVars(node parse.Node, vars map[string]struct{}) { + switch n := node.(type) { + case *parse.ListNode: + for _, child := range n.Nodes { + collectVars(child, vars) + } + case *parse.ActionNode: + collectVars(n.Pipe, vars) + case *parse.PipeNode: + for _, cmd := range n.Cmds { + collectVars(cmd, vars) + } + case *parse.CommandNode: + for _, arg := range n.Args { + collectVars(arg, vars) + } + + if 3 <= len(n.Args) && n.Args[0].Type() == parse.NodeIdentifier && n.Args[1].Type() == parse.NodeDot && n.Args[2].Type() == parse.NodeString { + vars[n.Args[2].(*parse.StringNode).Text] = struct{}{} + } + + case *parse.FieldNode: + if len(n.Ident) > 0 { + vars[n.Ident[0]] = struct{}{} + } + case *parse.VariableNode: + if len(n.Ident) > 0 { + vars[n.Ident[0]] = struct{}{} + } + } +} + func GetAttributeViewDataPath(avID string) (ret string) { av := filepath.Join(util.DataDir, "storage", "av") ret = filepath.Join(av, avID+".json") From e51e6d56628b1f8fea1321485fff19b9166675d6 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Wed, 13 Aug 2025 13:42:25 +0800 Subject: [PATCH 2/3] :art: Improve av --- kernel/sql/av_gallery.go | 4 ++-- kernel/sql/av_table.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/sql/av_gallery.go b/kernel/sql/av_gallery.go index 2b0162dfb..9bbdc1c0b 100644 --- a/kernel/sql/av_gallery.go +++ b/kernel/sql/av_gallery.go @@ -113,10 +113,10 @@ func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query // 批量获取块属性以提升性能 ials := BatchGetBlockAttrsWitTrees(ialIDs, boundTrees) - // 渲染自动生成的字段值,比如关联字段、汇总字段、创建时间字段和更新时间字段 + // 渲染自动生成的字段值,比如关联、汇总、创建时间和更新时间 fillAttributeViewAutoGeneratedValues(attrView, ret, ials, cardsValues, depth) - // 最后单独渲染模板字段,这样模板字段就可以使用汇总、关联、创建时间和更新时间字段的值了 + // 最后单独渲染模板字段,这样模板就可以使用汇总、关联、创建时间和更新时间的值了 renderTemplateErr := fillAttributeViewTemplateValues(attrView, ret, ials, cardsValues) if nil != renderTemplateErr { util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000) diff --git a/kernel/sql/av_table.go b/kernel/sql/av_table.go index 40866beac..3e33bd87c 100644 --- a/kernel/sql/av_table.go +++ b/kernel/sql/av_table.go @@ -106,10 +106,10 @@ func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query s } ials := BatchGetBlockAttrs(ialIDs) - // 渲染自动生成的列值,比如关联列、汇总列、创建时间列和更新时间列 + // 渲染自动生成的字段值,比如关联、汇总、创建时间和更新时间 fillAttributeViewAutoGeneratedValues(attrView, ret, ials, rowsValues, depth) - // 最后单独渲染模板列,这样模板列就可以使用汇总、关联、创建时间和更新时间列的值了 + // 最后单独渲染模板字段,这样模板就可以使用汇总、关联、创建时间和更新时间的值了 renderTemplateErr := fillAttributeViewTemplateValues(attrView, ret, ials, rowsValues) if nil != renderTemplateErr { util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000) From e4cb124ffd39e447b98812d1ed99f585d8ebd78d Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Wed, 13 Aug 2025 16:29:31 +0800 Subject: [PATCH 3/3] :art: Improve av https://github.com/siyuan-note/siyuan/issues/15560#issuecomment-3182691193 --- kernel/av/av.go | 68 --------------------------- kernel/model/attribute_view.go | 2 +- kernel/sql/av.go | 84 ++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 69 deletions(-) diff --git a/kernel/av/av.go b/kernel/av/av.go index 6b7af5ea4..17dcbff4e 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -23,8 +23,6 @@ import ( "path/filepath" "sort" "strings" - "text/template" - "text/template/parse" "github.com/88250/gulu" "github.com/88250/lute/ast" @@ -743,72 +741,6 @@ func (av *AttributeView) Clone() (ret *AttributeView) { return } -func (av *AttributeView) GetTemplateKeyRelevantKeys(templateKey *Key) (ret []*Key) { - ret = []*Key{} - if nil == templateKey || "" == templateKey.Template { - return - } - - vars, err := getTemplateVars(templateKey.Template) - if nil != err { - return - } - - for _, kValues := range av.KeyValues { - if gulu.Str.Contains(kValues.Key.Name, vars) { - ret = append(ret, kValues.Key) - } - } - return -} - -func getTemplateVars(tplContent string) ([]string, error) { - goTpl := template.New("").Delims(".action{", "}") - tpl, err := goTpl.Parse(tplContent) - if err != nil { - return nil, err - } - vars := make(map[string]struct{}) - collectVars(tpl.Tree.Root, vars) - var result []string - for v := range vars { - result = append(result, v) - } - return result, nil -} - -func collectVars(node parse.Node, vars map[string]struct{}) { - switch n := node.(type) { - case *parse.ListNode: - for _, child := range n.Nodes { - collectVars(child, vars) - } - case *parse.ActionNode: - collectVars(n.Pipe, vars) - case *parse.PipeNode: - for _, cmd := range n.Cmds { - collectVars(cmd, vars) - } - case *parse.CommandNode: - for _, arg := range n.Args { - collectVars(arg, vars) - } - - if 3 <= len(n.Args) && n.Args[0].Type() == parse.NodeIdentifier && n.Args[1].Type() == parse.NodeDot && n.Args[2].Type() == parse.NodeString { - vars[n.Args[2].(*parse.StringNode).Text] = struct{}{} - } - - case *parse.FieldNode: - if len(n.Ident) > 0 { - vars[n.Ident[0]] = struct{}{} - } - case *parse.VariableNode: - if len(n.Ident) > 0 { - vars[n.Ident[0]] = struct{}{} - } - } -} - func GetAttributeViewDataPath(avID string) (ret string) { av := filepath.Join(util.DataDir, "storage", "av") ret = filepath.Join(av, avID+".json") diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 2fa4741b5..6daa02e24 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -91,7 +91,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group templateRelevantKeys, rollupRelevantKeys := map[string][]*av.Key{}, map[string]*av.Key{} for _, keyValues := range attrView.KeyValues { if av.KeyTypeTemplate == keyValues.Key.Type { - if tplRelevantKeys := attrView.GetTemplateKeyRelevantKeys(keyValues.Key); 0 < len(tplRelevantKeys) { + if tplRelevantKeys := sql.GetTemplateKeyRelevantKeys(attrView, keyValues.Key); 0 < len(tplRelevantKeys) { for _, k := range tplRelevantKeys { templateRelevantKeys[keyValues.Key.ID] = append(templateRelevantKeys[keyValues.Key.ID], k) } diff --git a/kernel/sql/av.go b/kernel/sql/av.go index 2379bafcf..b5d2ce83f 100644 --- a/kernel/sql/av.go +++ b/kernel/sql/av.go @@ -22,8 +22,10 @@ import ( "sort" "strings" "text/template" + "text/template/parse" "time" + "github.com/88250/gulu" "github.com/88250/lute/ast" "github.com/jinzhu/copier" "github.com/siyuan-note/logging" @@ -746,3 +748,85 @@ func manualSort(view *av.View, collection av.Collection) { }) collection.SetItems(items) } + +func GetTemplateKeyRelevantKeys(attrView *av.AttributeView, templateKey *av.Key) (ret []*av.Key) { + ret = []*av.Key{} + if nil == templateKey || "" == templateKey.Template { + return + } + + vars, err := getTemplateVars(templateKey.Template) + if nil != err { + return + } + + for _, kValues := range attrView.KeyValues { + if gulu.Str.Contains(kValues.Key.Name, vars) { + ret = append(ret, kValues.Key) + } + } + + if 1 > len(ret) { + // 没有相关字段情况下直接尝试解析模板,如果能解析成功则返回模板字段本身 https://github.com/siyuan-note/siyuan/issues/15560#issuecomment-3182691193 + goTpl := template.New("").Delims(".action{", "}") + tplFuncMap := filesys.BuiltInTemplateFuncs() + SQLTemplateFuncs(&tplFuncMap) + goTpl = goTpl.Funcs(tplFuncMap) + _, parseErr := goTpl.Funcs(tplFuncMap).Parse(templateKey.Template) + if nil != parseErr { + return + } + ret = append(ret, templateKey) + } + return +} + +func getTemplateVars(tplContent string) ([]string, error) { + goTpl := template.New("").Delims(".action{", "}") + tplFuncMap := filesys.BuiltInTemplateFuncs() + SQLTemplateFuncs(&tplFuncMap) + goTpl = goTpl.Funcs(tplFuncMap) + tpl, parseErr := goTpl.Funcs(tplFuncMap).Parse(tplContent) + if parseErr != nil { + return nil, parseErr + } + vars := make(map[string]struct{}) + collectVars(tpl.Tree.Root, vars) + var result []string + for v := range vars { + result = append(result, v) + } + return result, nil +} + +func collectVars(node parse.Node, vars map[string]struct{}) { + switch n := node.(type) { + case *parse.ListNode: + for _, child := range n.Nodes { + collectVars(child, vars) + } + case *parse.ActionNode: + collectVars(n.Pipe, vars) + case *parse.PipeNode: + for _, cmd := range n.Cmds { + collectVars(cmd, vars) + } + case *parse.CommandNode: + for _, arg := range n.Args { + collectVars(arg, vars) + } + + if 3 <= len(n.Args) && n.Args[0].Type() == parse.NodeIdentifier && n.Args[1].Type() == parse.NodeDot && n.Args[2].Type() == parse.NodeString { + vars[n.Args[2].(*parse.StringNode).Text] = struct{}{} + } + + case *parse.FieldNode: + if len(n.Ident) > 0 { + vars[n.Ident[0]] = struct{}{} + } + case *parse.VariableNode: + if len(n.Ident) > 0 { + vars[n.Ident[0]] = struct{}{} + } + } +}