From 87096f96712e7f7d09eb25c1836cb92d4a599ed8 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 23 Dec 2023 11:35:53 +0800 Subject: [PATCH 1/5] :art: Add Relation and Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/av/av.go | 1 - kernel/model/attribute_view.go | 38 ++++++++++++++++++++++++++++++++++ kernel/model/transaction.go | 20 +++++++++++------- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/kernel/av/av.go b/kernel/av/av.go index a85a3766d..0767c850c 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -90,7 +90,6 @@ type Key struct { // 关联列 RelationAvID string `json:"relationAvID"` // 关联的属性视图 ID - RelationKeyID string `json:"relationKeyID"` // 关联列 ID IsBiRelation bool `json:"isBiRelation"` // 是否双向关联 BackRelationKeyID string `json:"backRelationKeyID"` // 双向关联时回链关联列的 ID diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 93cc157fb..0c143f844 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -655,6 +655,44 @@ func getRowBlockValue(keyValues []*av.KeyValues) (ret *av.Value) { return } +func (tx *Transaction) doUpdateAttrViewColRelation(operation *Operation) (ret *TxErr) { + err := updateAttributeViewColRelation(operation) + if nil != err { + return &TxErr{code: TxErrWriteAttributeView, id: operation.AvID, msg: err.Error()} + } + return +} + +func updateAttributeViewColRelation(operation *Operation) (err error) { + err = updateAttributeViewColRelation0(operation.AvID, operation.KeyID, operation.ID, operation.IsBiRelation, operation.BackRelationKeyID) + if nil != err { + return + } + + if operation.IsBiRelation { + err = updateAttributeViewColRelation0(operation.ID, operation.BackRelationKeyID, operation.AvID, operation.IsBiRelation, operation.KeyID) + } + return +} + +func updateAttributeViewColRelation0(avID, relKeyID, destAvID string, isBiRel bool, backRelKeyID string) (err error) { + attrView, err := av.ParseAttributeView(avID) + if nil != err { + return + } + + for _, keyValues := range attrView.KeyValues { + if keyValues.Key.ID == relKeyID { + keyValues.Key.RelationAvID = destAvID + keyValues.Key.IsBiRelation = isBiRel + keyValues.Key.BackRelationKeyID = backRelKeyID + err = av.SaveAttributeView(attrView) + return + } + } + return +} + func (tx *Transaction) doSortAttrViewView(operation *Operation) (ret *TxErr) { avID := operation.AvID attrView, err := av.ParseAttributeView(avID) diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go index 352d1b03e..3647eb77d 100644 --- a/kernel/model/transaction.go +++ b/kernel/model/transaction.go @@ -258,6 +258,8 @@ func performTx(tx *Transaction) (ret *TxErr) { ret = tx.doDuplicateAttrViewView(op) case "sortAttrViewView": ret = tx.doSortAttrViewView(op) + case "updateAttrViewColRelation": + ret = tx.doUpdateAttrViewColRelation(op) } if nil != ret { @@ -1181,14 +1183,16 @@ type Operation struct { DeckID string `json:"deckID"` // 用于添加/删除闪卡 - AvID string `json:"avID"` // 属性视图 ID - SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中 - IsDetached bool `json:"isDetached"` // 用于标识是否是脱离块,仅存在于属性视图中 - Name string `json:"name"` // 属性视图列名 - Typ string `json:"type"` // 属性视图列类型 - Format string `json:"format"` // 属性视图列格式化 - KeyID string `json:"keyID"` // 属性视列 ID - RowID string `json:"rowID"` // 属性视图行 ID + AvID string `json:"avID"` // 属性视图 ID + SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中 + IsDetached bool `json:"isDetached"` // 用于标识是否是脱离块,仅存在于属性视图中 + Name string `json:"name"` // 属性视图列名 + Typ string `json:"type"` // 属性视图列类型 + Format string `json:"format"` // 属性视图列格式化 + KeyID string `json:"keyID"` // 属性视列 ID + RowID string `json:"rowID"` // 属性视图行 ID + IsBiRelation bool `json:"isBiRelation"` // 属性视图关联列是否是双向关系 + BackRelationKeyID string `json:"backRelationKeyID"` // 属性视图关联列回链关联列的 ID } type Transaction struct { From 62e92c0e6bbe0544ba3cd6af83dbf1d24ae6d98a Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 23 Dec 2023 12:04:54 +0800 Subject: [PATCH 2/5] :art: Add Relation and Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/api/av.go | 29 +++++++++++++++++ kernel/api/router.go | 1 + kernel/model/attribute_view.go | 57 ++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/kernel/api/av.go b/kernel/api/av.go index c3959bf7f..8207c9724 100644 --- a/kernel/api/av.go +++ b/kernel/api/av.go @@ -26,6 +26,35 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func searchAttributeView(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, _ := util.JsonArg(c, ret) + if nil == arg { + return + } + + keyword := arg["keyword"].(string) + page := 1 + pageArg := arg["page"] + if nil != pageArg { + page = int(pageArg.(float64)) + } + + pageSize := 10 + pageSizeArg := arg["pageSize"] + if nil != pageSizeArg { + pageSize = int(pageSizeArg.(float64)) + } + + results, total := model.SearchAttributeView(keyword, page, pageSize) + ret.Data = map[string]interface{}{ + "results": results, + "total": total, + } +} + func renderSnapshotAttributeView(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/api/router.go b/kernel/api/router.go index 49138e216..910dcf2bb 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -386,6 +386,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/av/renderSnapshotAttributeView", model.CheckAuth, renderSnapshotAttributeView) ginServer.Handle("POST", "/api/av/getAttributeViewKeys", model.CheckAuth, getAttributeViewKeys) ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, model.CheckReadonly, setAttributeViewBlockAttr) + ginServer.Handle("POST", "/api/av/searchAttributeView", model.CheckAuth, model.CheckReadonly, searchAttributeView) ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT) ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 0c143f844..d077513f4 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -37,6 +37,63 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +type SearchAttributeViewResult struct { + AvID string `json:"avID"` + AvName string `json:"avName"` + BlockID string `json:"blockID"` +} + +func SearchAttributeView(keyword string, page int, pageSize int) (ret []*SearchAttributeViewResult, pageCount int) { + waitForSyncingStorages() + + ret = []*SearchAttributeViewResult{} + blocks, _, _, pageCount := FullTextSearchBlock(keyword, nil, nil, map[string]bool{"databaseBlock": true}, 0, 7, 0, page, pageSize) + trees := map[string]*parse.Tree{} + for _, block := range blocks { + tree := trees[block.RootID] + if nil == tree { + tree, _ = loadTreeByBlockID(block.ID) + if nil != tree { + trees[block.RootID] = tree + } + } + if nil == tree { + continue + } + + node := treenode.GetNodeInTree(tree, block.ID) + if nil == node { + continue + } + + if "" == node.AttributeViewID { + continue + } + + avID := node.AttributeViewID + attrView, _ := av.ParseAttributeView(avID) + if nil == attrView { + continue + } + + exist := false + for _, result := range ret { + if result.AvID == avID { + exist = true + break + } + } + if !exist { + ret = append(ret, &SearchAttributeViewResult{ + AvID: avID, + AvName: attrView.Name, + BlockID: block.ID, + }) + } + } + return +} + type BlockAttributeViewKeys struct { AvID string `json:"avID"` AvName string `json:"avName"` From c916cd0c231582d7627dadf68bb4791fb9f043e3 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 23 Dec 2023 17:17:47 +0800 Subject: [PATCH 3/5] :art: Add Relation and Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/api/av.go | 16 ++++++++++++++++ kernel/api/router.go | 1 + kernel/model/attribute_view.go | 13 ++++++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/kernel/api/av.go b/kernel/api/av.go index 8207c9724..c53b247ef 100644 --- a/kernel/api/av.go +++ b/kernel/api/av.go @@ -26,6 +26,22 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func getAttributeView(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, _ := util.JsonArg(c, ret) + if nil == arg { + return + } + + id := arg["id"].(string) + av := model.GetAttributeView(id) + ret.Data = map[string]interface{}{ + "av": av, + } +} + func searchAttributeView(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/api/router.go b/kernel/api/router.go index 910dcf2bb..e3b5ff7df 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -387,6 +387,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/av/getAttributeViewKeys", model.CheckAuth, getAttributeViewKeys) ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, model.CheckReadonly, setAttributeViewBlockAttr) ginServer.Handle("POST", "/api/av/searchAttributeView", model.CheckAuth, model.CheckReadonly, searchAttributeView) + ginServer.Handle("POST", "/api/av/getAttributeView", model.CheckAuth, model.CheckReadonly, getAttributeView) ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT) ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index d077513f4..d9f05c255 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -37,6 +37,13 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func GetAttributeView(avID string) (ret *av.AttributeView) { + waitForSyncingStorages() + + ret, _ = av.ParseAttributeView(avID) + return +} + type SearchAttributeViewResult struct { AvID string `json:"avID"` AvName string `json:"avName"` @@ -721,18 +728,18 @@ func (tx *Transaction) doUpdateAttrViewColRelation(operation *Operation) (ret *T } func updateAttributeViewColRelation(operation *Operation) (err error) { - err = updateAttributeViewColRelation0(operation.AvID, operation.KeyID, operation.ID, operation.IsBiRelation, operation.BackRelationKeyID) + err = updateAttributeViewColRelation0(operation.AvID, operation.KeyID, operation.ID, operation.IsBiRelation, operation.BackRelationKeyID, operation.Name) if nil != err { return } if operation.IsBiRelation { - err = updateAttributeViewColRelation0(operation.ID, operation.BackRelationKeyID, operation.AvID, operation.IsBiRelation, operation.KeyID) + err = updateAttributeViewColRelation0(operation.ID, operation.BackRelationKeyID, operation.AvID, operation.IsBiRelation, operation.KeyID, operation.Name) } return } -func updateAttributeViewColRelation0(avID, relKeyID, destAvID string, isBiRel bool, backRelKeyID string) (err error) { +func updateAttributeViewColRelation0(avID, relKeyID, destAvID string, isBiRel bool, backRelKeyID, backRelKeyName string) (err error) { attrView, err := av.ParseAttributeView(avID) if nil != err { return From 46c82acf6cd0d3eee3a18db6d5fb6b4bafe6f7e2 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 23 Dec 2023 17:45:46 +0800 Subject: [PATCH 4/5] :art: Add Relation and Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/av/av.go | 16 ++++++-- kernel/av/table.go | 8 ++-- kernel/model/attribute_view.go | 68 ++++++++++++++++++++++++++-------- kernel/model/transaction.go | 2 +- kernel/treenode/node.go | 2 + 5 files changed, 72 insertions(+), 24 deletions(-) diff --git a/kernel/av/av.go b/kernel/av/av.go index 0767c850c..7f793f272 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -89,12 +89,10 @@ type Key struct { Template string `json:"template"` // 模板内容 // 关联列 - RelationAvID string `json:"relationAvID"` // 关联的属性视图 ID - IsBiRelation bool `json:"isBiRelation"` // 是否双向关联 - BackRelationKeyID string `json:"backRelationKeyID"` // 双向关联时回链关联列的 ID + Relation *Relation `json:"relation,omitempty"` // 关联信息 // 汇总列 - RollupKeyID string `json:"rollupKeyID"` // 汇总列 ID + Rollup *Rollup `json:"rollup,omitempty"` // 汇总信息 } func NewKey(id, name, icon string, keyType KeyType) *Key { @@ -106,6 +104,16 @@ func NewKey(id, name, icon string, keyType KeyType) *Key { } } +type Rollup struct { + KeyID string `json:"keyID"` // 汇总列 ID +} + +type Relation struct { + AvID string `json:"avID"` // 关联的属性视图 ID + IsTwoWay bool `json:"isTwoWay"` // 是否双向关联 + BackKeyID string `json:"backKeyID"` // 双向关联时回链关联列的 ID +} + type KeySelectOption struct { Name string `json:"name"` Color string `json:"color"` diff --git a/kernel/av/table.go b/kernel/av/table.go index 50f47e4d6..71dd16cfd 100644 --- a/kernel/av/table.go +++ b/kernel/av/table.go @@ -651,9 +651,11 @@ type TableColumn struct { // 以下是某些列类型的特有属性 - Options []*KeySelectOption `json:"options,omitempty"` // 选项列表 - NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化 - Template string `json:"template"` // 模板内容 + Options []*KeySelectOption `json:"options,omitempty"` // 选项列表 + NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化 + Template string `json:"template"` // 模板内容 + Relation *Relation `json:"relation,omitempty"` // 关联列 + Rollup *Rollup `json:"rollup,omitempty"` // 汇总列 } type TableCell struct { diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index d9f05c255..648fc9066 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -553,6 +553,8 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a Options: key.Options, NumberFormat: key.NumberFormat, Template: key.Template, + Relation: key.Relation, + Rollup: key.Rollup, Wrap: col.Wrap, Hidden: col.Hidden, Width: col.Width, @@ -728,32 +730,66 @@ func (tx *Transaction) doUpdateAttrViewColRelation(operation *Operation) (ret *T } func updateAttributeViewColRelation(operation *Operation) (err error) { - err = updateAttributeViewColRelation0(operation.AvID, operation.KeyID, operation.ID, operation.IsBiRelation, operation.BackRelationKeyID, operation.Name) + // operation.AvID 源 avID + // operation.ID 目标 avID + // operation.KeyID 源 av 关联列 ID + // operation.IsTwoWay 是否双向关联 + // operation.BackRelationKeyID 双向关联的目标关联列 ID + // operation.Name 双向关联的目标关联列名称 + + srcAv, err := av.ParseAttributeView(operation.AvID) if nil != err { return } - if operation.IsBiRelation { - err = updateAttributeViewColRelation0(operation.ID, operation.BackRelationKeyID, operation.AvID, operation.IsBiRelation, operation.KeyID, operation.Name) - } - return -} - -func updateAttributeViewColRelation0(avID, relKeyID, destAvID string, isBiRel bool, backRelKeyID, backRelKeyName string) (err error) { - attrView, err := av.ParseAttributeView(avID) + destAv, err := av.ParseAttributeView(operation.ID) if nil != err { return } - for _, keyValues := range attrView.KeyValues { - if keyValues.Key.ID == relKeyID { - keyValues.Key.RelationAvID = destAvID - keyValues.Key.IsBiRelation = isBiRel - keyValues.Key.BackRelationKeyID = backRelKeyID - err = av.SaveAttributeView(attrView) - return + isSameAv := srcAv.ID == destAv.ID + + for _, keyValues := range srcAv.KeyValues { + if keyValues.Key.ID == operation.KeyID { + keyValues.Key.Relation = &av.Relation{ + AvID: operation.ID, + IsTwoWay: operation.IsTwoWay, + BackKeyID: operation.BackRelationKeyID, + } + break } } + + destAdded := false + for _, keyValues := range destAv.KeyValues { + if keyValues.Key.ID == operation.BackRelationKeyID { + keyValues.Key.Relation = &av.Relation{ + AvID: operation.AvID, + IsTwoWay: operation.IsTwoWay, + BackKeyID: operation.KeyID, + } + destAdded = true + break + } + } + if !destAdded { + destAv.KeyValues = append(destAv.KeyValues, &av.KeyValues{ + Key: &av.Key{ + ID: operation.BackRelationKeyID, + Name: operation.Name, + Type: av.KeyTypeRelation, + Relation: &av.Relation{AvID: operation.AvID, IsTwoWay: operation.IsTwoWay, BackKeyID: operation.KeyID}, + }, + }) + } + + err = av.SaveAttributeView(srcAv) + if nil != err { + return + } + if !isSameAv { + err = av.SaveAttributeView(destAv) + } return } diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go index 3647eb77d..35153b835 100644 --- a/kernel/model/transaction.go +++ b/kernel/model/transaction.go @@ -1191,7 +1191,7 @@ type Operation struct { Format string `json:"format"` // 属性视图列格式化 KeyID string `json:"keyID"` // 属性视列 ID RowID string `json:"rowID"` // 属性视图行 ID - IsBiRelation bool `json:"isBiRelation"` // 属性视图关联列是否是双向关系 + IsTwoWay bool `json:"isTwoWay"` // 属性视图关联列是否是双向关系 BackRelationKeyID string `json:"backRelationKeyID"` // 属性视图关联列回链关联列的 ID } diff --git a/kernel/treenode/node.go b/kernel/treenode/node.go index 1e07f7d86..5bca7a517 100644 --- a/kernel/treenode/node.go +++ b/kernel/treenode/node.go @@ -614,6 +614,8 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a Options: key.Options, NumberFormat: key.NumberFormat, Template: key.Template, + Relation: key.Relation, + Rollup: key.Rollup, Wrap: col.Wrap, Hidden: col.Hidden, Width: col.Width, From de73ca8c3b858fc6138597bc099c8f3cee33baca Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 23 Dec 2023 17:57:45 +0800 Subject: [PATCH 5/5] :art: Add Relation and Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/av/av.go | 4 ++-- kernel/av/table.go | 10 +++++----- kernel/model/attribute_view.go | 26 +++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/kernel/av/av.go b/kernel/av/av.go index 7f793f272..261d36f6f 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -80,7 +80,7 @@ type Key struct { // 以下是某些列类型的特有属性 // 单选/多选列 - Options []*KeySelectOption `json:"options,omitempty"` // 选项列表 + Options []*SelectOption `json:"options,omitempty"` // 选项列表 // 数字列 NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化 @@ -114,7 +114,7 @@ type Relation struct { BackKeyID string `json:"backKeyID"` // 双向关联时回链关联列的 ID } -type KeySelectOption struct { +type SelectOption struct { Name string `json:"name"` Color string `json:"color"` } diff --git a/kernel/av/table.go b/kernel/av/table.go index 71dd16cfd..d2bb32665 100644 --- a/kernel/av/table.go +++ b/kernel/av/table.go @@ -651,11 +651,11 @@ type TableColumn struct { // 以下是某些列类型的特有属性 - Options []*KeySelectOption `json:"options,omitempty"` // 选项列表 - NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化 - Template string `json:"template"` // 模板内容 - Relation *Relation `json:"relation,omitempty"` // 关联列 - Rollup *Rollup `json:"rollup,omitempty"` // 汇总列 + Options []*SelectOption `json:"options,omitempty"` // 选项列表 + NumberFormat NumberFormat `json:"numberFormat"` // 列数字格式化 + Template string `json:"template"` // 模板内容 + Relation *Relation `json:"relation,omitempty"` // 关联列 + Rollup *Rollup `json:"rollup,omitempty"` // 汇总列 } type TableCell struct { diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 648fc9066..8df88ef34 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -751,6 +751,30 @@ func updateAttributeViewColRelation(operation *Operation) (err error) { for _, keyValues := range srcAv.KeyValues { if keyValues.Key.ID == operation.KeyID { + // 已经设置过双向关联的话需要先断开双向关联 + if nil != keyValues.Key.Relation && keyValues.Key.Relation.IsTwoWay { + oldDestAv, parseErr := av.ParseAttributeView(keyValues.Key.Relation.AvID) + if nil == parseErr { + isOldSameAv := oldDestAv.ID == destAv.ID + if isOldSameAv { + oldDestAv = destAv + } + + oldDestKey, _ := oldDestAv.GetKey(keyValues.Key.Relation.BackKeyID) + if nil != oldDestKey && nil != oldDestKey.Relation && oldDestKey.Relation.AvID == srcAv.ID && oldDestKey.Relation.IsTwoWay { + oldDestKey.Relation.IsTwoWay = false + oldDestKey.Relation.BackKeyID = "" + } + + if !isOldSameAv { + err = av.SaveAttributeView(oldDestAv) + if nil != err { + return + } + } + } + } + keyValues.Key.Relation = &av.Relation{ AvID: operation.ID, IsTwoWay: operation.IsTwoWay, @@ -2086,7 +2110,7 @@ func updateAttributeViewColumnOptions(operation *Operation) (err error) { return } - options := []*av.KeySelectOption{} + options := []*av.SelectOption{} if err = gulu.JSON.UnmarshalJSON(jsonData, &options); nil != err { return }