Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vanessa 2025-09-03 09:41:54 +08:00
commit d8bb794437
8 changed files with 164 additions and 134 deletions

View file

@ -128,7 +128,7 @@ func listDocTree(c *gin.Context) {
doctree = append(doctree, parent) doctree = append(doctree, parent)
subPath := filepath.Join(root, entry.Name()) subPath := filepath.Join(root, entry.Name())
if err = walkDocTree(subPath, parent, &ids); err != nil { if err = walkDocTree(subPath, parent, ids); err != nil {
ret.Code = -1 ret.Code = -1
ret.Msg = err.Error() ret.Msg = err.Error()
return return
@ -152,7 +152,7 @@ type DocFile struct {
Children []*DocFile `json:"children,omitempty"` Children []*DocFile `json:"children,omitempty"`
} }
func walkDocTree(p string, docFile *DocFile, ids *map[string]bool) (err error) { func walkDocTree(p string, docFile *DocFile, ids map[string]bool) (err error) {
dir, err := os.ReadDir(p) dir, err := os.ReadDir(p)
if err != nil { if err != nil {
return return
@ -169,7 +169,7 @@ func walkDocTree(p string, docFile *DocFile, ids *map[string]bool) (err error) {
} }
parent := &DocFile{ID: entry.Name()} parent := &DocFile{ID: entry.Name()}
(*ids)[parent.ID] = true ids[parent.ID] = true
docFile.Children = append(docFile.Children, parent) docFile.Children = append(docFile.Children, parent)
subPath := filepath.Join(p, entry.Name()) subPath := filepath.Join(p, entry.Name())
@ -178,10 +178,10 @@ func walkDocTree(p string, docFile *DocFile, ids *map[string]bool) (err error) {
} }
} else { } else {
doc := &DocFile{ID: strings.TrimSuffix(entry.Name(), ".sy")} doc := &DocFile{ID: strings.TrimSuffix(entry.Name(), ".sy")}
if !(*ids)[doc.ID] { if !ids[doc.ID] {
docFile.Children = append(docFile.Children, doc) docFile.Children = append(docFile.Children, doc)
} }
(*ids)[doc.ID] = true ids[doc.ID] = true
} }
} }
return return

View file

@ -26,11 +26,12 @@ import (
// ViewFilter 描述了视图过滤规则的结构。 // ViewFilter 描述了视图过滤规则的结构。
type ViewFilter struct { type ViewFilter struct {
Column string `json:"column"` // 列字段ID Column string `json:"column"` // 列字段ID
Operator FilterOperator `json:"operator"` // 过滤操作符 Qualifier FilterQuantifier `json:"quantifier,omitempty"` // 量词
Value *Value `json:"value"` // 过滤值 Operator FilterOperator `json:"operator"` // 操作符
RelativeDate *RelativeDate `json:"relativeDate,omitempty"` // 相对时间 Value *Value `json:"value"` // 过滤值
RelativeDate2 *RelativeDate `json:"relativeDate2,omitempty"` // 第二个相对时间,用于某些操作符,比如 FilterOperatorIsBetween RelativeDate *RelativeDate `json:"relativeDate,omitempty"` // 相对时间
RelativeDate2 *RelativeDate `json:"relativeDate2,omitempty"` // 第二个相对时间,用于某些操作符,比如 FilterOperatorIsBetween
} }
type RelativeDateUnit int type RelativeDateUnit int
@ -76,7 +77,16 @@ const (
FilterOperatorIsFalse FilterOperator = "Is false" FilterOperatorIsFalse FilterOperator = "Is false"
) )
func Filter(viewable Viewable, attrView *AttributeView) { type FilterQuantifier string
const (
FilterQuantifierUndefined FilterQuantifier = ""
FilterQuantifierAny FilterQuantifier = "Any"
FilterQuantifierAll FilterQuantifier = "All"
FilterQuantifierNone FilterQuantifier = "None"
)
func Filter(viewable Viewable, attrView *AttributeView, rollupFurtherCollections map[string]Collection, cachedAttrViews map[string]*AttributeView) {
collection := viewable.(Collection) collection := viewable.(Collection)
filters := collection.GetFilters() filters := collection.GetFilters()
if 1 > len(filters) { if 1 > len(filters) {
@ -94,8 +104,6 @@ func Filter(viewable Viewable, attrView *AttributeView) {
} }
var items []Item var items []Item
attrViewCache := map[string]*AttributeView{}
attrViewCache[attrView.ID] = attrView
for _, item := range collection.GetItems() { for _, item := range collection.GetItems() {
pass := true pass := true
values := item.GetValues() values := item.GetValues()
@ -116,7 +124,7 @@ func Filter(viewable Viewable, attrView *AttributeView) {
break break
} }
if !values[index].Filter(filters[j], attrView, item.GetID(), &attrViewCache) { if !values[index].Filter(filters[j], attrView, item.GetID(), rollupFurtherCollections, cachedAttrViews) {
pass = false pass = false
break break
} }
@ -128,7 +136,7 @@ func Filter(viewable Viewable, attrView *AttributeView) {
collection.SetItems(items) collection.SetItems(items)
} }
func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID string, attrViewCache *map[string]*AttributeView) bool { func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, itemID string, rollupFurtherCollections map[string]Collection, cachedAttrViews map[string]*AttributeView) bool {
if nil == filter || (nil == filter.Value && nil == filter.RelativeDate) { if nil == filter || (nil == filter.Value && nil == filter.RelativeDate) {
return true return true
} }
@ -149,7 +157,6 @@ func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID st
nil != filter.Value.Rollup && 0 < len(filter.Value.Rollup.Contents) { nil != filter.Value.Rollup && 0 < len(filter.Value.Rollup.Contents) {
// 单独处理汇总类型的比较 // 单独处理汇总类型的比较
// 处理值比较
key, _ := attrView.GetKey(value.KeyID) key, _ := attrView.GetKey(value.KeyID)
if nil == key { if nil == key {
return false return false
@ -160,16 +167,16 @@ func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID st
return false return false
} }
relVal := attrView.GetValue(relKey.ID, rowID) relVal := attrView.GetValue(relKey.ID, itemID)
if nil == relVal || nil == relVal.Relation { if nil == relVal || nil == relVal.Relation {
return false return false
} }
destAv := (*attrViewCache)[relKey.Relation.AvID] destAv := cachedAttrViews[relKey.Relation.AvID]
if nil == destAv { if nil == destAv {
destAv, _ = ParseAttributeView(relKey.Relation.AvID) destAv, _ = ParseAttributeView(relKey.Relation.AvID)
if nil != destAv { if nil != destAv {
(*attrViewCache)[relKey.Relation.AvID] = destAv cachedAttrViews[relKey.Relation.AvID] = destAv
} }
} }
if nil == destAv { if nil == destAv {
@ -181,32 +188,40 @@ func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID st
return false return false
} }
value.Rollup.BuildContents(destAv.KeyValues, destKey, relVal, key.Rollup.Calc, nil) value.Rollup.BuildContents(destAv.KeyValues, destKey, relVal, key.Rollup.Calc, rollupFurtherCollections[key.ID])
for _, content := range value.Rollup.Contents {
switch filter.Operator { switch filter.Qualifier {
case FilterOperatorContains: case FilterQuantifierUndefined, FilterQuantifierAny:
if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) { for _, content := range value.Rollup.Contents {
return true switch filter.Operator {
} case FilterOperatorContains:
case FilterOperatorDoesNotContain: if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
ret := content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) return true
if !ret { }
return false case FilterOperatorDoesNotContain:
} if !content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
default: return false
if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) { }
return true default:
if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
}
}
case FilterQuantifierAll:
for _, content := range value.Rollup.Contents {
if !content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return false
}
}
return true
case FilterQuantifierNone:
for _, content := range value.Rollup.Contents {
if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return false
} }
} }
}
switch filter.Operator {
case FilterOperatorContains:
return false
case FilterOperatorDoesNotContain:
return true return true
default:
return false
} }
} }
@ -523,17 +538,17 @@ func filterRelativeTime(valueMills int64, valueIsNotEmpty bool, operator FilterO
switch operator { switch operator {
case FilterOperatorIsEqual: case FilterOperatorIsEqual:
return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && valueTime.Before(otherValueEnd) return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && (valueTime.Before(otherValueEnd) || valueTime.Equal(otherValueEnd))
case FilterOperatorIsNotEqual: case FilterOperatorIsNotEqual:
return valueTime.Before(otherValueStart) || valueTime.After(otherValueEnd) return valueTime.Before(otherValueStart) || valueTime.After(otherValueEnd)
case FilterOperatorIsGreater: case FilterOperatorIsGreater:
return valueTime.After(otherValueStart) return valueTime.After(otherValueEnd)
case FilterOperatorIsGreaterOrEqual: case FilterOperatorIsGreaterOrEqual:
return valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart) return valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)
case FilterOperatorIsLess: case FilterOperatorIsLess:
return valueTime.Before(otherValueStart) return valueTime.Before(otherValueStart)
case FilterOperatorIsLessOrEqual: case FilterOperatorIsLessOrEqual:
return valueTime.Before(otherValueStart) || valueTime.Equal(otherValueStart) return valueTime.Before(otherValueEnd) || valueTime.Equal(otherValueEnd)
case FilterOperatorIsBetween: case FilterOperatorIsBetween:
if RelativeDateDirectionBefore == direction { if RelativeDateDirectionBefore == direction {
if RelativeDateDirectionBefore == direction2 { if RelativeDateDirectionBefore == direction2 {

View file

@ -1465,16 +1465,16 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
return return
} }
attrViewCache := map[string]*av.AttributeView{} cachedAttrViews := map[string]*av.AttributeView{}
avIDs := strings.Split(avs, ",") avIDs := strings.Split(avs, ",")
for _, avID := range avIDs { for _, avID := range avIDs {
attrView := attrViewCache[avID] attrView := cachedAttrViews[avID]
if nil == attrView { if nil == attrView {
attrView, _ = av.ParseAttributeView(avID) attrView, _ = av.ParseAttributeView(avID)
if nil == attrView { if nil == attrView {
return return
} }
attrViewCache[avID] = attrView cachedAttrViews[avID] = attrView
} }
if !attrView.ExistBoundBlock(nodeID) { if !attrView.ExistBoundBlock(nodeID) {
@ -1572,42 +1572,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
// 再渲染关联和汇总 // 再渲染关联和汇总
rollupFurtherCollections := map[string]av.Collection{} rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
for _, kv := range keyValues {
if av.KeyTypeRollup != kv.Key.Type {
continue
}
relKey, _ := attrView.GetKey(kv.Key.Rollup.RelationKeyID)
if nil == relKey {
continue
}
destAv := attrViewCache[relKey.Relation.AvID]
if nil == destAv {
destAv, _ = av.ParseAttributeView(relKey.Relation.AvID)
if nil == destAv {
continue
}
attrViewCache[relKey.Relation.AvID] = destAv
}
destKey, _ := destAv.GetKey(kv.Key.Rollup.KeyID)
if nil == destKey {
continue
}
isSameAv := destAv.ID == attrView.ID
var furtherCollection av.Collection
if av.KeyTypeTemplate == destKey.Type || (!isSameAv && (av.KeyTypeUpdated == destKey.Type || av.KeyTypeCreated == destKey.Type)) {
viewable := sql.RenderView(destAv, destAv.Views[0], "")
if nil != viewable {
furtherCollection = viewable.(av.Collection)
}
}
rollupFurtherCollections[kv.Key.ID] = furtherCollection
}
for _, kv := range keyValues { for _, kv := range keyValues {
switch kv.Key.Type { switch kv.Key.Type {
case av.KeyTypeRollup: case av.KeyTypeRollup:
@ -1622,13 +1587,13 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
relVal := attrView.GetValue(kv.Key.Rollup.RelationKeyID, kv.Values[0].BlockID) relVal := attrView.GetValue(kv.Key.Rollup.RelationKeyID, kv.Values[0].BlockID)
if nil != relVal && nil != relVal.Relation { if nil != relVal && nil != relVal.Relation {
destAv := attrViewCache[relKey.Relation.AvID] destAv := cachedAttrViews[relKey.Relation.AvID]
if nil == destAv { if nil == destAv {
destAv, _ = av.ParseAttributeView(relKey.Relation.AvID) destAv, _ = av.ParseAttributeView(relKey.Relation.AvID)
if nil == destAv { if nil == destAv {
break break
} }
attrViewCache[relKey.Relation.AvID] = destAv cachedAttrViews[relKey.Relation.AvID] = destAv
} }
destKey, _ := destAv.GetKey(kv.Key.Rollup.KeyID) destKey, _ := destAv.GetKey(kv.Key.Rollup.KeyID)
@ -1642,14 +1607,14 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
break break
} }
destAv := attrViewCache[kv.Key.Relation.AvID] destAv := cachedAttrViews[kv.Key.Relation.AvID]
if nil == destAv { if nil == destAv {
destAv, _ = av.ParseAttributeView(kv.Key.Relation.AvID) destAv, _ = av.ParseAttributeView(kv.Key.Relation.AvID)
if nil == destAv { if nil == destAv {
break break
} }
attrViewCache[kv.Key.Relation.AvID] = destAv cachedAttrViews[kv.Key.Relation.AvID] = destAv
} }
blocks := map[string]*av.Value{} blocks := map[string]*av.Value{}
@ -2070,8 +2035,10 @@ func GetCurrentAttributeViewImages(avID, viewID, query string) (ret []string, er
view = attrView.GetView(attrView.ViewID) view = attrView.GetView(attrView.ViewID)
} }
cachedAttrViews := map[string]*av.AttributeView{}
rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
table := getAttrViewTable(attrView, view, query) table := getAttrViewTable(attrView, view, query)
av.Filter(table, attrView) av.Filter(table, attrView, rollupFurtherCollections, cachedAttrViews)
av.Sort(table, attrView) av.Sort(table, attrView)
ids := map[string]bool{} ids := map[string]bool{}
@ -2566,6 +2533,7 @@ func (tx *Transaction) doDuplicateAttrViewView(operation *Operation) (ret *TxErr
for _, filter := range masterView.Filters { for _, filter := range masterView.Filters {
view.Filters = append(view.Filters, &av.ViewFilter{ view.Filters = append(view.Filters, &av.ViewFilter{
Column: filter.Column, Column: filter.Column,
Qualifier: filter.Qualifier,
Operator: filter.Operator, Operator: filter.Operator,
Value: filter.Value, Value: filter.Value,
RelativeDate: filter.RelativeDate, RelativeDate: filter.RelativeDate,
@ -3284,8 +3252,10 @@ func getNewValueByNearItem(nearItem av.Item, key *av.Key, addingBlockID string)
} }
func getNearItem(attrView *av.AttributeView, view, groupView *av.View, previousItemID string) (ret av.Item) { func getNearItem(attrView *av.AttributeView, view, groupView *av.View, previousItemID string) (ret av.Item) {
cachedAttrViews := map[string]*av.AttributeView{}
rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
viewable := sql.RenderGroupView(attrView, view, groupView, "") viewable := sql.RenderGroupView(attrView, view, groupView, "")
av.Filter(viewable, attrView) av.Filter(viewable, attrView, rollupFurtherCollections, cachedAttrViews)
av.Sort(viewable, attrView) av.Sort(viewable, attrView)
items := viewable.(av.Collection).GetItems() items := viewable.(av.Collection).GetItems()
if 0 < len(items) { if 0 < len(items) {

View file

@ -377,7 +377,9 @@ func renderViewableInstance(viewable av.Viewable, view *av.View, attrView *av.At
return return
} }
av.Filter(viewable, attrView) cachedAttrViews := map[string]*av.AttributeView{}
rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
av.Filter(viewable, attrView, rollupFurtherCollections, cachedAttrViews)
av.Sort(viewable, attrView) av.Sort(viewable, attrView)
av.Calc(viewable, attrView) av.Calc(viewable, attrView)

View file

@ -83,7 +83,9 @@ func ExportAv2CSV(avID, blockID string) (zipPath string, err error) {
table := getAttrViewTable(attrView, view, "") table := getAttrViewTable(attrView, view, "")
// 遵循视图过滤和排序规则 Use filtering and sorting of current view settings when exporting database blocks https://github.com/siyuan-note/siyuan/issues/10474 // 遵循视图过滤和排序规则 Use filtering and sorting of current view settings when exporting database blocks https://github.com/siyuan-note/siyuan/issues/10474
av.Filter(table, attrView) cachedAttrViews := map[string]*av.AttributeView{}
rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
av.Filter(table, attrView, rollupFurtherCollections, cachedAttrViews)
av.Sort(table, attrView) av.Sort(table, attrView)
exportFolder := filepath.Join(util.TempDir, "export", "csv", name) exportFolder := filepath.Join(util.TempDir, "export", "csv", name)
@ -273,7 +275,7 @@ func Export2Liandi(id string) (err error) {
".md", 3, 1, 1, ".md", 3, 1, 1,
"#", "#", "#", "#",
"", "", "", "",
false, false, nil, true, false, &map[string]*parse.Tree{}) false, false, nil, true, false, map[string]*parse.Tree{})
result := gulu.Ret.NewResult() result := gulu.Ret.NewResult()
request := httpclient.NewCloudRequest30s() request := httpclient.NewCloudRequest30s()
request = request. request = request.
@ -584,7 +586,7 @@ func Preview(id string, fillCSSVar bool) (retStdHTML string) {
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
"#", "#", // 这里固定使用 # 包裹标签,否则无法正确解析标签 https://github.com/siyuan-note/siyuan/issues/13857 "#", "#", // 这里固定使用 # 包裹标签,否则无法正确解析标签 https://github.com/siyuan-note/siyuan/issues/13857
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, Conf.Export.InlineMemo, true, true, &map[string]*parse.Tree{}) Conf.Export.AddTitle, Conf.Export.InlineMemo, true, true, map[string]*parse.Tree{})
luteEngine := NewLute() luteEngine := NewLute()
enableLuteInlineSyntax(luteEngine) enableLuteInlineSyntax(luteEngine)
luteEngine.SetFootnotes(true) luteEngine.SetFootnotes(true)
@ -722,7 +724,7 @@ func ExportMarkdownHTML(id, savePath string, docx, merge bool) (name, dom string
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, Conf.Export.InlineMemo, true, true, &map[string]*parse.Tree{}) Conf.Export.AddTitle, Conf.Export.InlineMemo, true, true, map[string]*parse.Tree{})
name = path.Base(tree.HPath) name = path.Base(tree.HPath)
name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614 name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614
savePath = strings.TrimSpace(savePath) savePath = strings.TrimSpace(savePath)
@ -884,7 +886,7 @@ func ExportHTML(id, savePath string, pdf, image, keepFold, merge bool) (name, do
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, Conf.Export.InlineMemo, true, true, &map[string]*parse.Tree{}) Conf.Export.AddTitle, Conf.Export.InlineMemo, true, true, map[string]*parse.Tree{})
adjustHeadingLevel(bt, tree) adjustHeadingLevel(bt, tree)
name = path.Base(tree.HPath) name = path.Base(tree.HPath)
name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614 name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614
@ -1518,7 +1520,7 @@ func ExportStdMarkdown(id string, assetsDestSpace2Underscore, fillCSSVar, adjust
".md", Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, ".md", Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, Conf.Export.InlineMemo, defBlockIDs, true, fillCSSVar, &map[string]*parse.Tree{}) Conf.Export.AddTitle, Conf.Export.InlineMemo, defBlockIDs, true, fillCSSVar, map[string]*parse.Tree{})
} }
func ExportPandocConvertZip(ids []string, pandocTo, ext string) (name, zipPath string) { func ExportPandocConvertZip(ids []string, pandocTo, ext string) (name, zipPath string) {
@ -1684,7 +1686,7 @@ func exportSYZip(boxID, rootDirPath, baseFolderName string, docPaths []string) (
util.PushEndlessProgress(Conf.language(65) + " " + fmt.Sprintf(Conf.language(70), fmt.Sprintf("%d/%d %s", count, len(docPaths), tree.Root.IALAttr("title")))) util.PushEndlessProgress(Conf.language(65) + " " + fmt.Sprintf(Conf.language(70), fmt.Sprintf("%d/%d %s", count, len(docPaths), tree.Root.IALAttr("title"))))
refs := map[string]*parse.Tree{} refs := map[string]*parse.Tree{}
exportRefTrees(tree, &[]string{}, &refs, &treeCache) exportRefTrees(tree, &[]string{}, refs, treeCache)
for refTreeID, refTree := range refs { for refTreeID, refTree := range refs {
if nil == trees[refTreeID] { if nil == trees[refTreeID] {
refTrees[refTreeID] = refTree refTrees[refTreeID] = refTree
@ -2012,7 +2014,7 @@ func ExportMarkdownContent(id string, refMode, embedMode int, addYfm, fillCSSVar
".md", refMode, embedMode, Conf.Export.FileAnnotationRefMode, ".md", refMode, embedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, Conf.Export.InlineMemo, nil, true, fillCSSVar, &map[string]*parse.Tree{}) Conf.Export.AddTitle, Conf.Export.InlineMemo, nil, true, fillCSSVar, map[string]*parse.Tree{})
docIAL := parse.IAL2Map(tree.Root.KramdownIAL) docIAL := parse.IAL2Map(tree.Root.KramdownIAL)
if addYfm { if addYfm {
exportedMd = yfm(docIAL) + exportedMd exportedMd = yfm(docIAL) + exportedMd
@ -2020,7 +2022,7 @@ func ExportMarkdownContent(id string, refMode, embedMode int, addYfm, fillCSSVar
return return
} }
func exportMarkdownContent(id, ext string, exportRefMode int, defBlockIDs []string, singleFile bool, treeCache *map[string]*parse.Tree) (tree *parse.Tree, exportedMd string, isEmpty bool) { func exportMarkdownContent(id, ext string, exportRefMode int, defBlockIDs []string, singleFile bool, treeCache map[string]*parse.Tree) (tree *parse.Tree, exportedMd string, isEmpty bool) {
tree, err := loadTreeWithCache(id, treeCache) tree, err := loadTreeWithCache(id, treeCache)
if err != nil { if err != nil {
logging.LogErrorf("load tree by block id [%s] failed: %s", id, err) logging.LogErrorf("load tree by block id [%s] failed: %s", id, err)
@ -2055,7 +2057,7 @@ func exportMarkdownContent(id, ext string, exportRefMode int, defBlockIDs []stri
func exportMarkdownContent0(id string, tree *parse.Tree, cloudAssetsBase string, assetsDestSpace2Underscore, adjustHeadingLv, imgTag bool, func exportMarkdownContent0(id string, tree *parse.Tree, cloudAssetsBase string, assetsDestSpace2Underscore, adjustHeadingLv, imgTag bool,
ext string, blockRefMode, blockEmbedMode, fileAnnotationRefMode int, ext string, blockRefMode, blockEmbedMode, fileAnnotationRefMode int,
tagOpenMarker, tagCloseMarker string, blockRefTextLeft, blockRefTextRight string, tagOpenMarker, tagCloseMarker string, blockRefTextLeft, blockRefTextRight string,
addTitle, inlineMemo bool, defBlockIDs []string, singleFile, fillCSSVar bool, treeCache *map[string]*parse.Tree) (ret string) { addTitle, inlineMemo bool, defBlockIDs []string, singleFile, fillCSSVar bool, treeCache map[string]*parse.Tree) (ret string) {
tree = exportTree(tree, false, false, false, tree = exportTree(tree, false, false, false,
blockRefMode, blockEmbedMode, fileAnnotationRefMode, blockRefMode, blockEmbedMode, fileAnnotationRefMode,
tagOpenMarker, tagCloseMarker, tagOpenMarker, tagCloseMarker,
@ -2182,11 +2184,11 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
blockRefMode, blockEmbedMode, fileAnnotationRefMode int, blockRefMode, blockEmbedMode, fileAnnotationRefMode int,
tagOpenMarker, tagCloseMarker string, tagOpenMarker, tagCloseMarker string,
blockRefTextLeft, blockRefTextRight string, blockRefTextLeft, blockRefTextRight string,
addTitle, inlineMemo, addDocAnchorSpan, singleFile bool, treeCache *map[string]*parse.Tree) (ret *parse.Tree) { addTitle, inlineMemo, addDocAnchorSpan, singleFile bool, treeCache map[string]*parse.Tree) (ret *parse.Tree) {
luteEngine := NewLute() luteEngine := NewLute()
ret = tree ret = tree
id := tree.Root.ID id := tree.Root.ID
(*treeCache)[tree.ID] = tree treeCache[tree.ID] = tree
// 解析查询嵌入节点 // 解析查询嵌入节点
depth := 0 depth := 0
@ -2544,7 +2546,9 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
table := getAttrViewTable(attrView, view, "") table := getAttrViewTable(attrView, view, "")
// 遵循视图过滤和排序规则 Use filtering and sorting of current view settings when exporting database blocks https://github.com/siyuan-note/siyuan/issues/10474 // 遵循视图过滤和排序规则 Use filtering and sorting of current view settings when exporting database blocks https://github.com/siyuan-note/siyuan/issues/10474
av.Filter(table, attrView) cachedAttrViews := map[string]*av.AttributeView{}
rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
av.Filter(table, attrView, rollupFurtherCollections, cachedAttrViews)
av.Sort(table, attrView) av.Sort(table, attrView)
var aligns []int var aligns []int
@ -2821,7 +2825,7 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
return ret return ret
} }
func resolveFootnotesDefs(refFootnotes *[]*refAsFootnotes, currentTree *parse.Tree, currentTreeNodeIDs map[string]bool, blockRefTextLeft, blockRefTextRight string, treeCache *map[string]*parse.Tree) (footnotesDefBlock *ast.Node) { func resolveFootnotesDefs(refFootnotes *[]*refAsFootnotes, currentTree *parse.Tree, currentTreeNodeIDs map[string]bool, blockRefTextLeft, blockRefTextRight string, treeCache map[string]*parse.Tree) (footnotesDefBlock *ast.Node) {
if 1 > len(*refFootnotes) { if 1 > len(*refFootnotes) {
return nil return nil
} }
@ -2955,7 +2959,7 @@ func resolveFootnotesDefs(refFootnotes *[]*refAsFootnotes, currentTree *parse.Tr
return return
} }
func blockLink2Ref(currentTree *parse.Tree, id string, treeCache *map[string]*parse.Tree, depth *int) { func blockLink2Ref(currentTree *parse.Tree, id string, treeCache map[string]*parse.Tree, depth *int) {
*depth++ *depth++
if 4096 < *depth { if 4096 < *depth {
return return
@ -2985,7 +2989,7 @@ func blockLink2Ref(currentTree *parse.Tree, id string, treeCache *map[string]*pa
return return
} }
func blockLink2Ref0(currentTree *parse.Tree, node *ast.Node, treeCache *map[string]*parse.Tree, depth *int) { func blockLink2Ref0(currentTree *parse.Tree, node *ast.Node, treeCache map[string]*parse.Tree, depth *int) {
ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus { ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering { if !entering {
return ast.WalkContinue return ast.WalkContinue
@ -3006,7 +3010,7 @@ func blockLink2Ref0(currentTree *parse.Tree, node *ast.Node, treeCache *map[stri
}) })
} }
func collectFootnotesDefs(currentTree *parse.Tree, id string, refFootnotes *[]*refAsFootnotes, treeCache *map[string]*parse.Tree, depth *int) { func collectFootnotesDefs(currentTree *parse.Tree, id string, refFootnotes *[]*refAsFootnotes, treeCache map[string]*parse.Tree, depth *int) {
*depth++ *depth++
if 4096 < *depth { if 4096 < *depth {
return return
@ -3035,7 +3039,7 @@ func collectFootnotesDefs(currentTree *parse.Tree, id string, refFootnotes *[]*r
return return
} }
func collectFootnotesDefs0(currentTree *parse.Tree, node *ast.Node, refFootnotes *[]*refAsFootnotes, treeCache *map[string]*parse.Tree, depth *int) { func collectFootnotesDefs0(currentTree *parse.Tree, node *ast.Node, refFootnotes *[]*refAsFootnotes, treeCache map[string]*parse.Tree, depth *int) {
ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus { ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering { if !entering {
return ast.WalkContinue return ast.WalkContinue
@ -3143,7 +3147,7 @@ func processFileAnnotationRef(refID string, n *ast.Node, fileAnnotationRefMode i
} }
func exportPandocConvertZip(baseFolderName string, docPaths, defBlockIDs []string, func exportPandocConvertZip(baseFolderName string, docPaths, defBlockIDs []string,
pandocFrom, pandocTo, ext string, treeCache *map[string]*parse.Tree) (zipPath string) { pandocFrom, pandocTo, ext string, treeCache map[string]*parse.Tree) (zipPath string) {
defer util.ClearPushProgress(100) defer util.ClearPushProgress(100)
dir, name := path.Split(baseFolderName) dir, name := path.Split(baseFolderName)
@ -3296,9 +3300,9 @@ func getExportBlockRefLinkText(blockRef *ast.Node, blockRefTextLeft, blockRefTex
return return
} }
func prepareExportTrees(docPaths []string) (defBlockIDs []string, trees *map[string]*parse.Tree, relatedDocPaths []string) { func prepareExportTrees(docPaths []string) (defBlockIDs []string, trees map[string]*parse.Tree, relatedDocPaths []string) {
trees = &map[string]*parse.Tree{} trees = map[string]*parse.Tree{}
treeCache := &map[string]*parse.Tree{} treeCache := map[string]*parse.Tree{}
defBlockIDs = []string{} defBlockIDs = []string{}
for i, p := range docPaths { for i, p := range docPaths {
rootID := strings.TrimSuffix(path.Base(p), ".sy") rootID := strings.TrimSuffix(path.Base(p), ".sy")
@ -3315,18 +3319,18 @@ func prepareExportTrees(docPaths []string) (defBlockIDs []string, trees *map[str
util.PushEndlessProgress(Conf.language(65) + " " + fmt.Sprintf(Conf.language(70), fmt.Sprintf("%d/%d %s", i+1, len(docPaths), tree.Root.IALAttr("title")))) util.PushEndlessProgress(Conf.language(65) + " " + fmt.Sprintf(Conf.language(70), fmt.Sprintf("%d/%d %s", i+1, len(docPaths), tree.Root.IALAttr("title"))))
} }
for _, tree := range *trees { for _, tree := range trees {
relatedDocPaths = append(relatedDocPaths, tree.Path) relatedDocPaths = append(relatedDocPaths, tree.Path)
} }
relatedDocPaths = gulu.Str.RemoveDuplicatedElem(relatedDocPaths) relatedDocPaths = gulu.Str.RemoveDuplicatedElem(relatedDocPaths)
return return
} }
func exportRefTrees(tree *parse.Tree, defBlockIDs *[]string, retTrees, treeCache *map[string]*parse.Tree) { func exportRefTrees(tree *parse.Tree, defBlockIDs *[]string, retTrees, treeCache map[string]*parse.Tree) {
if nil != (*retTrees)[tree.ID] { if nil != retTrees[tree.ID] {
return return
} }
(*retTrees)[tree.ID] = tree retTrees[tree.ID] = tree
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus { ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering { if !entering {
@ -3345,14 +3349,14 @@ func exportRefTrees(tree *parse.Tree, defBlockIDs *[]string, retTrees, treeCache
var defTree *parse.Tree var defTree *parse.Tree
var err error var err error
if (*treeCache)[defBlock.RootID] != nil { if treeCache[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID] defTree = treeCache[defBlock.RootID]
} else { } else {
defTree, err = loadTreeWithCache(defBlock.RootID, treeCache) defTree, err = loadTreeWithCache(defBlock.RootID, treeCache)
if err != nil { if err != nil {
return ast.WalkSkipChildren return ast.WalkSkipChildren
} }
(*treeCache)[defBlock.RootID] = defTree treeCache[defBlock.RootID] = defTree
} }
*defBlockIDs = append(*defBlockIDs, defID) *defBlockIDs = append(*defBlockIDs, defID)
@ -3369,14 +3373,14 @@ func exportRefTrees(tree *parse.Tree, defBlockIDs *[]string, retTrees, treeCache
var defTree *parse.Tree var defTree *parse.Tree
var err error var err error
if (*treeCache)[defBlock.RootID] != nil { if treeCache[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID] defTree = treeCache[defBlock.RootID]
} else { } else {
defTree, err = loadTreeWithCache(defBlock.RootID, treeCache) defTree, err = loadTreeWithCache(defBlock.RootID, treeCache)
if err != nil { if err != nil {
return ast.WalkSkipChildren return ast.WalkSkipChildren
} }
(*treeCache)[defBlock.RootID] = defTree treeCache[defBlock.RootID] = defTree
} }
*defBlockIDs = append(*defBlockIDs, defID) *defBlockIDs = append(*defBlockIDs, defID)
@ -3412,14 +3416,14 @@ func exportRefTrees(tree *parse.Tree, defBlockIDs *[]string, retTrees, treeCache
var defTree *parse.Tree var defTree *parse.Tree
var err error var err error
if (*treeCache)[defBlock.RootID] != nil { if treeCache[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID] defTree = treeCache[defBlock.RootID]
} else { } else {
defTree, err = loadTreeWithCache(defBlock.RootID, treeCache) defTree, err = loadTreeWithCache(defBlock.RootID, treeCache)
if err != nil { if err != nil {
continue continue
} }
(*treeCache)[defBlock.RootID] = defTree treeCache[defBlock.RootID] = defTree
} }
*defBlockIDs = append(*defBlockIDs, val.BlockID) *defBlockIDs = append(*defBlockIDs, val.BlockID)
@ -3432,13 +3436,13 @@ func exportRefTrees(tree *parse.Tree, defBlockIDs *[]string, retTrees, treeCache
*defBlockIDs = gulu.Str.RemoveDuplicatedElem(*defBlockIDs) *defBlockIDs = gulu.Str.RemoveDuplicatedElem(*defBlockIDs)
} }
func loadTreeWithCache(id string, treeCache *map[string]*parse.Tree) (tree *parse.Tree, err error) { func loadTreeWithCache(id string, treeCache map[string]*parse.Tree) (tree *parse.Tree, err error) {
if tree = (*treeCache)[id]; nil != tree { if tree = treeCache[id]; nil != tree {
return return
} }
tree, err = LoadTreeByBlockID(id) tree, err = LoadTreeByBlockID(id)
if nil == err && nil != tree { if nil == err && nil != tree {
(*treeCache)[id] = tree treeCache[id] = tree
} }
return return
} }

View file

@ -280,10 +280,10 @@ func generateAttrViewItems(attrView *av.AttributeView, view *av.View) (ret map[s
return return
} }
func filterNotFoundAttrViewItems(keyValuesMap *map[string][]*av.KeyValues) { func filterNotFoundAttrViewItems(keyValuesMap map[string][]*av.KeyValues) {
var notFound []string var notFound []string
var toCheckBlockIDs []string var toCheckBlockIDs []string
for blockID, keyValues := range *keyValuesMap { for blockID, keyValues := range keyValuesMap {
blockValue := getBlockValue(keyValues) blockValue := getBlockValue(keyValues)
if nil == blockValue || nil == blockValue.Block { if nil == blockValue || nil == blockValue.Block {
notFound = append(notFound, blockID) notFound = append(notFound, blockID)
@ -308,7 +308,7 @@ func filterNotFoundAttrViewItems(keyValuesMap *map[string][]*av.KeyValues) {
} }
} }
for _, blockID := range notFound { for _, blockID := range notFound {
delete(*keyValuesMap, blockID) delete(keyValuesMap, blockID)
} }
} }
@ -518,6 +518,45 @@ func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection
} }
} }
func GetFurtherCollections(attrView *av.AttributeView, cachedAttrViews map[string]*av.AttributeView) (ret map[string]av.Collection) {
ret = map[string]av.Collection{}
for _, kv := range attrView.KeyValues {
if av.KeyTypeRollup != kv.Key.Type {
continue
}
relKey, _ := attrView.GetKey(kv.Key.Rollup.RelationKeyID)
if nil == relKey {
continue
}
destAv := cachedAttrViews[relKey.Relation.AvID]
if nil == destAv {
destAv, _ = av.ParseAttributeView(relKey.Relation.AvID)
if nil == destAv {
continue
}
cachedAttrViews[relKey.Relation.AvID] = destAv
}
destKey, _ := destAv.GetKey(kv.Key.Rollup.KeyID)
if nil == destKey {
continue
}
isSameAv := destAv.ID == attrView.ID
var furtherCollection av.Collection
if av.KeyTypeTemplate == destKey.Type || (!isSameAv && (av.KeyTypeUpdated == destKey.Type || av.KeyTypeCreated == destKey.Type)) {
viewable := RenderView(destAv, destAv.Views[0], "")
if nil != viewable {
furtherCollection = viewable.(av.Collection)
}
}
ret[kv.Key.ID] = furtherCollection
}
return
}
func fillAttributeViewTemplateValues(attrView *av.AttributeView, view *av.View, collection av.Collection, ials map[string]map[string]string) (err error) { func fillAttributeViewTemplateValues(attrView *av.AttributeView, view *av.View, collection av.Collection, ials map[string]map[string]string) (err error) {
items := generateAttrViewItems(attrView, view) items := generateAttrViewItems(attrView, view)
existTemplateField := false existTemplateField := false

View file

@ -62,7 +62,7 @@ func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query
} }
cardsValues := generateAttrViewItems(attrView, view) // 生成卡片 cardsValues := generateAttrViewItems(attrView, view) // 生成卡片
filterNotFoundAttrViewItems(&cardsValues) // 过滤掉不存在的卡片 filterNotFoundAttrViewItems(cardsValues) // 过滤掉不存在的卡片
// 批量加载绑定块对应的树 // 批量加载绑定块对应的树
var ialIDs []string var ialIDs []string

View file

@ -62,7 +62,7 @@ func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query s
} }
rowsValues := generateAttrViewItems(attrView, view) // 生成行 rowsValues := generateAttrViewItems(attrView, view) // 生成行
filterNotFoundAttrViewItems(&rowsValues) // 过滤掉不存在的行 filterNotFoundAttrViewItems(rowsValues) // 过滤掉不存在的行
// 生成行单元格 // 生成行单元格
for rowID, rowValues := range rowsValues { for rowID, rowValues := range rowsValues {