From 1e28680a58b4399f16351fb0599a14c486e4686d Mon Sep 17 00:00:00 2001
From: Daniel <845765@qq.com>
Date: Wed, 20 Nov 2024 20:20:41 +0800
Subject: [PATCH 1/4] :art: The database supports calculating the "Percent
unique values" of the field
https://github.com/siyuan-note/siyuan/issues/13192
---
app/appearance/langs/de_DE.json | 2 +
app/appearance/langs/en_US.json | 2 +
app/appearance/langs/es_ES.json | 2 +
app/appearance/langs/fr_FR.json | 2 +
app/appearance/langs/he_IL.json | 2 +
app/appearance/langs/it_IT.json | 2 +
app/appearance/langs/ja_JP.json | 2 +
app/appearance/langs/pl_PL.json | 2 +
app/appearance/langs/ru_RU.json | 2 +
app/appearance/langs/zh_CHT.json | 2 +
app/appearance/langs/zh_CN.json | 2 +
app/src/protyle/render/av/calc.ts | 16 +++
kernel/av/calc.go | 41 +++---
kernel/av/table_calc.go | 218 ++++++++++++++++++++++++++++++
kernel/av/value.go | 12 ++
15 files changed, 289 insertions(+), 20 deletions(-)
diff --git a/app/appearance/langs/de_DE.json b/app/appearance/langs/de_DE.json
index 2d5ad6ae2..be4f6f99a 100644
--- a/app/appearance/langs/de_DE.json
+++ b/app/appearance/langs/de_DE.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "NICHT LEER",
"calcResultPercentEmpty": "LEER",
"calcResultPercentNotEmpty": "NICHT LEER",
+ "calcResultPercentUniqueValues": "Prozent einzigartige Werte",
"calcResultSum": "SUMME",
"calcResultAverage": "DURCHSCHNITT",
"calcResultMedian": "MEDIAN",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "Nicht leer zählen",
"calcOperatorPercentEmpty": "Prozent leer",
"calcOperatorPercentNotEmpty": "Prozent nicht leer",
+ "calcOperatorPercentUniqueValues": "Prozent einzigartige Werte",
"calcOperatorSum": "Summe",
"calcOperatorAverage": "Durchschnitt",
"calcOperatorMedian": "Median",
diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json
index bc62c35ca..c859910d3 100644
--- a/app/appearance/langs/en_US.json
+++ b/app/appearance/langs/en_US.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "NOT EMPTY",
"calcResultPercentEmpty": "EMPTY",
"calcResultPercentNotEmpty": "NOT EMPTY",
+ "calcResultPercentUniqueValues": "Percent unique values",
"calcResultSum": "SUM",
"calcResultAverage": "AVERAGE",
"calcResultMedian": "MEDIAN",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "Count not empty",
"calcOperatorPercentEmpty": "Percent empty",
"calcOperatorPercentNotEmpty": "Percent not empty",
+ "calcOperatorPercentUniqueValues": "Percent unique values",
"calcOperatorSum": "Sum",
"calcOperatorAverage": "Average",
"calcOperatorMedian": "Median",
diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json
index e363199a2..486723607 100644
--- a/app/appearance/langs/es_ES.json
+++ b/app/appearance/langs/es_ES.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "NO VACÍO",
"calcResultPercentEmpty": "VACÍO",
"calcResultPercentNotEmpty": "NO VACÍO",
+ "calcResultPercentUniqueValues": "Porcentaje de valores únicos",
"calcResultSum": "SUMA",
"calcResultAverage": "PROMEDIO",
"calcResultMedian": "MEDIANA",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "Cuenta no vacía",
"calcOperatorPercentEmpty": "Porcentaje vacío",
"calcOperatorPercentNotEmpty": "Porcentaje no vacío",
+ "calcOperatorPercentUniqueValues": "Porcentaje de valores únicos",
"calcOperatorSum": "Suma",
"calcOperatorAverage": "Promedio",
"calcOperatorMedian": "Mediana",
diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json
index 547f15f3a..faf345df5 100644
--- a/app/appearance/langs/fr_FR.json
+++ b/app/appearance/langs/fr_FR.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "NON VIDE",
"calcResultPercentEmpty": "VIDE",
"calcResultPercentNotEmpty": "NON VIDE",
+ "calcResultPercentUniqueValues": "Pourcentage de valeurs uniques",
"calcResultSum": "SOMME",
"calcResultAverage": "MOYENNE",
"calcResultMedian": "MÉDIANE",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "Compter non vide",
"calcOperatorPercentEmpty": "Pourcentage vide",
"calcOperatorPercentNotEmpty": "Pourcentage non vide",
+ "calcOperatorPercentUniqueValues": "Pourcentage de valeurs uniques",
"calcOperatorSum": "Somme",
"calcOperatorAverage": "Moyenne",
"calcOperatorMedian": "Médiane",
diff --git a/app/appearance/langs/he_IL.json b/app/appearance/langs/he_IL.json
index 334715c7f..329d6e856 100644
--- a/app/appearance/langs/he_IL.json
+++ b/app/appearance/langs/he_IL.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "לא ריק",
"calcResultPercentEmpty": "ריק",
"calcResultPercentNotEmpty": "לא ריק",
+ "calcResultPercentUniqueValues": "אחוז ערכים ייחודיים",
"calcResultSum": "סכום",
"calcResultAverage": "ממוצע",
"calcResultMedian": "חציון",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "ספור לא ריקים",
"calcOperatorPercentEmpty": "אחוז ריקים",
"calcOperatorPercentNotEmpty": "אחוז לא ריקים",
+ "calcOperatorPercentUniqueValues": "אחוז ערכים ייחודיים",
"calcOperatorSum": "סכום",
"calcOperatorAverage": "ממוצע",
"calcOperatorMedian": "חציון",
diff --git a/app/appearance/langs/it_IT.json b/app/appearance/langs/it_IT.json
index 75d491886..e9bd4429c 100644
--- a/app/appearance/langs/it_IT.json
+++ b/app/appearance/langs/it_IT.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "NON VUOTO",
"calcResultPercentEmpty": "VUOTO",
"calcResultPercentNotEmpty": "NON VUOTO",
+ "calcResultPercentUniqueValues": "Percentuale di valori unici",
"calcResultSum": "SOMMA",
"calcResultAverage": "MEDIA",
"calcResultMedian": "MEDIANA",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "Conta non vuoti",
"calcOperatorPercentEmpty": "Percentuale vuoti",
"calcOperatorPercentNotEmpty": "Percentuale non vuoti",
+ "calcOperatorPercentUniqueValues": "Percentuale di valori unici",
"calcOperatorSum": "Somma",
"calcOperatorAverage": "Media",
"calcOperatorMedian": "Mediana",
diff --git a/app/appearance/langs/ja_JP.json b/app/appearance/langs/ja_JP.json
index 8b545572b..47800fbd4 100644
--- a/app/appearance/langs/ja_JP.json
+++ b/app/appearance/langs/ja_JP.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "空ではない数",
"calcResultPercentEmpty": "空のパーセント",
"calcResultPercentNotEmpty": "空ではないパーセント",
+ "calcResultPercentUniqueValues": "ユニーク値の割合",
"calcResultSum": "合計値",
"calcResultAverage": "平均値",
"calcResultMedian": "中央値",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "空ではない数",
"calcOperatorPercentEmpty": "空のパーセント",
"calcOperatorPercentNotEmpty": "空ではないパーセント",
+ "calcOperatorPercentUniqueValues": "ユニーク値の割合",
"calcOperatorSum": "合計値",
"calcOperatorAverage": "平均値",
"calcOperatorMedian": "中央値",
diff --git a/app/appearance/langs/pl_PL.json b/app/appearance/langs/pl_PL.json
index cfabd3dcd..957f1b2f7 100644
--- a/app/appearance/langs/pl_PL.json
+++ b/app/appearance/langs/pl_PL.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "NIE PUSTY",
"calcResultPercentEmpty": "PUSTE",
"calcResultPercentNotEmpty": "NIE PUSTE",
+ "calcResultPercentUniqueValues": "Procent unikalnych wartości",
"calcResultSum": "SUMA",
"calcResultAverage": "ŚREDNIA",
"calcResultMedian": "MEDIANA",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "Zlicz niepuste",
"calcOperatorPercentEmpty": "Procent pustych",
"calcOperatorPercentNotEmpty": "Procent niepustych",
+ "calcOperatorPercentUniqueValues": "Procent unikalnych wartości",
"calcOperatorSum": "Suma",
"calcOperatorAverage": "Średnia",
"calcOperatorMedian": "Mediana",
diff --git a/app/appearance/langs/ru_RU.json b/app/appearance/langs/ru_RU.json
index 64d2b29f3..7cfe6c8fa 100644
--- a/app/appearance/langs/ru_RU.json
+++ b/app/appearance/langs/ru_RU.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "НЕ ПУСТО",
"calcResultPercentEmpty": "ПУСТО",
"calcResultPercentNotEmpty": "НЕ ПУСТО",
+ "calcResultPercentUniqueValues": "Процент уникальных значений",
"calcResultSum": "СУММА",
"calcResultAverage": "СРЕДНЕЕ",
"calcResultMedian": "МЕДИАНА",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "Подсчитать непустые",
"calcOperatorPercentEmpty": "Процент пустых",
"calcOperatorPercentNotEmpty": "Процент не пустых",
+ "calcOperatorPercentUniqueValues": "Процент уникальных значений",
"calcOperatorSum": "Сумма",
"calcOperatorAverage": "Среднее",
"calcOperatorMedian": "Медиана",
diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json
index e39dadf54..4c3910f5d 100644
--- a/app/appearance/langs/zh_CHT.json
+++ b/app/appearance/langs/zh_CHT.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "已填寫",
"calcResultPercentEmpty": "未填寫佔比",
"calcResultPercentNotEmpty": "已填寫佔比",
+ "calcResultPercentUniqueValues": "唯一值佔比",
"calcResultSum": "求和",
"calcResultAverage": "平均值",
"calcResultMedian": "中位數",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "已填寫",
"calcOperatorPercentEmpty": "未填寫佔比",
"calcOperatorPercentNotEmpty": "已填寫佔比",
+ "calcOperatorPercentUniqueValues": "唯一值佔比",
"calcOperatorSum": "求和",
"calcOperatorAverage": "平均值",
"calcOperatorMedian": "中位數",
diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json
index 9a060b97f..da133b4dc 100644
--- a/app/appearance/langs/zh_CN.json
+++ b/app/appearance/langs/zh_CN.json
@@ -239,6 +239,7 @@
"calcResultCountNotEmpty": "已填写",
"calcResultPercentEmpty": "未填写占比",
"calcResultPercentNotEmpty": "已填写占比",
+ "calcResultPercentUniqueValues": "唯一值占比",
"calcResultSum": "求和",
"calcResultAverage": "平均值",
"calcResultMedian": "中位数",
@@ -256,6 +257,7 @@
"calcOperatorCountNotEmpty": "已填写",
"calcOperatorPercentEmpty": "未填写占比",
"calcOperatorPercentNotEmpty": "已填写占比",
+ "calcOperatorPercentUniqueValues": "唯一值占比",
"calcOperatorSum": "求和",
"calcOperatorAverage": "平均值",
"calcOperatorMedian": "中位数",
diff --git a/app/src/protyle/render/av/calc.ts b/app/src/protyle/render/av/calc.ts
index 4d570675a..cdb2b3ef1 100644
--- a/app/src/protyle/render/av/calc.ts
+++ b/app/src/protyle/render/av/calc.ts
@@ -208,6 +208,17 @@ export const openCalcMenu = async (protyle: IProtyle, calcElement: HTMLElement,
blockID,
target: calcElement
});
+ calcItem({
+ menu,
+ protyle,
+ colId,
+ avId,
+ oldOperator,
+ operator: "Percent unique values",
+ data: panelData?.data,
+ blockID,
+ target: calcElement
+ });
} else {
calcItem({
menu,
@@ -427,6 +438,9 @@ export const getCalcValue = (column: IAVColumn) => {
case "Percent not empty":
value = `${resultCalc.formattedContent}${window.siyuan.languages.calcResultPercentNotEmpty}`;
break;
+ case "Percent unique values":
+ value = `${resultCalc.formattedContent}${window.siyuan.languages.calcResultPercentUniqueValues}`;
+ break;
case "Sum":
value = `${resultCalc.formattedContent}${window.siyuan.languages.calcResultSum}`;
break;
@@ -486,6 +500,8 @@ export const getNameByOperator = (operator: string, isRollup: boolean) => {
return window.siyuan.languages.calcOperatorPercentEmpty;
case "Percent not empty":
return window.siyuan.languages.calcOperatorPercentNotEmpty;
+ case "Percent unique values":
+ return window.siyuan.languages.calcOperatorPercentUniqueValues;
case "Checked":
return window.siyuan.languages.checked;
case "Unchecked":
diff --git a/kernel/av/calc.go b/kernel/av/calc.go
index 4260fcdda..b312cae11 100644
--- a/kernel/av/calc.go
+++ b/kernel/av/calc.go
@@ -28,24 +28,25 @@ type ColumnCalc struct {
type CalcOperator string
const (
- CalcOperatorNone CalcOperator = ""
- CalcOperatorCountAll CalcOperator = "Count all"
- CalcOperatorCountValues CalcOperator = "Count values"
- CalcOperatorCountUniqueValues CalcOperator = "Count unique values"
- CalcOperatorCountEmpty CalcOperator = "Count empty"
- CalcOperatorCountNotEmpty CalcOperator = "Count not empty"
- CalcOperatorPercentEmpty CalcOperator = "Percent empty"
- CalcOperatorPercentNotEmpty CalcOperator = "Percent not empty"
- CalcOperatorSum CalcOperator = "Sum"
- CalcOperatorAverage CalcOperator = "Average"
- CalcOperatorMedian CalcOperator = "Median"
- CalcOperatorMin CalcOperator = "Min"
- CalcOperatorMax CalcOperator = "Max"
- CalcOperatorRange CalcOperator = "Range"
- CalcOperatorEarliest CalcOperator = "Earliest"
- CalcOperatorLatest CalcOperator = "Latest"
- CalcOperatorChecked CalcOperator = "Checked"
- CalcOperatorUnchecked CalcOperator = "Unchecked"
- CalcOperatorPercentChecked CalcOperator = "Percent checked"
- CalcOperatorPercentUnchecked CalcOperator = "Percent unchecked"
+ CalcOperatorNone CalcOperator = ""
+ CalcOperatorCountAll CalcOperator = "Count all"
+ CalcOperatorCountValues CalcOperator = "Count values"
+ CalcOperatorCountUniqueValues CalcOperator = "Count unique values"
+ CalcOperatorCountEmpty CalcOperator = "Count empty"
+ CalcOperatorCountNotEmpty CalcOperator = "Count not empty"
+ CalcOperatorPercentEmpty CalcOperator = "Percent empty"
+ CalcOperatorPercentNotEmpty CalcOperator = "Percent not empty"
+ CalcOperatorPercentUniqueValues CalcOperator = "Percent unique values"
+ CalcOperatorSum CalcOperator = "Sum"
+ CalcOperatorAverage CalcOperator = "Average"
+ CalcOperatorMedian CalcOperator = "Median"
+ CalcOperatorMin CalcOperator = "Min"
+ CalcOperatorMax CalcOperator = "Max"
+ CalcOperatorRange CalcOperator = "Range"
+ CalcOperatorEarliest CalcOperator = "Earliest"
+ CalcOperatorLatest CalcOperator = "Latest"
+ CalcOperatorChecked CalcOperator = "Checked"
+ CalcOperatorUnchecked CalcOperator = "Unchecked"
+ CalcOperatorPercentChecked CalcOperator = "Percent checked"
+ CalcOperatorPercentUnchecked CalcOperator = "Percent unchecked"
)
diff --git a/kernel/av/table_calc.go b/kernel/av/table_calc.go
index acd5ec68e..52fb18cc0 100644
--- a/kernel/av/table_calc.go
+++ b/kernel/av/table_calc.go
@@ -130,6 +130,20 @@ func (table *Table) calcColTemplate(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content {
+ if !uniqueValues[row.Cells[colIndex].Value.Template.Content] {
+ uniqueValues[row.Cells[colIndex].Value.Template.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
case CalcOperatorSum:
sum := 0.0
for _, row := range table.Rows {
@@ -276,6 +290,22 @@ func (table *Table) calcColMAsset(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) {
+ for _, sel := range row.Cells[colIndex].Value.MAsset {
+ if _, ok := uniqueValues[sel.Content]; !ok {
+ uniqueValues[sel.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -341,6 +371,22 @@ func (table *Table) calcColMSelect(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) {
+ for _, sel := range row.Cells[colIndex].Value.MSelect {
+ if _, ok := uniqueValues[sel.Content]; !ok {
+ uniqueValues[sel.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -404,6 +450,20 @@ func (table *Table) calcColSelect(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) && nil != row.Cells[colIndex].Value.MSelect[0] && "" != row.Cells[colIndex].Value.MSelect[0].Content {
+ if _, ok := uniqueValues[row.Cells[colIndex].Value.MSelect[0].Content]; !ok {
+ uniqueValues[row.Cells[colIndex].Value.MSelect[0].Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -467,6 +527,20 @@ func (table *Table) calcColDate(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[int64]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty {
+ if _, ok := uniqueValues[row.Cells[colIndex].Value.Date.Content]; !ok {
+ countUniqueValues++
+ uniqueValues[row.Cells[colIndex].Value.Date.Content] = true
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
case CalcOperatorEarliest:
earliest := int64(0)
var isNotTime, hasEndDate bool
@@ -581,6 +655,20 @@ func (table *Table) calcColNumber(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[float64]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty {
+ if !uniqueValues[row.Cells[colIndex].Value.Number.Content] {
+ uniqueValues[row.Cells[colIndex].Value.Number.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
case CalcOperatorSum:
sum := 0.0
for _, row := range table.Rows {
@@ -719,6 +807,20 @@ func (table *Table) calcColText(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Text && "" != row.Cells[colIndex].Value.Text.Content {
+ if !uniqueValues[row.Cells[colIndex].Value.Text.Content] {
+ uniqueValues[row.Cells[colIndex].Value.Text.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -782,6 +884,20 @@ func (table *Table) calcColURL(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.URL && "" != row.Cells[colIndex].Value.URL.Content {
+ if !uniqueValues[row.Cells[colIndex].Value.URL.Content] {
+ uniqueValues[row.Cells[colIndex].Value.URL.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -845,6 +961,20 @@ func (table *Table) calcColEmail(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Email && "" != row.Cells[colIndex].Value.Email.Content {
+ if !uniqueValues[row.Cells[colIndex].Value.Email.Content] {
+ uniqueValues[row.Cells[colIndex].Value.Email.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -908,6 +1038,20 @@ func (table *Table) calcColPhone(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Phone && "" != row.Cells[colIndex].Value.Phone.Content {
+ if !uniqueValues[row.Cells[colIndex].Value.Phone.Content] {
+ uniqueValues[row.Cells[colIndex].Value.Phone.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -971,6 +1115,20 @@ func (table *Table) calcColBlock(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Block && "" != row.Cells[colIndex].Value.Block.Content {
+ if !uniqueValues[row.Cells[colIndex].Value.Block.Content] {
+ uniqueValues[row.Cells[colIndex].Value.Block.Content] = true
+ countUniqueValues++
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -1034,6 +1192,20 @@ func (table *Table) calcColCreated(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[int64]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created {
+ if _, ok := uniqueValues[row.Cells[colIndex].Value.Created.Content]; !ok {
+ countUniqueValues++
+ uniqueValues[row.Cells[colIndex].Value.Created.Content] = true
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
case CalcOperatorEarliest:
earliest := int64(0)
for _, row := range table.Rows {
@@ -1137,6 +1309,20 @@ func (table *Table) calcColUpdated(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[int64]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty {
+ if _, ok := uniqueValues[row.Cells[colIndex].Value.Updated.Content]; !ok {
+ countUniqueValues++
+ uniqueValues[row.Cells[colIndex].Value.Updated.Content] = true
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
case CalcOperatorEarliest:
earliest := int64(0)
for _, row := range table.Rows {
@@ -1285,6 +1471,22 @@ func (table *Table) calcColRelation(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Relation {
+ for _, id := range row.Cells[colIndex].Value.Relation.BlockIDs {
+ if !uniqueValues[id] {
+ uniqueValues[id] = true
+ countUniqueValues++
+ }
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
}
}
@@ -1350,6 +1552,22 @@ func (table *Table) calcColRollup(col *TableColumn, colIndex int) {
if 0 < len(table.Rows) {
col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, row := range table.Rows {
+ if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup {
+ for _, content := range row.Cells[colIndex].Value.Rollup.Contents {
+ if !uniqueValues[content.String(true)] {
+ uniqueValues[content.String(true)] = true
+ countUniqueValues++
+ }
+ }
+ }
+ }
+ if 0 < len(table.Rows) {
+ col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)}
+ }
case CalcOperatorSum:
sum := 0.0
for _, row := range table.Rows {
diff --git a/kernel/av/value.go b/kernel/av/value.go
index bc00f0ee8..b50d133ef 100644
--- a/kernel/av/value.go
+++ b/kernel/av/value.go
@@ -730,6 +730,18 @@ func (r *ValueRollup) RenderContents(calc *RollupCalc, destKey *Key) {
if 0 < len(r.Contents) {
r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(float64(countNonEmpty)/float64(len(r.Contents)), NumberFormatPercent)}}
}
+ case CalcOperatorPercentUniqueValues:
+ countUniqueValues := 0
+ uniqueValues := map[string]bool{}
+ for _, v := range r.Contents {
+ if _, ok := uniqueValues[v.String(true)]; !ok {
+ uniqueValues[v.String(true)] = true
+ countUniqueValues++
+ }
+ }
+ if 0 < len(r.Contents) {
+ r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(r.Contents)), NumberFormatPercent)}}
+ }
case CalcOperatorSum:
sum := 0.0
for _, v := range r.Contents {
From 72469c277b7fff1d83f712170b7e1f2148cd8a09 Mon Sep 17 00:00:00 2001
From: Daniel <845765@qq.com>
Date: Wed, 20 Nov 2024 20:31:30 +0800
Subject: [PATCH 2/4] :art: The database supports calculating the "Percent
unique values" of the field
https://github.com/siyuan-note/siyuan/issues/13192
---
app/appearance/langs/de_DE.json | 24 ++++++++++++------------
app/appearance/langs/en_US.json | 26 +++++++++++++-------------
app/appearance/langs/es_ES.json | 26 +++++++++++++-------------
app/appearance/langs/fr_FR.json | 24 ++++++++++++------------
app/appearance/langs/he_IL.json | 14 +++++++-------
app/appearance/langs/it_IT.json | 26 +++++++++++++-------------
app/appearance/langs/pl_PL.json | 26 +++++++++++++-------------
app/appearance/langs/ru_RU.json | 30 +++++++++++++++---------------
8 files changed, 98 insertions(+), 98 deletions(-)
diff --git a/app/appearance/langs/de_DE.json b/app/appearance/langs/de_DE.json
index be4f6f99a..bfed92ab8 100644
--- a/app/appearance/langs/de_DE.json
+++ b/app/appearance/langs/de_DE.json
@@ -232,20 +232,20 @@
"builtIn": "Integriert",
"endDate": "Enddatum",
"needLogin": "Diese Funktion erfordert ein Anmelden",
- "calcResultCountAll": "ANZAHL",
- "calcResultCountValues": "WERT",
- "calcResultCountUniqueValues": "EINZIGARTIG",
- "calcResultCountEmpty": "LEER",
- "calcResultCountNotEmpty": "NICHT LEER",
- "calcResultPercentEmpty": "LEER",
- "calcResultPercentNotEmpty": "NICHT LEER",
+ "calcResultCountAll": "Alle zählen",
+ "calcResultCountValues": "Werte zählen",
+ "calcResultCountUniqueValues": "Eindeutige Werte zählen",
+ "calcResultCountEmpty": "Leer zählen",
+ "calcResultCountNotEmpty": "Nicht leer zählen",
+ "calcResultPercentEmpty": "Prozent leer",
+ "calcResultPercentNotEmpty": "Prozent nicht leer",
"calcResultPercentUniqueValues": "Prozent einzigartige Werte",
"calcResultSum": "SUMME",
- "calcResultAverage": "DURCHSCHNITT",
- "calcResultMedian": "MEDIAN",
- "calcResultMin": "MIN",
- "calcResultMax": "MAX",
- "calcResultRange": "BEREICH",
+ "calcResultAverage": "Durchschnitt",
+ "calcResultMedian": "Median",
+ "calcResultMin": "Min",
+ "calcResultMax": "Max",
+ "calcResultRange": "Bereich",
"calc": "Berechnen",
"createWorkspace": "Arbeitsbereich erstellen",
"createWorkspaceTip": "Sind Sie sicher, dass Sie diesen Pfad verwenden möchten, um einen Arbeitsbereich zu erstellen?",
diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json
index c859910d3..a134824cc 100644
--- a/app/appearance/langs/en_US.json
+++ b/app/appearance/langs/en_US.json
@@ -232,20 +232,20 @@
"builtIn": "Built-in",
"endDate": "End date",
"needLogin": "This function needs to be logged in to use",
- "calcResultCountAll": "COUNT",
- "calcResultCountValues": "VALUES",
- "calcResultCountUniqueValues": "UNIQUE",
- "calcResultCountEmpty": "EMPTY",
- "calcResultCountNotEmpty": "NOT EMPTY",
- "calcResultPercentEmpty": "EMPTY",
- "calcResultPercentNotEmpty": "NOT EMPTY",
+ "calcResultCountAll": "Count all",
+ "calcResultCountValues": "Count Values",
+ "calcResultCountUniqueValues": "Count unique values",
+ "calcResultCountEmpty": "Count empty",
+ "calcResultCountNotEmpty": "Count not empty",
+ "calcResultPercentEmpty": "Percent empty",
+ "calcResultPercentNotEmpty": "Percent not empty",
"calcResultPercentUniqueValues": "Percent unique values",
- "calcResultSum": "SUM",
- "calcResultAverage": "AVERAGE",
- "calcResultMedian": "MEDIAN",
- "calcResultMin": "MIN",
- "calcResultMax": "MAX",
- "calcResultRange": "RANGE",
+ "calcResultSum": "Sum",
+ "calcResultAverage": "Average",
+ "calcResultMedian": "Median",
+ "calcResultMin": "Min",
+ "calcResultMax": "Max",
+ "calcResultRange": "Range",
"calc": "Calculate",
"createWorkspace": "Create Workspace",
"createWorkspaceTip": "Are you sure to use this path to create a workspace?",
diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json
index 486723607..c65770957 100644
--- a/app/appearance/langs/es_ES.json
+++ b/app/appearance/langs/es_ES.json
@@ -232,20 +232,20 @@
"builtIn": "Incorporado",
"endDate": "Fecha de finalización",
"needLogin": "Esta función requiere iniciar sesión en la cuenta antes de poder usarla",
- "calcResultCountAll": "CONTAR",
- "calcResultCountValues": "VALORES",
- "calcResultCountUniqueValues": "ÚNICO",
- "calcResultCountEmpty": "VACÍO",
- "calcResultCountNotEmpty": "NO VACÍO",
- "calcResultPercentEmpty": "VACÍO",
- "calcResultPercentNotEmpty": "NO VACÍO",
+ "calcResultCountAll": "Contar todo",
+ "calcResultCountValues": "Valores de conteo",
+ "calcResultCountUniqueValues": "Contar valores únicos",
+ "calcResultCountEmpty": "Cuenta vacía",
+ "calcResultCountNotEmpty": "Cuenta no vacía",
+ "calcResultPercentEmpty": "Porcentaje vacío",
+ "calcResultPercentNotEmpty": "Porcentaje no vacío",
"calcResultPercentUniqueValues": "Porcentaje de valores únicos",
"calcResultSum": "SUMA",
- "calcResultAverage": "PROMEDIO",
- "calcResultMedian": "MEDIANA",
- "calcResultMin": "MIN",
- "calcResultMax": "MAX",
- "calcResultRange": "RANGO",
+ "calcResultAverage": "Promedio",
+ "calcResultMedian": "Mediana",
+ "calcResultMin": "Min",
+ "calcResultMax": "Máx",
+ "calcResultRange": "Rango",
"calc": "Calcular",
"createWorkspace": "Crear espacio de trabajo",
"createWorkspaceTip": "¿Estás seguro de usar esta ruta para crear un espacio de trabajo?",
@@ -262,7 +262,7 @@
"calcOperatorAverage": "Promedio",
"calcOperatorMedian": "Mediana",
"calcOperatorMin": "Min",
- "calcOperatorMax": "Máx.",
+ "calcOperatorMax": "Máx",
"calcOperatorRange": "Rango",
"calcOperatorEarliest": "Primero",
"calcOperatorLatest": "Último",
diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json
index faf345df5..79ac2c406 100644
--- a/app/appearance/langs/fr_FR.json
+++ b/app/appearance/langs/fr_FR.json
@@ -232,20 +232,20 @@
"builtIn": "Intégré",
"endDate": "Date de fin",
"needLogin": "La fonctionnalité nécessite un numéro de compte de connexion avant de pouvoir être utilisée",
- "calcResultCountAll": "COUNT",
- "calcResultCountValues": "VALEURS",
- "calcResultCountUniqueValues": "UNIQUE",
- "calcResultCountVide": "VIDE",
- "calcResultCountNotEmpty": "NON VIDE",
- "calcResultPercentEmpty": "VIDE",
- "calcResultPercentNotEmpty": "NON VIDE",
+ "calcResultCountAll": "Compter tout",
+ "calcResultCountValues": "Compter les valeurs",
+ "calcResultCountUniqueValues": "Compter les valeurs uniques",
+ "calcResultCountVide": "Compter vide",
+ "calcResultCountNotEmpty": "Compter non vide",
+ "calcResultPercentEmpty": "Pourcentage vide",
+ "calcResultPercentNotEmpty": "Pourcentage non vide",
"calcResultPercentUniqueValues": "Pourcentage de valeurs uniques",
"calcResultSum": "SOMME",
- "calcResultAverage": "MOYENNE",
- "calcResultMedian": "MÉDIANE",
- "calcResultMin": "MIN",
- "calcResultMax": "MAX",
- "calcResultRange": "PLAGE",
+ "calcResultAverage": "Moyenne",
+ "calcResultMedian": "Médiane",
+ "calcResultMin": "Min",
+ "calcResultMax": "Max",
+ "calcResultRange": "Plage",
"calc": "Calculer",
"createWorkspace": "Créer un espace de travail",
"createWorkspaceTip": "Êtes-vous sûr d'utiliser ce chemin pour créer un espace de travail ?",
diff --git a/app/appearance/langs/he_IL.json b/app/appearance/langs/he_IL.json
index 329d6e856..dee1aaef1 100644
--- a/app/appearance/langs/he_IL.json
+++ b/app/appearance/langs/he_IL.json
@@ -232,13 +232,13 @@
"builtIn": "מותקן",
"endDate": "תאריך סיום",
"needLogin": "פונקציה זו דורשת כניסה",
- "calcResultCountAll": "מנה",
- "calcResultCountValues": "ערכים",
- "calcResultCountUniqueValues": "ערכים ייחודיים",
- "calcResultCountEmpty": "ריק",
- "calcResultCountNotEmpty": "לא ריק",
- "calcResultPercentEmpty": "ריק",
- "calcResultPercentNotEmpty": "לא ריק",
+ "calcResultCountAll": "ספור הכל",
+ "calcResultCountValues": "ספור ערכים",
+ "calcResultCountUniqueValues": "ספור ערכים ייחודיים",
+ "calcResultCountEmpty": "ספור ריקים",
+ "calcResultCountNotEmpty": "ספור לא ריקים",
+ "calcResultPercentEmpty": "אחוז ריקים",
+ "calcResultPercentNotEmpty": "אחוז לא ריקים",
"calcResultPercentUniqueValues": "אחוז ערכים ייחודיים",
"calcResultSum": "סכום",
"calcResultAverage": "ממוצע",
diff --git a/app/appearance/langs/it_IT.json b/app/appearance/langs/it_IT.json
index e9bd4429c..0a043470f 100644
--- a/app/appearance/langs/it_IT.json
+++ b/app/appearance/langs/it_IT.json
@@ -232,20 +232,20 @@
"builtIn": "Integrato",
"endDate": "Data di fine",
"needLogin": "Questa funzione richiede il login per essere utilizzata",
- "calcResultCountAll": "CONTA",
- "calcResultCountValues": "VALORI",
- "calcResultCountUniqueValues": "UNICI",
- "calcResultCountEmpty": "VUOTO",
- "calcResultCountNotEmpty": "NON VUOTO",
- "calcResultPercentEmpty": "VUOTO",
- "calcResultPercentNotEmpty": "NON VUOTO",
+ "calcResultCountAll": "Conta tutto",
+ "calcResultCountValues": "Conta valori",
+ "calcResultCountUniqueValues": "Conta valori unici",
+ "calcResultCountEmpty": "Conta vuoti",
+ "calcResultCountNotEmpty": "Conta non vuoti",
+ "calcResultPercentEmpty": "Percentuale vuoti",
+ "calcResultPercentNotEmpty": "Percentuale non vuoti",
"calcResultPercentUniqueValues": "Percentuale di valori unici",
- "calcResultSum": "SOMMA",
- "calcResultAverage": "MEDIA",
- "calcResultMedian": "MEDIANA",
- "calcResultMin": "MIN",
- "calcResultMax": "MAX",
- "calcResultRange": "INTERVALLO",
+ "calcResultSum": "Somma",
+ "calcResultAverage": "Media",
+ "calcResultMedian": "Mediana",
+ "calcResultMin": "Min",
+ "calcResultMax": "Max",
+ "calcResultRange": "Intervallo",
"calc": "Calcola",
"createWorkspace": "Crea area di lavoro",
"createWorkspaceTip": "Sei sicuro di voler utilizzare questo percorso per creare un'area di lavoro?",
diff --git a/app/appearance/langs/pl_PL.json b/app/appearance/langs/pl_PL.json
index 957f1b2f7..f43fc9349 100644
--- a/app/appearance/langs/pl_PL.json
+++ b/app/appearance/langs/pl_PL.json
@@ -232,20 +232,20 @@
"builtIn": "Wbudowane",
"endDate": "Data zakończenia",
"needLogin": "Ta funkcja wymaga zalogowania się",
- "calcResultCountAll": "LICZBA",
- "calcResultCountValues": "WARTOŚCI",
- "calcResultCountUniqueValues": "UNIKALNE",
- "calcResultCountEmpty": "PUSTY",
- "calcResultCountNotEmpty": "NIE PUSTY",
- "calcResultPercentEmpty": "PUSTE",
- "calcResultPercentNotEmpty": "NIE PUSTE",
+ "calcResultCountAll": "Zlicz wszystko",
+ "calcResultCountValues": "Zlicz wartości",
+ "calcResultCountUniqueValues": "Zlicz unikalne wartości",
+ "calcResultCountEmpty": "Zlicz puste",
+ "calcResultCountNotEmpty": "Zlicz niepuste",
+ "calcResultPercentEmpty": "Procent pustych",
+ "calcResultPercentNotEmpty": "Procent niepustych",
"calcResultPercentUniqueValues": "Procent unikalnych wartości",
- "calcResultSum": "SUMA",
- "calcResultAverage": "ŚREDNIA",
- "calcResultMedian": "MEDIANA",
- "calcResultMin": "MIN",
- "calcResultMax": "MAX",
- "calcResultRange": "ZAKRES",
+ "calcResultSum": "Suma",
+ "calcResultAverage": "Średnia",
+ "calcResultMedian": "Mediana",
+ "calcResultMin": "Min",
+ "calcResultMax": "Max",
+ "calcResultRange": "Zakres",
"calc": "Oblicz",
"createWorkspace": "Utwórz obszar roboczy",
"createWorkspaceTip": "Czy na pewno chcesz użyć tej ścieżki do utworzenia obszaru roboczego?",
diff --git a/app/appearance/langs/ru_RU.json b/app/appearance/langs/ru_RU.json
index 7cfe6c8fa..2ec076827 100644
--- a/app/appearance/langs/ru_RU.json
+++ b/app/appearance/langs/ru_RU.json
@@ -232,20 +232,20 @@
"builtIn": "Встроенный",
"endDate": "Дата окончания",
"needLogin": "Эта функция требует входа в систему для использования",
- "calcResultCountAll": "СЧЕТ",
- "calcResultCountValues": "ЗНАЧЕНИЯ",
- "calcResultCountUniqueValues": "УНИКАЛЬНЫЕ",
- "calcResultCountEmpty": "ПУСТО",
- "calcResultCountNotEmpty": "НЕ ПУСТО",
- "calcResultPercentEmpty": "ПУСТО",
- "calcResultPercentNotEmpty": "НЕ ПУСТО",
+ "calcResultCountAll": "Подсчитать все",
+ "calcResultCountValues": "Подсчитать значения",
+ "calcResultCountUniqueValues": "Подсчитать уникальные значения",
+ "calcResultCountEmpty": "Подсчитать пустые",
+ "calcResultCountNotEmpty": "Подсчитать непустые",
+ "calcResultPercentEmpty": "Процент пустых",
+ "calcResultPercentNotEmpty": "Процент не пустых",
"calcResultPercentUniqueValues": "Процент уникальных значений",
- "calcResultSum": "СУММА",
- "calcResultAverage": "СРЕДНЕЕ",
- "calcResultMedian": "МЕДИАНА",
- "calcResultMin": "МИН.",
- "calcResultMax": "МАКС.",
- "calcResultRange": "ДИАПАЗОН",
+ "calcResultSum": "Сумма",
+ "calcResultAverage": "Среднее",
+ "calcResultMedian": "Медиана",
+ "calcResultMin": "Мин.",
+ "calcResultMax": "Макс.",
+ "calcResultRange": "Диапазон",
"calc": "Вычислить",
"createWorkspace": "Создать рабочее пространство",
"createWorkspaceTip": "Вы уверены, что хотите использовать этот путь для создания рабочего пространства?",
@@ -261,8 +261,8 @@
"calcOperatorSum": "Сумма",
"calcOperatorAverage": "Среднее",
"calcOperatorMedian": "Медиана",
- "calcOperatorMin": "Мин.",
- "calcOperatorMax": "Макс.",
+ "calcOperatorMin": "Мин",
+ "calcOperatorMax": "Макс",
"calcOperatorRange": "Диапазон",
"calcOperatorEarliest": "Самый ранний",
"calcOperatorLatest": "Самый поздний",
From 31c68efcdcd07c3d380f8a0cf20d5b5c932d7737 Mon Sep 17 00:00:00 2001
From: Daniel <845765@qq.com>
Date: Wed, 20 Nov 2024 21:06:20 +0800
Subject: [PATCH 3/4] :art: The database supports calculating the "Percent
unique values" of the field
https://github.com/siyuan-note/siyuan/issues/13192
---
app/appearance/langs/ru_RU.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/appearance/langs/ru_RU.json b/app/appearance/langs/ru_RU.json
index 2ec076827..a7e954e9d 100644
--- a/app/appearance/langs/ru_RU.json
+++ b/app/appearance/langs/ru_RU.json
@@ -243,8 +243,8 @@
"calcResultSum": "Сумма",
"calcResultAverage": "Среднее",
"calcResultMedian": "Медиана",
- "calcResultMin": "Мин.",
- "calcResultMax": "Макс.",
+ "calcResultMin": "Мин",
+ "calcResultMax": "Макс",
"calcResultRange": "Диапазон",
"calc": "Вычислить",
"createWorkspace": "Создать рабочее пространство",
From 0b2f92169753d5bce9414763b9a6377b933fd8d3 Mon Sep 17 00:00:00 2001
From: Daniel <845765@qq.com>
Date: Wed, 20 Nov 2024 21:25:18 +0800
Subject: [PATCH 4/4] :art: Improve inline elements find-replace
https://github.com/siyuan-note/siyuan/issues/13200
---
kernel/model/search.go | 53 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/kernel/model/search.go b/kernel/model/search.go
index 6a130cc21..b00088f52 100644
--- a/kernel/model/search.go
+++ b/kernel/model/search.go
@@ -625,6 +625,10 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
n.TextMarkTextContent = escapedR.ReplaceAllString(n.TextMarkTextContent, util.EscapeHTML(replacement))
}
}
+
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("a") {
if replaceTypes["aText"] {
if 0 == method {
@@ -636,6 +640,9 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
n.TextMarkTextContent = r.ReplaceAllString(n.TextMarkTextContent, replacement)
}
}
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
}
if replaceTypes["aTitle"] {
@@ -661,61 +668,87 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
}
}
}
-
} else if n.IsTextMarkType("em") {
if !replaceTypes["em"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "em")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("strong") {
if !replaceTypes["strong"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "strong")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("kbd") {
if !replaceTypes["kbd"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "kbd")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("mark") {
if !replaceTypes["mark"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "mark")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("s") {
if !replaceTypes["s"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "s")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("sub") {
if !replaceTypes["sub"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "sub")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("sup") {
if !replaceTypes["sup"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "sup")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("tag") {
if !replaceTypes["tag"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "tag")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("u") {
if !replaceTypes["u"] {
return ast.WalkContinue
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "u")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("inline-math") {
if !replaceTypes["inlineMath"] {
return ast.WalkContinue
@@ -730,6 +763,10 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
n.TextMarkInlineMathContent = r.ReplaceAllString(n.TextMarkInlineMathContent, replacement)
}
}
+
+ if "" == n.TextMarkInlineMathContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("inline-memo") {
if !replaceTypes["inlineMemo"] {
return ast.WalkContinue
@@ -744,6 +781,10 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
n.TextMarkInlineMemoContent = r.ReplaceAllString(n.TextMarkInlineMemoContent, replacement)
}
}
+
+ if "" == n.TextMarkInlineMemoContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("text") {
// Search and replace fails in some cases https://github.com/siyuan-note/siyuan/issues/10016
if !replaceTypes["text"] {
@@ -751,6 +792,9 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
}
replaceNodeTextMarkTextContent(n, method, keyword, replacement, r, "text")
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("block-ref") {
if !replaceTypes["blockRef"] {
return ast.WalkContinue
@@ -767,6 +811,10 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
n.TextMarkBlockRefSubtype = "s"
}
}
+
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
} else if n.IsTextMarkType("file-annotation-ref") {
if !replaceTypes["fileAnnotationRef"] {
return ast.WalkContinue
@@ -781,6 +829,9 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
n.TextMarkTextContent = r.ReplaceAllString(n.TextMarkTextContent, replacement)
}
}
+ if "" == n.TextMarkTextContent {
+ unlinks = append(unlinks, n)
+ }
}
}
return ast.WalkContinue