From 8bf3efb355c87f812f25d0aa7b9099132d0f0777 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 30 Dec 2023 22:36:23 +0800 Subject: [PATCH 1/6] :art: Add Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9958 --- kernel/model/attribute_view.go | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 31f9cbb3d..0549b3985 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -806,6 +806,57 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.String()) } + + if nil != rollupKey.Rollup.Calc { + switch rollupKey.Rollup.Calc.Operator { + case av.CalcOperatorCountAll: + cell.Value.Rollup.Contents = []string{strconv.Itoa(len(cell.Value.Rollup.Contents))} + case av.CalcOperatorCountValues: + cell.Value.Rollup.Contents = []string{strconv.Itoa(len(cell.Value.Rollup.Contents))} + case av.CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, v := range cell.Value.Rollup.Contents { + if !uniqueValues[v] { + uniqueValues[v] = true + countUniqueValues++ + } + } + cell.Value.Rollup.Contents = []string{strconv.Itoa(countUniqueValues)} + case av.CalcOperatorCountEmpty: + countEmpty := 0 + for _, v := range cell.Value.Rollup.Contents { + if "" == v { + countEmpty++ + } + } + cell.Value.Rollup.Contents = []string{strconv.Itoa(countEmpty)} + case av.CalcOperatorCountNotEmpty: + countNonEmpty := 0 + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + countNonEmpty++ + } + } + cell.Value.Rollup.Contents = []string{strconv.Itoa(countNonEmpty)} + case av.CalcOperatorPercentEmpty: + countEmpty := 0 + for _, v := range cell.Value.Rollup.Contents { + if "" == v { + countEmpty++ + } + } + cell.Value.Rollup.Contents = []string{strconv.Itoa(countEmpty*100/len(cell.Value.Rollup.Contents)) + "%"} + case av.CalcOperatorPercentNotEmpty: + countNonEmpty := 0 + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + countNonEmpty++ + } + } + cell.Value.Rollup.Contents = []string{strconv.Itoa(countNonEmpty*100/len(cell.Value.Rollup.Contents)) + "%"} + } + } case av.KeyTypeRelation: // 渲染关联列 relKey, _ := attrView.GetKey(cell.Value.KeyID) if nil != relKey && nil != relKey.Relation { From 6a8602c1cd47347f4511fa9cf90911151fc0558d Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 30 Dec 2023 23:01:21 +0800 Subject: [PATCH 2/6] :art: Add Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9958 --- kernel/av/filter.go | 2 +- kernel/av/table.go | 46 +++++++++++++++++++++++++++++----- kernel/model/attribute_view.go | 4 +-- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/kernel/av/filter.go b/kernel/av/filter.go index 50cc15713..8dd8c137e 100644 --- a/kernel/av/filter.go +++ b/kernel/av/filter.go @@ -22,7 +22,7 @@ import ( ) type Filterable interface { - FilterRows() + FilterRows(attrView *AttributeView) } type ViewFilter struct { diff --git a/kernel/av/table.go b/kernel/av/table.go index 57ea9a2a4..3928ac4da 100644 --- a/kernel/av/table.go +++ b/kernel/av/table.go @@ -230,7 +230,44 @@ func (value *Value) Compare(other *Value) int { return 0 } -func (value *Value) CompareOperator(other *Value, operator FilterOperator) bool { +func (value *Value) CompareOperator(other *Value, operator FilterOperator, attrView *AttributeView, rowID string) bool { + if nil != value.Rollup && nil != other.Rollup { + rollupKey, _ := attrView.GetKey(value.KeyID) + if nil == rollupKey { + return false + } + relKey, _ := attrView.GetKey(rollupKey.Rollup.RelationKeyID) + if nil == relKey { + return false + } + + relVal := attrView.GetValue(relKey.ID, rowID) + if nil == relVal || nil == relVal.Relation { + return false + } + + destAv, _ := ParseAttributeView(relKey.Relation.AvID) + if nil == destAv { + return false + } + + for _, blockID := range relVal.Relation.BlockIDs { + destVal := destAv.GetValue(rollupKey.Rollup.KeyID, blockID) + if nil == destVal { + continue + } + + if destVal.compareOperator(other, operator, attrView) { + return true + } + } + return false + } + + return value.compareOperator(other, operator, attrView) +} + +func (value *Value) compareOperator(other *Value, operator FilterOperator, attrView *AttributeView) bool { if nil == other { return true } @@ -639,9 +676,6 @@ func (value *Value) CompareOperator(other *Value, operator FilterOperator) bool } } - if nil != value.Rollup && nil != other.Rollup { - // TODO: rollup filter - } return false } @@ -745,7 +779,7 @@ func (table *Table) SortRows() { }) } -func (table *Table) FilterRows() { +func (table *Table) FilterRows(attrView *AttributeView) { if 1 > len(table.Filters) { return } @@ -780,7 +814,7 @@ func (table *Table) FilterRows() { break } - if !row.Cells[index].Value.CompareOperator(table.Filters[j].Value, operator) { + if !row.Cells[index].Value.CompareOperator(table.Filters[j].Value, operator, attrView, row.ID) { pass = false break } diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 0549b3985..1d1cfb033 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -547,7 +547,7 @@ func renderAttributeView(attrView *av.AttributeView, viewID string, page, pageSi viewable, err = renderAttributeViewTable(attrView, view) } - viewable.FilterRows() + viewable.FilterRows(attrView) viewable.SortRows() viewable.CalcCols() @@ -1566,7 +1566,7 @@ func addAttributeViewBlock(blockID string, operation *Operation, tree *parse.Tre view, _ := attrView.GetCurrentView() if nil != view && 0 < len(view.Table.Filters) { viewable, _ := renderAttributeViewTable(attrView, view) - viewable.FilterRows() + viewable.FilterRows(attrView) viewable.SortRows() addedVal := false From 9d1d7611336494340d7ec6793f074498f07389d8 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 30 Dec 2023 23:08:45 +0800 Subject: [PATCH 3/6] :art: Add Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9958 --- kernel/model/attribute_view.go | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 1d1cfb033..609c711df 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -18,6 +18,7 @@ package model import ( "bytes" + "math" "os" "path/filepath" "sort" @@ -855,6 +856,79 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a } } cell.Value.Rollup.Contents = []string{strconv.Itoa(countNonEmpty*100/len(cell.Value.Rollup.Contents)) + "%"} + case av.CalcOperatorSum: + sum := 0.0 + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + sum += n + } + } + cell.Value.Rollup.Contents = []string{strconv.FormatFloat(sum, 'f', -1, 64)} + case av.CalcOperatorAverage: + sum := 0.0 + count := 0 + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + sum += n + count++ + } + } + cell.Value.Rollup.Contents = []string{strconv.FormatFloat(sum/float64(count), 'f', -1, 64)} + case av.CalcOperatorMedian: + numbers := []float64{} + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + numbers = append(numbers, n) + } + } + sort.Float64s(numbers) + if 0 < len(numbers) { + if 0 == len(numbers)%2 { + cell.Value.Rollup.Contents = []string{strconv.FormatFloat((numbers[len(numbers)/2-1]+numbers[len(numbers)/2])/2, 'f', -1, 64)} + } else { + cell.Value.Rollup.Contents = []string{strconv.FormatFloat(numbers[len(numbers)/2], 'f', -1, 64)} + } + } + case av.CalcOperatorMin: + min := math.MaxFloat64 + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + if n < min { + min = n + } + } + } + cell.Value.Rollup.Contents = []string{strconv.FormatFloat(min, 'f', -1, 64)} + case av.CalcOperatorMax: + max := -math.MaxFloat64 + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + if n > max { + max = n + } + } + } + cell.Value.Rollup.Contents = []string{strconv.FormatFloat(max, 'f', -1, 64)} + case av.CalcOperatorRange: + min := math.MaxFloat64 + max := -math.MaxFloat64 + for _, v := range cell.Value.Rollup.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + if n < min { + min = n + } + if n > max { + max = n + } + } + } + cell.Value.Rollup.Contents = []string{strconv.FormatFloat(min, 'f', -1, 64) + "~" + strconv.FormatFloat(max, 'f', -1, 64)} } } case av.KeyTypeRelation: // 渲染关联列 From 454d3d0b9391b62ca18e57e3e6e2dcbaeb864f97 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 30 Dec 2023 23:13:01 +0800 Subject: [PATCH 4/6] :art: Add Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9958 --- kernel/model/attribute_view.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 609c711df..453f1b11d 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -928,7 +928,7 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a } } } - cell.Value.Rollup.Contents = []string{strconv.FormatFloat(min, 'f', -1, 64) + "~" + strconv.FormatFloat(max, 'f', -1, 64)} + cell.Value.Rollup.Contents = []string{strconv.FormatFloat(max-min, 'f', -1, 64)} } } case av.KeyTypeRelation: // 渲染关联列 From 59599472ba86c1fc516710077e6f09c4c5522c2e Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 30 Dec 2023 23:34:53 +0800 Subject: [PATCH 5/6] :art: Add Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9958 --- kernel/av/value.go | 130 +++++++++++++++++++++++++++++ kernel/model/attribute_view.go | 148 ++++----------------------------- 2 files changed, 147 insertions(+), 131 deletions(-) diff --git a/kernel/av/value.go b/kernel/av/value.go index 4c0c6bddd..46ab870f9 100644 --- a/kernel/av/value.go +++ b/kernel/av/value.go @@ -19,6 +19,7 @@ package av import ( "fmt" "math" + "sort" "strconv" "strings" "time" @@ -469,3 +470,132 @@ type ValueRelation struct { type ValueRollup struct { Contents []string `json:"contents"` } + +func (r *ValueRollup) RenderContents(calc *RollupCalc) { + if nil == calc { + return + } + + switch calc.Operator { + case CalcOperatorNone: + case CalcOperatorCountAll: + r.Contents = []string{strconv.Itoa(len(r.Contents))} + case CalcOperatorCountValues: + r.Contents = []string{strconv.Itoa(len(r.Contents))} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, v := range r.Contents { + if !uniqueValues[v] { + uniqueValues[v] = true + countUniqueValues++ + } + } + r.Contents = []string{strconv.Itoa(countUniqueValues)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, v := range r.Contents { + if "" == v { + countEmpty++ + } + } + r.Contents = []string{strconv.Itoa(countEmpty)} + case CalcOperatorCountNotEmpty: + countNonEmpty := 0 + for _, v := range r.Contents { + if "" != v { + countNonEmpty++ + } + } + r.Contents = []string{strconv.Itoa(countNonEmpty)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, v := range r.Contents { + if "" == v { + countEmpty++ + } + } + r.Contents = []string{strconv.Itoa(countEmpty*100/len(r.Contents)) + "%"} + case CalcOperatorPercentNotEmpty: + countNonEmpty := 0 + for _, v := range r.Contents { + if "" != v { + countNonEmpty++ + } + } + r.Contents = []string{strconv.Itoa(countNonEmpty*100/len(r.Contents)) + "%"} + case CalcOperatorSum: + sum := 0.0 + for _, v := range r.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + sum += n + } + } + r.Contents = []string{strconv.FormatFloat(sum, 'f', -1, 64)} + case CalcOperatorAverage: + sum := 0.0 + count := 0 + for _, v := range r.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + sum += n + count++ + } + } + r.Contents = []string{strconv.FormatFloat(sum/float64(count), 'f', -1, 64)} + case CalcOperatorMedian: + numbers := []float64{} + for _, v := range r.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + numbers = append(numbers, n) + } + } + sort.Float64s(numbers) + if 0 < len(numbers) { + if 0 == len(numbers)%2 { + r.Contents = []string{strconv.FormatFloat((numbers[len(numbers)/2-1]+numbers[len(numbers)/2])/2, 'f', -1, 64)} + } else { + r.Contents = []string{strconv.FormatFloat(numbers[len(numbers)/2], 'f', -1, 64)} + } + } + case CalcOperatorMin: + min := math.MaxFloat64 + for _, v := range r.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + if n < min { + min = n + } + } + } + r.Contents = []string{strconv.FormatFloat(min, 'f', -1, 64)} + case CalcOperatorMax: + max := -math.MaxFloat64 + for _, v := range r.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + if n > max { + max = n + } + } + } + r.Contents = []string{strconv.FormatFloat(max, 'f', -1, 64)} + case CalcOperatorRange: + min := math.MaxFloat64 + max := -math.MaxFloat64 + for _, v := range r.Contents { + if "" != v { + n, _ := strconv.ParseFloat(v, 64) + if n < min { + min = n + } + if n > max { + max = n + } + } + } + r.Contents = []string{strconv.FormatFloat(max-min, 'f', -1, 64)} + } +} diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 453f1b11d..29f3aca01 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -18,7 +18,6 @@ package model import ( "bytes" - "math" "os" "path/filepath" "sort" @@ -212,6 +211,8 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { } switch kValues.Key.Type { + case av.KeyTypeRollup: + kValues.Values = append(kValues.Values, &av.Value{ID: ast.NewNodeID(), KeyID: kValues.Key.ID, BlockID: blockID, Type: av.KeyTypeRollup, Rollup: &av.ValueRollup{Contents: []string{}}}) case av.KeyTypeTemplate: kValues.Values = append(kValues.Values, &av.Value{ID: ast.NewNodeID(), KeyID: kValues.Key.ID, BlockID: blockID, Type: av.KeyTypeTemplate, Template: &av.ValueTemplate{Content: ""}}) case av.KeyTypeCreated: @@ -239,16 +240,23 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { break } - var blockIDs []string relVal := attrView.GetValue(kv.Key.Rollup.RelationKeyID, kv.Values[0].BlockID) if nil != relVal && nil != relVal.Relation { - blockIDs = relVal.Relation.BlockIDs - } + destAv, _ := av.ParseAttributeView(relKey.Relation.AvID) + if nil != destAv { + for _, bID := range relVal.Relation.BlockIDs { + destVal := destAv.GetValue(kv.Key.Rollup.KeyID, bID) + if nil != destVal { + if av.KeyTypeNumber == destVal.Type { + destVal.Number.Format = kv.Key.NumberFormat + destVal.Number.FormatNumber() + } - destAv, _ := av.ParseAttributeView(relKey.Relation.AvID) - if nil != destAv { - for _, bID := range blockIDs { - kv.Values[0].Rollup.Contents = append(kv.Values[0].Rollup.Contents, destAv.GetValue(kv.Key.Rollup.KeyID, bID).String()) + kv.Values[0].Rollup.Contents = append(kv.Values[0].Rollup.Contents, destAv.GetValue(kv.Key.Rollup.KeyID, bID).String()) + } + + kv.Values[0].Rollup.RenderContents(kv.Key.Rollup.Calc) + } } } case av.KeyTypeRelation: @@ -808,129 +816,7 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.String()) } - if nil != rollupKey.Rollup.Calc { - switch rollupKey.Rollup.Calc.Operator { - case av.CalcOperatorCountAll: - cell.Value.Rollup.Contents = []string{strconv.Itoa(len(cell.Value.Rollup.Contents))} - case av.CalcOperatorCountValues: - cell.Value.Rollup.Contents = []string{strconv.Itoa(len(cell.Value.Rollup.Contents))} - case av.CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, v := range cell.Value.Rollup.Contents { - if !uniqueValues[v] { - uniqueValues[v] = true - countUniqueValues++ - } - } - cell.Value.Rollup.Contents = []string{strconv.Itoa(countUniqueValues)} - case av.CalcOperatorCountEmpty: - countEmpty := 0 - for _, v := range cell.Value.Rollup.Contents { - if "" == v { - countEmpty++ - } - } - cell.Value.Rollup.Contents = []string{strconv.Itoa(countEmpty)} - case av.CalcOperatorCountNotEmpty: - countNonEmpty := 0 - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - countNonEmpty++ - } - } - cell.Value.Rollup.Contents = []string{strconv.Itoa(countNonEmpty)} - case av.CalcOperatorPercentEmpty: - countEmpty := 0 - for _, v := range cell.Value.Rollup.Contents { - if "" == v { - countEmpty++ - } - } - cell.Value.Rollup.Contents = []string{strconv.Itoa(countEmpty*100/len(cell.Value.Rollup.Contents)) + "%"} - case av.CalcOperatorPercentNotEmpty: - countNonEmpty := 0 - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - countNonEmpty++ - } - } - cell.Value.Rollup.Contents = []string{strconv.Itoa(countNonEmpty*100/len(cell.Value.Rollup.Contents)) + "%"} - case av.CalcOperatorSum: - sum := 0.0 - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) - sum += n - } - } - cell.Value.Rollup.Contents = []string{strconv.FormatFloat(sum, 'f', -1, 64)} - case av.CalcOperatorAverage: - sum := 0.0 - count := 0 - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) - sum += n - count++ - } - } - cell.Value.Rollup.Contents = []string{strconv.FormatFloat(sum/float64(count), 'f', -1, 64)} - case av.CalcOperatorMedian: - numbers := []float64{} - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) - numbers = append(numbers, n) - } - } - sort.Float64s(numbers) - if 0 < len(numbers) { - if 0 == len(numbers)%2 { - cell.Value.Rollup.Contents = []string{strconv.FormatFloat((numbers[len(numbers)/2-1]+numbers[len(numbers)/2])/2, 'f', -1, 64)} - } else { - cell.Value.Rollup.Contents = []string{strconv.FormatFloat(numbers[len(numbers)/2], 'f', -1, 64)} - } - } - case av.CalcOperatorMin: - min := math.MaxFloat64 - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) - if n < min { - min = n - } - } - } - cell.Value.Rollup.Contents = []string{strconv.FormatFloat(min, 'f', -1, 64)} - case av.CalcOperatorMax: - max := -math.MaxFloat64 - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) - if n > max { - max = n - } - } - } - cell.Value.Rollup.Contents = []string{strconv.FormatFloat(max, 'f', -1, 64)} - case av.CalcOperatorRange: - min := math.MaxFloat64 - max := -math.MaxFloat64 - for _, v := range cell.Value.Rollup.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) - if n < min { - min = n - } - if n > max { - max = n - } - } - } - cell.Value.Rollup.Contents = []string{strconv.FormatFloat(max-min, 'f', -1, 64)} - } - } + cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc) case av.KeyTypeRelation: // 渲染关联列 relKey, _ := attrView.GetKey(cell.Value.KeyID) if nil != relKey && nil != relKey.Relation { From 44f99c8fb63c31303422059921d343ba254e99bf Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sat, 30 Dec 2023 23:35:43 +0800 Subject: [PATCH 6/6] :art: Add Rollup column to database table view https://github.com/siyuan-note/siyuan/issues/9958 --- kernel/treenode/node.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/treenode/node.go b/kernel/treenode/node.go index bbf792455..ee906e6f2 100644 --- a/kernel/treenode/node.go +++ b/kernel/treenode/node.go @@ -757,8 +757,11 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a destVal.Number.Format = rollupKey.NumberFormat destVal.Number.FormatNumber() } + cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.String()) } + + cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc) case av.KeyTypeRelation: // 渲染关联列 relKey, _ := attrView.GetKey(cell.Value.KeyID) if nil != relKey && nil != relKey.Relation {