diff --git a/kernel/av/av.go b/kernel/av/av.go
index bd04f8f4f..204b341e1 100644
--- a/kernel/av/av.go
+++ b/kernel/av/av.go
@@ -23,7 +23,6 @@ import (
"path/filepath"
"sort"
"strings"
- "time"
"github.com/88250/gulu"
"github.com/88250/lute/ast"
@@ -366,129 +365,7 @@ func SaveAttributeView(av *AttributeView) (err error) {
}
// 做一些数据兼容和订正处理
- now := util.CurrentTimeMillis()
- for _, kv := range av.KeyValues {
- switch kv.Key.Type {
- case KeyTypeBlock:
- // 补全 block 的创建时间和更新时间
- for _, v := range kv.Values {
- if 0 == v.Block.Created {
- logging.LogWarnf("block [%s] created time is empty", v.BlockID)
- if "" == v.Block.ID {
- v.Block.ID = v.BlockID
- if "" == v.Block.ID {
- v.Block.ID = ast.NewNodeID()
- v.BlockID = v.Block.ID
- }
- }
-
- createdStr := v.Block.ID[:len("20060102150405")]
- created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
- if nil == parseErr {
- v.Block.Created = created.UnixMilli()
- } else {
- v.Block.Created = now
- }
- }
- if 0 == v.Block.Updated {
- logging.LogWarnf("block [%s] updated time is empty", v.BlockID)
- v.Block.Updated = v.Block.Created
- }
- }
- case KeyTypeNumber:
- for _, v := range kv.Values {
- if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
- v.Number.IsNotEmpty = true
- }
- }
- }
-
- for _, v := range kv.Values {
- if "" == kv.Key.ID {
- kv.Key.ID = ast.NewNodeID()
- for _, val := range kv.Values {
- val.KeyID = kv.Key.ID
- }
- if "" == v.KeyID {
- logging.LogWarnf("value [%s] key id is empty", v.ID)
- v.KeyID = kv.Key.ID
- }
-
- // 校验日期 IsNotEmpty
- if KeyTypeDate == kv.Key.Type {
- if nil != v.Date && 0 != v.Date.Content && !v.Date.IsNotEmpty {
- v.Date.IsNotEmpty = true
- }
- }
-
- // 校验数字 IsNotEmpty
- if KeyTypeNumber == kv.Key.Type {
- if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
- v.Number.IsNotEmpty = true
- }
- }
-
- // 清空关联实际值
- if KeyTypeRelation == kv.Key.Type {
- v.Relation.Contents = nil
- }
-
- // 清空汇总实际值
- if KeyTypeRollup == kv.Key.Type {
- v.Rollup.Contents = nil
- }
-
- for _, view := range av.Views {
- switch view.LayoutType {
- case LayoutTypeTable:
- for _, column := range view.Table.Columns {
- if "" == column.ID {
- column.ID = kv.Key.ID
- break
- }
- }
- }
- }
- }
-
- // 补全值的创建时间和更新时间
- if "" == v.ID {
- logging.LogWarnf("value id is empty")
- v.ID = ast.NewNodeID()
- }
-
- if 0 == v.CreatedAt {
- logging.LogWarnf("value [%s] created time is empty", v.ID)
- createdStr := v.ID[:len("20060102150405")]
- created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
- if nil == parseErr {
- v.CreatedAt = created.UnixMilli()
- } else {
- v.CreatedAt = now
- }
- }
-
- if 0 == v.UpdatedAt {
- logging.LogWarnf("value [%s] updated time is empty", v.ID)
- v.UpdatedAt = v.CreatedAt
- }
- }
- }
-
- // 补全过滤器 Value
- for _, view := range av.Views {
- if nil != view.Table {
- for _, f := range view.Table.Filters {
- if nil != f.Value {
- continue
- }
-
- if k, _ := av.GetKey(f.Column); nil != k {
- f.Value = &Value{Type: k.Type}
- }
- }
- }
- }
+ UpgradeSpec(av)
// 值去重
blockValues := av.GetBlockKeyValues()
diff --git a/kernel/av/av_fix.go b/kernel/av/av_fix.go
new file mode 100644
index 000000000..54f0044c1
--- /dev/null
+++ b/kernel/av/av_fix.go
@@ -0,0 +1,160 @@
+// 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 .
+
+package av
+
+import (
+ "github.com/88250/lute/ast"
+ "github.com/siyuan-note/logging"
+ "github.com/siyuan-note/siyuan/kernel/util"
+ "time"
+)
+
+func UpgradeSpec(av *AttributeView) {
+ upgradeSpec1(av)
+}
+
+func upgradeSpec1(av *AttributeView) {
+ if 1 <= av.Spec {
+ return
+ }
+
+ now := util.CurrentTimeMillis()
+ for _, kv := range av.KeyValues {
+ switch kv.Key.Type {
+ case KeyTypeBlock:
+ // 补全 block 的创建时间和更新时间
+ for _, v := range kv.Values {
+ if 0 == v.Block.Created {
+ logging.LogWarnf("block [%s] created time is empty", v.BlockID)
+ if "" == v.Block.ID {
+ v.Block.ID = v.BlockID
+ if "" == v.Block.ID {
+ v.Block.ID = ast.NewNodeID()
+ v.BlockID = v.Block.ID
+ }
+ }
+
+ createdStr := v.Block.ID[:len("20060102150405")]
+ created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
+ if nil == parseErr {
+ v.Block.Created = created.UnixMilli()
+ } else {
+ v.Block.Created = now
+ }
+ }
+ if 0 == v.Block.Updated {
+ logging.LogWarnf("block [%s] updated time is empty", v.BlockID)
+ v.Block.Updated = v.Block.Created
+ }
+ }
+ case KeyTypeNumber:
+ for _, v := range kv.Values {
+ if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
+ v.Number.IsNotEmpty = true
+ }
+ }
+ }
+
+ for _, v := range kv.Values {
+ if "" == kv.Key.ID {
+ kv.Key.ID = ast.NewNodeID()
+ for _, val := range kv.Values {
+ val.KeyID = kv.Key.ID
+ }
+ if "" == v.KeyID {
+ logging.LogWarnf("value [%s] key id is empty", v.ID)
+ v.KeyID = kv.Key.ID
+ }
+
+ // 校验日期 IsNotEmpty
+ if KeyTypeDate == kv.Key.Type {
+ if nil != v.Date && 0 != v.Date.Content && !v.Date.IsNotEmpty {
+ v.Date.IsNotEmpty = true
+ }
+ }
+
+ // 校验数字 IsNotEmpty
+ if KeyTypeNumber == kv.Key.Type {
+ if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
+ v.Number.IsNotEmpty = true
+ }
+ }
+
+ // 清空关联实际值
+ if KeyTypeRelation == kv.Key.Type {
+ v.Relation.Contents = nil
+ }
+
+ // 清空汇总实际值
+ if KeyTypeRollup == kv.Key.Type {
+ v.Rollup.Contents = nil
+ }
+
+ for _, view := range av.Views {
+ switch view.LayoutType {
+ case LayoutTypeTable:
+ for _, column := range view.Table.Columns {
+ if "" == column.ID {
+ column.ID = kv.Key.ID
+ break
+ }
+ }
+ }
+ }
+ }
+
+ // 补全值的创建时间和更新时间
+ if "" == v.ID {
+ logging.LogWarnf("value id is empty")
+ v.ID = ast.NewNodeID()
+ }
+
+ if 0 == v.CreatedAt {
+ logging.LogWarnf("value [%s] created time is empty", v.ID)
+ createdStr := v.ID[:len("20060102150405")]
+ created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
+ if nil == parseErr {
+ v.CreatedAt = created.UnixMilli()
+ } else {
+ v.CreatedAt = now
+ }
+ }
+
+ if 0 == v.UpdatedAt {
+ logging.LogWarnf("value [%s] updated time is empty", v.ID)
+ v.UpdatedAt = v.CreatedAt
+ }
+ }
+ }
+
+ // 补全过滤器 Value
+ for _, view := range av.Views {
+ if nil != view.Table {
+ for _, f := range view.Table.Filters {
+ if nil != f.Value {
+ continue
+ }
+
+ if k, _ := av.GetKey(f.Column); nil != k {
+ f.Value = &Value{Type: k.Type}
+ }
+ }
+ }
+ }
+
+ av.Spec = 1
+}
diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go
index 929fd149b..cb829e53c 100644
--- a/kernel/model/attribute_view.go
+++ b/kernel/model/attribute_view.go
@@ -875,82 +875,7 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page,
}
// 做一些数据兼容和订正处理,保存的时候也会做 av.SaveAttributeView()
- currentTimeMillis := util.CurrentTimeMillis()
- for _, kv := range attrView.KeyValues {
- switch kv.Key.Type {
- case av.KeyTypeBlock: // 补全 block 的创建时间和更新时间
- for _, v := range kv.Values {
- if 0 == v.Block.Created {
- if "" == v.Block.ID {
- v.Block.ID = v.BlockID
- if "" == v.Block.ID {
- v.Block.ID = ast.NewNodeID()
- v.BlockID = v.Block.ID
- }
- }
-
- createdStr := v.Block.ID[:len("20060102150405")]
- created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
- if nil == parseErr {
- v.Block.Created = created.UnixMilli()
- } else {
- v.Block.Created = currentTimeMillis
- }
- }
- if 0 == v.Block.Updated {
- v.Block.Updated = v.Block.Created
- }
- }
- }
-
- for _, v := range kv.Values {
- // 校验日期 IsNotEmpty
- if av.KeyTypeDate == kv.Key.Type {
- if nil != v.Date && 0 != v.Date.Content && !v.Date.IsNotEmpty {
- v.Date.IsNotEmpty = true
- }
- }
-
- // 校验数字 IsNotEmpty
- if av.KeyTypeNumber == kv.Key.Type {
- if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
- v.Number.IsNotEmpty = true
- }
- }
-
- // 补全值的创建时间和更新时间
- if "" == v.ID {
- v.ID = ast.NewNodeID()
- }
-
- if 0 == v.CreatedAt {
- createdStr := v.ID[:len("20060102150405")]
- created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
- if nil == parseErr {
- v.CreatedAt = created.UnixMilli()
- } else {
- v.CreatedAt = currentTimeMillis
- }
- }
-
- if 0 == v.UpdatedAt {
- v.UpdatedAt = v.CreatedAt
- }
- }
- }
-
- // 补全过滤器 Value
- if nil != view.Table {
- for _, f := range view.Table.Filters {
- if nil != f.Value {
- continue
- }
-
- if k, _ := attrView.GetKey(f.Column); nil != k {
- f.Value = &av.Value{Type: k.Type}
- }
- }
- }
+ upgradeAttributeViewSpec(attrView)
switch view.LayoutType {
case av.LayoutTypeTable:
diff --git a/kernel/model/attribute_view_fix.go b/kernel/model/attribute_view_fix.go
new file mode 100644
index 000000000..cf8fe0d26
--- /dev/null
+++ b/kernel/model/attribute_view_fix.go
@@ -0,0 +1,74 @@
+// 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 .
+
+package model
+
+import (
+ "github.com/88250/gulu"
+ "github.com/siyuan-note/siyuan/kernel/av"
+ "github.com/siyuan-note/siyuan/kernel/filesys"
+ "github.com/siyuan-note/siyuan/kernel/treenode"
+)
+
+func upgradeAttributeViewSpec(attrView *av.AttributeView) {
+ currentSpec := attrView.Spec
+
+ upgradeAttributeViewSpec1(attrView)
+ av.UpgradeSpec(attrView)
+
+ newSpec := attrView.Spec
+ if currentSpec != newSpec {
+ av.SaveAttributeView(attrView)
+ }
+}
+
+func upgradeAttributeViewSpec1(attrView *av.AttributeView) {
+ if 1 <= attrView.Spec {
+ return
+ }
+
+ var blockIDs []string
+ idBlocks := map[string]*av.Value{}
+ for _, kv := range attrView.KeyValues {
+ switch kv.Key.Type {
+ case av.KeyTypeBlock:
+ for _, v := range kv.Values {
+ if !v.IsDetached {
+ blockIDs = append(blockIDs, v.BlockID)
+ idBlocks[v.BlockID] = v
+ }
+ }
+ }
+ }
+ blockIDs = gulu.Str.RemoveDuplicatedElem(blockIDs)
+
+ trees := filesys.LoadTrees(blockIDs)
+ for _, id := range blockIDs {
+ tree := trees[id]
+ if nil == tree {
+ continue
+ }
+
+ node := treenode.GetNodeInTree(tree, id)
+ if nil == node {
+ continue
+ }
+
+ if block := idBlocks[id].Block; nil != block {
+ block.Icon = node.IALAttr("icon")
+ }
+ }
+}