From 441874b2cd92b1d14fcf45ea98ab7f6741d52e8a Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 15:47:19 +0800 Subject: [PATCH 1/9] :art: Database grouping by field https://github.com/siyuan-note/siyuan/issues/10964 --- app/appearance/langs/ar_SA.json | 7 ++- app/appearance/langs/de_DE.json | 7 ++- app/appearance/langs/en_US.json | 7 ++- app/appearance/langs/es_ES.json | 7 ++- app/appearance/langs/fr_FR.json | 7 ++- app/appearance/langs/he_IL.json | 7 ++- app/appearance/langs/it_IT.json | 7 ++- app/appearance/langs/ja_JP.json | 7 ++- app/appearance/langs/pl_PL.json | 7 ++- app/appearance/langs/pt_BR.json | 7 ++- app/appearance/langs/ru_RU.json | 7 ++- app/appearance/langs/zh_CHT.json | 7 ++- app/appearance/langs/zh_CN.json | 7 ++- kernel/model/attribute_view.go | 100 +++++++++++++++++++------------ 14 files changed, 140 insertions(+), 51 deletions(-) diff --git a/app/appearance/langs/ar_SA.json b/app/appearance/langs/ar_SA.json index f011b84c0..ea3c7594d 100644 --- a/app/appearance/langs/ar_SA.json +++ b/app/appearance/langs/ar_SA.json @@ -1627,6 +1627,11 @@ "255": "قام الموقع المستهدف بتمكين حماية الروابط المباشرة، لذلك لا يمكن تنزيل [%d] من الموارد", "256": "المسار المحدد [%s] يحتوي على مسار مساحة عمل رئيسي [%s]", "257": "تذكرني لمدة 30 يومًا", - "258": "فشلت العملية، يرجى المحاولة مرة أخرى لاحقًا" + "258": "فشلت العملية، يرجى المحاولة مرة أخرى لاحقًا", + "259": "آخر %d أيام", + "260": "أمس", + "261": "اليوم", + "262": "غدًا", + "263": "التالي %d أيام" } } diff --git a/app/appearance/langs/de_DE.json b/app/appearance/langs/de_DE.json index 6c06c9497..de631d2c0 100644 --- a/app/appearance/langs/de_DE.json +++ b/app/appearance/langs/de_DE.json @@ -1627,6 +1627,11 @@ "255": "Die Zielseite hat den Hotlink-Schutz aktiviert, daher ist es nicht möglich, [%d] Ressourcen herunterzuladen", "256": "Der angegebene Pfad [%s] hat einen übergeordneten Arbeitsbereichspfad [%s]", "257": "Erinnere dich 30 Tage an mich", - "258": "Vorgang fehlgeschlagen, bitte versuchen Sie es später erneut" + "258": "Vorgang fehlgeschlagen, bitte versuchen Sie es später erneut", + "259": "Letzte %d Tage", + "260": "Gestern", + "261": "Heute", + "262": "Morgen", + "263": "Nächste %d Tage" } } diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index 5868a6891..ff71310c3 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1627,6 +1627,11 @@ "255": "The target site has enabled hotlink protection, so it is not possible to download [%d] resources", "256": "The specified path [%s] has a parent workspace path [%s]", "257": "Remember me for 30 days", - "258": "Operation failed, please try again later" + "258": "Operation failed, please try again later", + "259": "Last %d days", + "260": "Yesterday", + "261": "Today", + "262": "Tomorrow", + "263": "Next %d days" } } diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index 791c3ac64..e219f4c59 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1627,6 +1627,11 @@ "255": "El sitio de destino ha activado la protección contra hotlinking, por lo que no es posible descargar [%d] recursos", "256": "La ruta especificada [%s] tiene una ruta de espacio de trabajo padre [%s]", "257": "Recuérdame durante 30 días", - "258": "La operación falló, inténtelo de nuevo más tarde" + "258": "La operación falló, inténtelo de nuevo más tarde", + "259": "Últimos %d días", + "260": "Ayer", + "261": "Hoy", + "262": "Mañana", + "263": "Próximos %d días" } } diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index a9de3f59b..084b84c60 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1627,6 +1627,11 @@ "255": "Le site cible a activé la protection contre le hotlinking, il est donc impossible de télécharger [%d] ressources", "256": "Le chemin spécifié [%s] contient un chemin d'espace de travail parent [%s]", "257": "Souviens-toi de moi pendant 30 jours", - "258": "Échec de l'opération, veuillez réessayer plus tard" + "258": "Échec de l'opération, veuillez réessayer plus tard", + "259": "Les %d derniers jours", + "260": "Hier", + "261": "Aujourd'hui", + "262": "Demain", + "263": "Les %d prochains jours" } } diff --git a/app/appearance/langs/he_IL.json b/app/appearance/langs/he_IL.json index a6b5c9605..441cf2a88 100644 --- a/app/appearance/langs/he_IL.json +++ b/app/appearance/langs/he_IL.json @@ -1627,6 +1627,11 @@ "255": "האתר היעד הפעיל הגנה על קישורים חמים, ולכן לא ניתן להוריד [%d] משאבים", "256": "הנתיב שצוין [%s] מכיל נתיב מרחב עבודה אב [%s]", "257": "זכור אותי למשך 30 יום", - "258": "הפעולה נכשלה, נסה שוב מאוחר יותר" + "258": "הפעולה נכשלה, נסה שוב מאוחר יותר", + "259": "ה-%d ימים האחרונים", + "260": "אתמול", + "261": "היום", + "262": "מחר", + "263": "ה-%d ימים הבאים" } } diff --git a/app/appearance/langs/it_IT.json b/app/appearance/langs/it_IT.json index 9bbefee47..1e5c829da 100644 --- a/app/appearance/langs/it_IT.json +++ b/app/appearance/langs/it_IT.json @@ -1627,6 +1627,11 @@ "255": "Il sito di destinazione ha abilitato la protezione hotlink, quindi non è possibile scaricare [%d] risorse", "256": "Il percorso specificato [%s] ha un percorso di spazio di lavoro padre [%s]", "257": "Ricordati di me per 30 giorni", - "258": "Operazione fallita, riprova più tardi" + "258": "Operazione fallita, riprova più tardi", + "259": "Ultimi %d giorni", + "260": "Ieri", + "261": "Oggi", + "262": "Domani", + "263": "Prossimi %d giorni" } } diff --git a/app/appearance/langs/ja_JP.json b/app/appearance/langs/ja_JP.json index 3a2d8f7dc..691cd0ee3 100644 --- a/app/appearance/langs/ja_JP.json +++ b/app/appearance/langs/ja_JP.json @@ -1627,6 +1627,11 @@ "255": "ターゲットサイトはホットリンク保護を有効にしているため、[%d] 個のリソースをダウンロードできません", "256": "指定されたパス [%s] の親にワークスペースパス [%s] が存在します", "257": "30日間私を覚えていてください", - "258": "操作に失敗しました。後でもう一度お試しください" + "258": "操作に失敗しました。後でもう一度お試しください", + "259": "過去 %d 日間", + "260": "昨日", + "261": "今日", + "262": "明日", + "263": "次の %d 日間" } } diff --git a/app/appearance/langs/pl_PL.json b/app/appearance/langs/pl_PL.json index c6167b2cb..44437727f 100644 --- a/app/appearance/langs/pl_PL.json +++ b/app/appearance/langs/pl_PL.json @@ -1627,6 +1627,11 @@ "255": "Docelowa strona włączyła ochronę przed hotlinkowaniem, [%d] zasobów nie można pobrać", "256": "Określona ścieżka [%s] ma nadrzędną ścieżkę przestrzeni roboczej [%s]", "257": "Zapamiętaj mnie na 30 dni", - "258": "Operacja nie powiodła się, spróbuj ponownie później" + "258": "Operacja nie powiodła się, spróbuj ponownie później", + "259": "Ostatnie %d dni", + "260": "Wczoraj", + "261": "Dzisiaj", + "262": "Jutro", + "263": "Następne %d dni" } } diff --git a/app/appearance/langs/pt_BR.json b/app/appearance/langs/pt_BR.json index 7f2a7c600..1a4279646 100644 --- a/app/appearance/langs/pt_BR.json +++ b/app/appearance/langs/pt_BR.json @@ -1627,6 +1627,11 @@ "255": "O site de destino ativou proteção contra hotlinking, portanto não é possível baixar [%d] recursos", "256": "O caminho especificado [%s] tem um caminho de espaço de trabalho pai [%s]", "257": "Lembre-se de mim por 30 dias", - "258": "A operação falhou, tente novamente mais tarde" + "258": "A operação falhou, tente novamente mais tarde", + "259": "Últimos %d dias", + "260": "Ontem", + "261": "Hoje", + "262": "Amanhã", + "263": "Próximos %d dias" } } diff --git a/app/appearance/langs/ru_RU.json b/app/appearance/langs/ru_RU.json index b5873e368..8836010fc 100644 --- a/app/appearance/langs/ru_RU.json +++ b/app/appearance/langs/ru_RU.json @@ -1627,6 +1627,11 @@ "255": "Целевой сайт включил защиту от хотлинков, [%d] ресурсов невозможно скачать", "256": "Указанный путь [%s] имеет родительский путь рабочей области [%s]", "257": "Запомнить меня на 30 дней", - "258": "Операция не удалась, попробуйте позже" + "258": "Операция не удалась, попробуйте позже", + "259": "Прошедшие %d дней", + "260": "Вчера", + "261": "Сегодня", + "262": "Завтра", + "263": "Следующие %d дней" } } diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index a7366eb0a..122758b09 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1627,6 +1627,11 @@ "255": "目標站點啟用了防盜鏈,[%d] 個資源無法下載", "256": "指定的路徑 [%s] 父級存在工作空間路徑 [%s]", "257": "記住我 30 天", - "258": "操作失敗,請稍後再試" + "258": "操作失敗,請稍後再試", + "259": "過去 %d 天", + "260": "昨天", + "261": "今天", + "262": "明天", + "263": "未來 %d 天" } } diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index dd4ec71ce..afb061186 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1627,6 +1627,11 @@ "255": "目标站点启用了防盗链,[%d] 个资源无法下载", "256": "指定的路径 [%s] 父级存在工作空间路径 [%s]", "257": "记住我 30 天", - "258": "操作失败,请稍后再试" + "258": "操作失败,请稍后再试", + "259": "过去 %d 天", + "260": "昨天", + "261": "今天", + "262": "明天", + "263": "未来 %d 天" } } diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 88dae2279..db747f600 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -195,46 +195,70 @@ func SetAttributeViewGroup(avID, blockID string, group *av.ViewGroup) (err error value := item.GetValue(group.Field) if value.IsEmpty() { groupName = defaultGroupName - } else { + groupItemsMap[groupName] = append(groupItemsMap[groupName], item) + continue + } + + switch group.Method { + case av.GroupMethodValue: + groupName = value.String(false) + case av.GroupMethodRangeNum: + if group.Range.NumStart > value.Number.Content || group.Range.NumEnd < value.Number.Content { + groupName = notInRange + break + } + + for rangeEnd <= group.Range.NumEnd && rangeEnd < value.Number.Content { + rangeStart += group.Range.NumStep + rangeEnd += group.Range.NumStep + } + + if rangeStart <= value.Number.Content && rangeEnd >= value.Number.Content { + groupName = fmt.Sprintf("%s - %s", strconv.FormatFloat(rangeStart, 'f', -1, 64), strconv.FormatFloat(rangeEnd, 'f', -1, 64)) + } + case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative: + var contentTime time.Time + switch value.Type { + case av.KeyTypeDate: + contentTime = time.UnixMilli(value.Date.Content) + case av.KeyTypeCreated: + contentTime = time.UnixMilli(value.Created.Content) + case av.KeyTypeUpdated: + contentTime = time.UnixMilli(value.Updated.Content) + } switch group.Method { - case av.GroupMethodValue: - groupName = value.String(false) - case av.GroupMethodRangeNum: - if group.Range.NumStart > value.Number.Content || group.Range.NumEnd < value.Number.Content { - groupName = notInRange - break - } - - for rangeEnd <= group.Range.NumEnd && rangeEnd < value.Number.Content { - rangeStart += group.Range.NumStep - rangeEnd += group.Range.NumStep - } - - if rangeStart <= value.Number.Content && rangeEnd >= value.Number.Content { - groupName = fmt.Sprintf("%s - %s", strconv.FormatFloat(rangeStart, 'f', -1, 64), strconv.FormatFloat(rangeEnd, 'f', -1, 64)) - } - case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative: - var contentTime time.Time - switch value.Type { - case av.KeyTypeDate: - contentTime = time.UnixMilli(value.Date.Content) - case av.KeyTypeCreated: - contentTime = time.UnixMilli(value.Created.Content) - case av.KeyTypeUpdated: - contentTime = time.UnixMilli(value.Updated.Content) - } - switch group.Method { - case av.GroupMethodDateDay: - groupName = contentTime.Format("2006-01-02") - case av.GroupMethodDateWeek: - year, week := contentTime.ISOWeek() - groupName = fmt.Sprintf("%d-W%02d", year, week) - case av.GroupMethodDateMonth: + case av.GroupMethodDateDay: + groupName = contentTime.Format("2006-01-02") + case av.GroupMethodDateWeek: + year, week := contentTime.ISOWeek() + groupName = fmt.Sprintf("%d-W%02d", year, week) + case av.GroupMethodDateMonth: + groupName = contentTime.Format("2006-01") + case av.GroupMethodDateYear: + groupName = contentTime.Format("2006") + case av.GroupMethodDateRelative: + // 过去 30 天之前的按月分组 + // 过去 30 天、过去 7 天、昨天、今天、明天、未来 7 天、未来 30 天 + // 未来 30 天之后的按月分组 + now := time.Now() + if contentTime.Before(now.AddDate(0, 0, -30)) { + groupName = contentTime.Format("2006-01") + } else if contentTime.Before(now.AddDate(0, 0, -7)) { + groupName = fmt.Sprintf(Conf.language(259), 30) + } else if contentTime.Before(now.AddDate(0, 0, -1)) { + groupName = fmt.Sprintf(Conf.language(259), 7) + } else if contentTime.Equal(now.AddDate(0, 0, -1)) { + groupName = Conf.language(260) + } else if contentTime.Equal(now) { + groupName = Conf.language(261) + } else if contentTime.Equal(now.AddDate(0, 0, 1)) { + groupName = Conf.language(262) + } else if contentTime.Before(now.AddDate(0, 0, 7)) { + groupName = fmt.Sprintf(Conf.language(263), 7) + } else if contentTime.Before(now.AddDate(0, 0, 30)) { + groupName = fmt.Sprintf(Conf.language(263), 30) + } else { groupName = contentTime.Format("2006-01") - case av.GroupMethodDateYear: - groupName = contentTime.Format("2006") - case av.GroupMethodDateRelative: - } } } From 9655780ba4917dad4e03bc9a6895b3f0c37f6ba3 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 15:55:45 +0800 Subject: [PATCH 2/9] :art: Database grouping by field https://github.com/siyuan-note/siyuan/issues/10964 --- kernel/av/av.go | 2 +- kernel/model/attribute_view.go | 288 +++++++++++++++++---------------- 2 files changed, 149 insertions(+), 141 deletions(-) diff --git a/kernel/av/av.go b/kernel/av/av.go index 56fa399c9..ffbf76f69 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -185,13 +185,13 @@ type View struct { Desc string `json:"desc"` // 视图描述 Filters []*ViewFilter `json:"filters,omitempty"` // 过滤规则 Sorts []*ViewSort `json:"sorts,omitempty"` // 排序规则 - Group *ViewGroup `json:"group,omitempty"` // 分组规则 PageSize int `json:"pageSize"` // 每页条目数 LayoutType LayoutType `json:"type"` // 当前布局类型 Table *LayoutTable `json:"table,omitempty"` // 表格布局 Gallery *LayoutGallery `json:"gallery,omitempty"` // 卡片布局 ItemIDs []string `json:"itemIds,omitempty"` // 项目 ID 列表,用于维护所有项目 + Group *ViewGroup `json:"group,omitempty"` // 分组规则 Groups []*View `json:"groups,omitempty"` // 分组视图列表 GroupItemIDs []string `json:"groupItemIds,omitempty"` // 分组项目 ID 列表,用于维护分组中的所有项目 GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则 diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index db747f600..4af1e28a6 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -143,146 +143,6 @@ func SetAttributeViewGroup(avID, blockID string, group *av.ViewGroup) (err error } view.Group = group - view.Groups = nil - - // 生成分组数据 - const ( - defaultGroupName = "_@default@_" - notInRange = "_@notInRange@_" - ) - var groupName string - viewable := sql.RenderView(attrView, view, "") - - var items []av.Item - for _, item := range viewable.(av.Collection).GetItems() { - items = append(items, item) - } - var rangeStart, rangeEnd float64 - switch group.Method { - case av.GroupMethodValue: - if av.GroupOrderMan != group.Order { - sort.SliceStable(items, func(i, j int) bool { - if av.GroupOrderAsc == group.Order { - return items[i].GetValue(group.Field).String(false) < items[j].GetValue(group.Field).String(false) - } - return items[i].GetValue(group.Field).String(false) > items[j].GetValue(group.Field).String(false) - }) - } - case av.GroupMethodRangeNum: - if nil == group.Range { - logging.LogWarnf("range is nil in av [%s]", avID) - return - } - - rangeStart, rangeEnd = group.Range.NumStart, group.Range.NumStart+group.Range.NumStep - sort.SliceStable(items, func(i, j int) bool { - if av.GroupOrderAsc == group.Order { - return items[i].GetValue(group.Field).Number.Content < items[j].GetValue(group.Field).Number.Content - } - return items[i].GetValue(group.Field).Number.Content > items[j].GetValue(group.Field).Number.Content - }) - case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative: - sort.SliceStable(items, func(i, j int) bool { - if av.GroupOrderAsc == group.Order { - return items[i].GetValue(group.Field).Date.Content < items[j].GetValue(group.Field).Date.Content - } - return items[i].GetValue(group.Field).Date.Content > items[j].GetValue(group.Field).Date.Content - }) - } - - groupItemsMap := map[string][]av.Item{} - for _, item := range items { - value := item.GetValue(group.Field) - if value.IsEmpty() { - groupName = defaultGroupName - groupItemsMap[groupName] = append(groupItemsMap[groupName], item) - continue - } - - switch group.Method { - case av.GroupMethodValue: - groupName = value.String(false) - case av.GroupMethodRangeNum: - if group.Range.NumStart > value.Number.Content || group.Range.NumEnd < value.Number.Content { - groupName = notInRange - break - } - - for rangeEnd <= group.Range.NumEnd && rangeEnd < value.Number.Content { - rangeStart += group.Range.NumStep - rangeEnd += group.Range.NumStep - } - - if rangeStart <= value.Number.Content && rangeEnd >= value.Number.Content { - groupName = fmt.Sprintf("%s - %s", strconv.FormatFloat(rangeStart, 'f', -1, 64), strconv.FormatFloat(rangeEnd, 'f', -1, 64)) - } - case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative: - var contentTime time.Time - switch value.Type { - case av.KeyTypeDate: - contentTime = time.UnixMilli(value.Date.Content) - case av.KeyTypeCreated: - contentTime = time.UnixMilli(value.Created.Content) - case av.KeyTypeUpdated: - contentTime = time.UnixMilli(value.Updated.Content) - } - switch group.Method { - case av.GroupMethodDateDay: - groupName = contentTime.Format("2006-01-02") - case av.GroupMethodDateWeek: - year, week := contentTime.ISOWeek() - groupName = fmt.Sprintf("%d-W%02d", year, week) - case av.GroupMethodDateMonth: - groupName = contentTime.Format("2006-01") - case av.GroupMethodDateYear: - groupName = contentTime.Format("2006") - case av.GroupMethodDateRelative: - // 过去 30 天之前的按月分组 - // 过去 30 天、过去 7 天、昨天、今天、明天、未来 7 天、未来 30 天 - // 未来 30 天之后的按月分组 - now := time.Now() - if contentTime.Before(now.AddDate(0, 0, -30)) { - groupName = contentTime.Format("2006-01") - } else if contentTime.Before(now.AddDate(0, 0, -7)) { - groupName = fmt.Sprintf(Conf.language(259), 30) - } else if contentTime.Before(now.AddDate(0, 0, -1)) { - groupName = fmt.Sprintf(Conf.language(259), 7) - } else if contentTime.Equal(now.AddDate(0, 0, -1)) { - groupName = Conf.language(260) - } else if contentTime.Equal(now) { - groupName = Conf.language(261) - } else if contentTime.Equal(now.AddDate(0, 0, 1)) { - groupName = Conf.language(262) - } else if contentTime.Before(now.AddDate(0, 0, 7)) { - groupName = fmt.Sprintf(Conf.language(263), 7) - } else if contentTime.Before(now.AddDate(0, 0, 30)) { - groupName = fmt.Sprintf(Conf.language(263), 30) - } else { - groupName = contentTime.Format("2006-01") - } - } - } - groupItemsMap[groupName] = append(groupItemsMap[groupName], item) - } - - for name, groupItems := range groupItemsMap { - var v *av.View - switch view.LayoutType { - case av.LayoutTypeTable: - v = av.NewTableView() - v.Table = av.NewLayoutTable() - case av.LayoutTypeGallery: - v = av.NewGalleryView() - v.Gallery = av.NewLayoutGallery() - } - for _, item := range groupItems { - v.GroupItemIDs = append(v.GroupItemIDs, item.GetID()) - } - v.Name = name - view.Groups = append(view.Groups, v) - view.GroupDefault = name == defaultGroupName - } - err = av.SaveAttributeView(attrView) return err } @@ -1468,6 +1328,9 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page, return } + // 如果存在分组设置的话生成分组数据 + genGroup(view, attrView) + // 如果存在分组的话渲染分组视图视图 var groups []av.Viewable for _, groupView := range view.Groups { @@ -1489,6 +1352,151 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page, return } +func genGroup(view *av.View, attrView *av.AttributeView) { + if nil == view.Group { + return + } + + group := view.Group + view.Groups = nil + + const ( + defaultGroupName = "_@default@_" + notInRange = "_@notInRange@_" + ) + var groupName string + viewable := sql.RenderView(attrView, view, "") + + var items []av.Item + for _, item := range viewable.(av.Collection).GetItems() { + items = append(items, item) + } + var rangeStart, rangeEnd float64 + switch group.Method { + case av.GroupMethodValue: + if av.GroupOrderMan != group.Order { + sort.SliceStable(items, func(i, j int) bool { + if av.GroupOrderAsc == group.Order { + return items[i].GetValue(group.Field).String(false) < items[j].GetValue(group.Field).String(false) + } + return items[i].GetValue(group.Field).String(false) > items[j].GetValue(group.Field).String(false) + }) + } + case av.GroupMethodRangeNum: + if nil == group.Range { + return + } + + rangeStart, rangeEnd = group.Range.NumStart, group.Range.NumStart+group.Range.NumStep + sort.SliceStable(items, func(i, j int) bool { + if av.GroupOrderAsc == group.Order { + return items[i].GetValue(group.Field).Number.Content < items[j].GetValue(group.Field).Number.Content + } + return items[i].GetValue(group.Field).Number.Content > items[j].GetValue(group.Field).Number.Content + }) + case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative: + sort.SliceStable(items, func(i, j int) bool { + if av.GroupOrderAsc == group.Order { + return items[i].GetValue(group.Field).Date.Content < items[j].GetValue(group.Field).Date.Content + } + return items[i].GetValue(group.Field).Date.Content > items[j].GetValue(group.Field).Date.Content + }) + } + + groupItemsMap := map[string][]av.Item{} + for _, item := range items { + value := item.GetValue(group.Field) + if value.IsEmpty() { + groupName = defaultGroupName + groupItemsMap[groupName] = append(groupItemsMap[groupName], item) + continue + } + + switch group.Method { + case av.GroupMethodValue: + groupName = value.String(false) + case av.GroupMethodRangeNum: + if group.Range.NumStart > value.Number.Content || group.Range.NumEnd < value.Number.Content { + groupName = notInRange + break + } + + for rangeEnd <= group.Range.NumEnd && rangeEnd < value.Number.Content { + rangeStart += group.Range.NumStep + rangeEnd += group.Range.NumStep + } + + if rangeStart <= value.Number.Content && rangeEnd >= value.Number.Content { + groupName = fmt.Sprintf("%s - %s", strconv.FormatFloat(rangeStart, 'f', -1, 64), strconv.FormatFloat(rangeEnd, 'f', -1, 64)) + } + case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative: + var contentTime time.Time + switch value.Type { + case av.KeyTypeDate: + contentTime = time.UnixMilli(value.Date.Content) + case av.KeyTypeCreated: + contentTime = time.UnixMilli(value.Created.Content) + case av.KeyTypeUpdated: + contentTime = time.UnixMilli(value.Updated.Content) + } + switch group.Method { + case av.GroupMethodDateDay: + groupName = contentTime.Format("2006-01-02") + case av.GroupMethodDateWeek: + year, week := contentTime.ISOWeek() + groupName = fmt.Sprintf("%d-W%02d", year, week) + case av.GroupMethodDateMonth: + groupName = contentTime.Format("2006-01") + case av.GroupMethodDateYear: + groupName = contentTime.Format("2006") + case av.GroupMethodDateRelative: + // 过去 30 天之前的按月分组 + // 过去 30 天、过去 7 天、昨天、今天、明天、未来 7 天、未来 30 天 + // 未来 30 天之后的按月分组 + now := time.Now() + if contentTime.Before(now.AddDate(0, 0, -30)) { + groupName = contentTime.Format("2006-01") + } else if contentTime.Before(now.AddDate(0, 0, -7)) { + groupName = fmt.Sprintf(Conf.language(259), 30) + } else if contentTime.Before(now.AddDate(0, 0, -1)) { + groupName = fmt.Sprintf(Conf.language(259), 7) + } else if contentTime.Equal(now.AddDate(0, 0, -1)) { + groupName = Conf.language(260) + } else if contentTime.Equal(now) { + groupName = Conf.language(261) + } else if contentTime.Equal(now.AddDate(0, 0, 1)) { + groupName = Conf.language(262) + } else if contentTime.Before(now.AddDate(0, 0, 7)) { + groupName = fmt.Sprintf(Conf.language(263), 7) + } else if contentTime.Before(now.AddDate(0, 0, 30)) { + groupName = fmt.Sprintf(Conf.language(263), 30) + } else { + groupName = contentTime.Format("2006-01") + } + } + } + groupItemsMap[groupName] = append(groupItemsMap[groupName], item) + } + + for name, groupItems := range groupItemsMap { + var v *av.View + switch view.LayoutType { + case av.LayoutTypeTable: + v = av.NewTableView() + v.Table = av.NewLayoutTable() + case av.LayoutTypeGallery: + v = av.NewGalleryView() + v.Gallery = av.NewLayoutGallery() + } + for _, item := range groupItems { + v.GroupItemIDs = append(v.GroupItemIDs, item.GetID()) + } + v.Name = name + view.Groups = append(view.Groups, v) + view.GroupDefault = name == defaultGroupName + } +} + func renderViewableInstance(viewable av.Viewable, view *av.View, attrView *av.AttributeView, page, pageSize int) (err error) { if nil == viewable { err = av.ErrViewNotFound From 91a4578859fa7cede66a541549eb802efa28e01f Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 16:04:11 +0800 Subject: [PATCH 3/9] :art: Database grouping by field https://github.com/siyuan-note/siyuan/issues/10964 --- kernel/model/attribute_view.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 4af1e28a6..3e152680a 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1359,18 +1359,12 @@ func genGroup(view *av.View, attrView *av.AttributeView) { group := view.Group view.Groups = nil - - const ( - defaultGroupName = "_@default@_" - notInRange = "_@notInRange@_" - ) - var groupName string viewable := sql.RenderView(attrView, view, "") - var items []av.Item for _, item := range viewable.(av.Collection).GetItems() { items = append(items, item) } + var rangeStart, rangeEnd float64 switch group.Method { case av.GroupMethodValue: @@ -1403,6 +1397,8 @@ func genGroup(view *av.View, attrView *av.AttributeView) { }) } + const defaultGroupName, notInRange = "_@default@_", "_@notInRange@_" + var groupName string groupItemsMap := map[string][]av.Item{} for _, item := range items { value := item.GetValue(group.Field) From 89dc7c62e71c779ad83bd9872f746712e2929312 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 16:33:37 +0800 Subject: [PATCH 4/9] :art: Database grouping by field https://github.com/siyuan-note/siyuan/issues/10964 --- app/appearance/langs/ar_SA.json | 3 ++- app/appearance/langs/de_DE.json | 3 ++- app/appearance/langs/en_US.json | 3 ++- app/appearance/langs/es_ES.json | 3 ++- app/appearance/langs/fr_FR.json | 3 ++- app/appearance/langs/he_IL.json | 3 ++- app/appearance/langs/it_IT.json | 3 ++- app/appearance/langs/ja_JP.json | 3 ++- app/appearance/langs/pl_PL.json | 3 ++- app/appearance/langs/pt_BR.json | 3 ++- app/appearance/langs/ru_RU.json | 3 ++- app/appearance/langs/zh_CHT.json | 3 ++- app/appearance/langs/zh_CN.json | 3 ++- kernel/av/av.go | 4 ---- kernel/av/layout.go | 16 +++++----------- kernel/model/attribute_view.go | 10 +++++++++- 16 files changed, 40 insertions(+), 29 deletions(-) diff --git a/app/appearance/langs/ar_SA.json b/app/appearance/langs/ar_SA.json index ea3c7594d..3aa9fc413 100644 --- a/app/appearance/langs/ar_SA.json +++ b/app/appearance/langs/ar_SA.json @@ -1632,6 +1632,7 @@ "260": "أمس", "261": "اليوم", "262": "غدًا", - "263": "التالي %d أيام" + "263": "التالي %d أيام", + "264": "الحقل [%s] فارغ" } } diff --git a/app/appearance/langs/de_DE.json b/app/appearance/langs/de_DE.json index de631d2c0..451775985 100644 --- a/app/appearance/langs/de_DE.json +++ b/app/appearance/langs/de_DE.json @@ -1632,6 +1632,7 @@ "260": "Gestern", "261": "Heute", "262": "Morgen", - "263": "Nächste %d Tage" + "263": "Nächste %d Tage", + "264": "Das Feld [%s] ist leer" } } diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index ff71310c3..8621420c6 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1632,6 +1632,7 @@ "260": "Yesterday", "261": "Today", "262": "Tomorrow", - "263": "Next %d days" + "263": "Next %d days", + "264": "Field [%s] is empty" } } diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index e219f4c59..ac80a2b70 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1632,6 +1632,7 @@ "260": "Ayer", "261": "Hoy", "262": "Mañana", - "263": "Próximos %d días" + "263": "Próximos %d días", + "264": "El campo [%s] está vacío" } } diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index 084b84c60..9ba4be4d2 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1632,6 +1632,7 @@ "260": "Hier", "261": "Aujourd'hui", "262": "Demain", - "263": "Les %d prochains jours" + "263": "Les %d prochains jours", + "264": "Le champ [%s] est vide" } } diff --git a/app/appearance/langs/he_IL.json b/app/appearance/langs/he_IL.json index 441cf2a88..9f9747bc5 100644 --- a/app/appearance/langs/he_IL.json +++ b/app/appearance/langs/he_IL.json @@ -1632,6 +1632,7 @@ "260": "אתמול", "261": "היום", "262": "מחר", - "263": "ה-%d ימים הבאים" + "263": "ה-%d ימים הבאים", + "264": "השדה [%s] ריק" } } diff --git a/app/appearance/langs/it_IT.json b/app/appearance/langs/it_IT.json index 1e5c829da..6e40d7a0e 100644 --- a/app/appearance/langs/it_IT.json +++ b/app/appearance/langs/it_IT.json @@ -1632,6 +1632,7 @@ "260": "Ieri", "261": "Oggi", "262": "Domani", - "263": "Prossimi %d giorni" + "263": "Prossimi %d giorni", + "264": "Il campo [%s] è vuoto" } } diff --git a/app/appearance/langs/ja_JP.json b/app/appearance/langs/ja_JP.json index 691cd0ee3..53c33aefd 100644 --- a/app/appearance/langs/ja_JP.json +++ b/app/appearance/langs/ja_JP.json @@ -1632,6 +1632,7 @@ "260": "昨日", "261": "今日", "262": "明日", - "263": "次の %d 日間" + "263": "次の %d 日間", + "264": "フィールド [%s] の値が空です" } } diff --git a/app/appearance/langs/pl_PL.json b/app/appearance/langs/pl_PL.json index 44437727f..3367fb288 100644 --- a/app/appearance/langs/pl_PL.json +++ b/app/appearance/langs/pl_PL.json @@ -1632,6 +1632,7 @@ "260": "Wczoraj", "261": "Dzisiaj", "262": "Jutro", - "263": "Następne %d dni" + "263": "Następne %d dni", + "264": "Pole [%s] jest puste" } } diff --git a/app/appearance/langs/pt_BR.json b/app/appearance/langs/pt_BR.json index 1a4279646..d719a82ca 100644 --- a/app/appearance/langs/pt_BR.json +++ b/app/appearance/langs/pt_BR.json @@ -1632,6 +1632,7 @@ "260": "Ontem", "261": "Hoje", "262": "Amanhã", - "263": "Próximos %d dias" + "263": "Próximos %d dias", + "264": "O campo [%s] está vazio" } } diff --git a/app/appearance/langs/ru_RU.json b/app/appearance/langs/ru_RU.json index 8836010fc..cfce581ad 100644 --- a/app/appearance/langs/ru_RU.json +++ b/app/appearance/langs/ru_RU.json @@ -1632,6 +1632,7 @@ "260": "Вчера", "261": "Сегодня", "262": "Завтра", - "263": "Следующие %d дней" + "263": "Следующие %d дней", + "264": "Поле [%s] имеет пустое значение" } } diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index 122758b09..1c241541b 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1632,6 +1632,7 @@ "260": "昨天", "261": "今天", "262": "明天", - "263": "未來 %d 天" + "263": "未來 %d 天", + "264": "字段 [%s] 值為空" } } diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index afb061186..d8b51a7b3 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1632,6 +1632,7 @@ "260": "昨天", "261": "今天", "262": "明天", - "263": "未来 %d 天" + "263": "未来 %d 天", + "264": "字段 [%s] 值为空" } } diff --git a/kernel/av/av.go b/kernel/av/av.go index ffbf76f69..580f836b9 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -199,7 +199,6 @@ type View struct { GroupFolded bool `json:"groupFolded,omitempty"` // 分组是否折叠 GroupHidden bool `json:"groupHidden,omitempty"` // 分组是否隐藏 GroupHideEmpty bool `json:"groupHideEmpty,omitempty"` // 分组是否隐藏空分组 - GroupDefault bool `json:"groupDefault,omitempty"` // 是否为默认分组 } // GroupCalc 描述了分组计算规则和结果的结构。 @@ -291,9 +290,6 @@ type Viewable interface { // SetGroupHidden 设置分组是否隐藏。 SetGroupHidden(hidden bool) - - // SetGroupDefault 设置分组是否为默认分组。 - SetGroupDefault(defaulted bool) } func NewAttributeView(id string) (ret *AttributeView) { diff --git a/kernel/av/layout.go b/kernel/av/layout.go index c81524167..c642dd8d3 100644 --- a/kernel/av/layout.go +++ b/kernel/av/layout.go @@ -66,12 +66,11 @@ type BaseInstance struct { Folded bool `json:"folded,omitempty"` // 是否折叠 Hidden bool `json:"hidden,omitempty"` // 是否隐藏 - Groups []Viewable `json:"groups,omitempty"` // 分组实例列表 - GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则和结果 - GroupName string `json:"groupName,omitempty"` // 分组名称 - GroupFolded bool `json:"groupFolded,omitempty"` // 分组是否折叠 - GroupHidden bool `json:"groupHidden,omitempty"` // 分组是否隐藏 - GroupDefault bool `json:"groupDefault,omitempty"` // 是否为默认分组 + Groups []Viewable `json:"groups,omitempty"` // 分组实例列表 + GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则和结果 + GroupName string `json:"groupName,omitempty"` // 分组名称 + GroupFolded bool `json:"groupFolded,omitempty"` // 分组是否折叠 + GroupHidden bool `json:"groupHidden,omitempty"` // 分组是否隐藏 } func NewViewBaseInstance(view *View) *BaseInstance { @@ -97,7 +96,6 @@ func NewViewBaseInstance(view *View) *BaseInstance { GroupName: view.GroupName, GroupFolded: view.GroupFolded, GroupHidden: view.GroupHidden, - GroupDefault: view.GroupDefault, ShowIcon: showIcon, WrapField: wrapField, } @@ -135,10 +133,6 @@ func (baseInstance *BaseInstance) SetGroupHidden(hidden bool) { baseInstance.GroupHidden = hidden } -func (baseInstance *BaseInstance) SetGroupDefault(defaulted bool) { - baseInstance.GroupDefault = defaulted -} - func (baseInstance *BaseInstance) GetID() string { return baseInstance.ID } diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 3e152680a..b8bc752c5 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1365,6 +1365,11 @@ func genGroup(view *av.View, attrView *av.AttributeView) { items = append(items, item) } + groupKey, _ := attrView.GetKey(group.Field) + if nil == groupKey { + return + } + var rangeStart, rangeEnd float64 switch group.Method { case av.GroupMethodValue: @@ -1487,9 +1492,12 @@ func genGroup(view *av.View, attrView *av.AttributeView) { for _, item := range groupItems { v.GroupItemIDs = append(v.GroupItemIDs, item.GetID()) } + + if defaultGroupName == name { + name = fmt.Sprintf(Conf.language(264), groupKey.Name) + } v.Name = name view.Groups = append(view.Groups, v) - view.GroupDefault = name == defaultGroupName } } From 4e6365fe77586d82c37adcf646d64d17eab43b92 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 16:35:26 +0800 Subject: [PATCH 5/9] :art: Database grouping by field https://github.com/siyuan-note/siyuan/issues/10964 --- kernel/model/attribute_view.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index b8bc752c5..a59cfa951 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1488,6 +1488,9 @@ func genGroup(view *av.View, attrView *av.AttributeView) { case av.LayoutTypeGallery: v = av.NewGalleryView() v.Gallery = av.NewLayoutGallery() + default: + logging.LogWarnf("unknown layout type [%s] for group view", view.LayoutType) + return } for _, item := range groupItems { v.GroupItemIDs = append(v.GroupItemIDs, item.GetID()) From e4fb89ee0e1979d249d6029d86896cc531cc9fa2 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 17:06:52 +0800 Subject: [PATCH 6/9] :art: Database grouping by field https://github.com/siyuan-note/siyuan/issues/10964 --- kernel/av/av.go | 1 + kernel/model/attribute_view.go | 63 ++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/kernel/av/av.go b/kernel/av/av.go index 580f836b9..b03672875 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -192,6 +192,7 @@ type View struct { ItemIDs []string `json:"itemIds,omitempty"` // 项目 ID 列表,用于维护所有项目 Group *ViewGroup `json:"group,omitempty"` // 分组规则 + GroupUpdated int64 `json:"groupUpdated"` // 分组规则更新时间戳 Groups []*View `json:"groups,omitempty"` // 分组视图列表 GroupItemIDs []string `json:"groupItemIds,omitempty"` // 分组项目 ID 列表,用于维护分组中的所有项目 GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则 diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index a59cfa951..12d8d0199 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -143,7 +143,7 @@ func SetAttributeViewGroup(avID, blockID string, group *av.ViewGroup) (err error } view.Group = group - err = av.SaveAttributeView(attrView) + genGroup(view, attrView) return err } @@ -1289,6 +1289,12 @@ func RenderAttributeView(avID, viewID, query string, page, pageSize int) (viewab return } +const ( + groupNameLast30Days, groupNameLast7Days = "_@last30Days@_", "_@last7Days@_" + groupNameYesterday, groupNameToday, groupNameTomorrow = "_@yesterday@_", "_@today@_", "_@tomorrow@_" + groupNameNext7Days, groupNameNext30Days = "_@next7Days@_", "_@next30Days@_" +) + func renderAttributeView(attrView *av.AttributeView, viewID, query string, page, pageSize int) (viewable av.Viewable, err error) { if 1 > len(attrView.Views) { view, _, _ := av.NewTableViewWithBlockKey(ast.NewNodeID()) @@ -1328,8 +1334,32 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page, return } - // 如果存在分组设置的话生成分组数据 - genGroup(view, attrView) + // 当前日期可能会变,所以如果是按日期分组则需要重新生成分组 + if isGroupByDate(view) { + updatedDate := time.UnixMilli(view.GroupUpdated).Format("2006-01-02") + if time.Now().Format("2006-01-02") != updatedDate { + genGroup(view, attrView) + } + + for _, groupView := range view.Groups { + switch groupView.Name { + case groupNameLast30Days: + groupView.Name = fmt.Sprintf(Conf.language(259), 30) + case groupNameLast7Days: + groupView.Name = fmt.Sprintf(Conf.language(259), 7) + case groupNameYesterday: + groupView.Name = Conf.language(260) + case groupNameToday: + groupView.Name = Conf.language(261) + case groupNameTomorrow: + groupView.Name = Conf.language(262) + case groupNameNext7Days: + groupView.Name = fmt.Sprintf(Conf.language(263), 7) + case groupNameNext30Days: + groupView.Name = fmt.Sprintf(Conf.language(263), 30) + } + } + } // 如果存在分组的话渲染分组视图视图 var groups []av.Viewable @@ -1458,19 +1488,19 @@ func genGroup(view *av.View, attrView *av.AttributeView) { if contentTime.Before(now.AddDate(0, 0, -30)) { groupName = contentTime.Format("2006-01") } else if contentTime.Before(now.AddDate(0, 0, -7)) { - groupName = fmt.Sprintf(Conf.language(259), 30) + groupName = groupNameLast30Days } else if contentTime.Before(now.AddDate(0, 0, -1)) { - groupName = fmt.Sprintf(Conf.language(259), 7) + groupName = groupNameLast7Days } else if contentTime.Equal(now.AddDate(0, 0, -1)) { - groupName = Conf.language(260) + groupName = groupNameYesterday } else if contentTime.Equal(now) { - groupName = Conf.language(261) + groupName = groupNameToday } else if contentTime.Equal(now.AddDate(0, 0, 1)) { - groupName = Conf.language(262) + groupName = groupNameTomorrow } else if contentTime.Before(now.AddDate(0, 0, 7)) { - groupName = fmt.Sprintf(Conf.language(263), 7) + groupName = groupNameNext7Days } else if contentTime.Before(now.AddDate(0, 0, 30)) { - groupName = fmt.Sprintf(Conf.language(263), 30) + groupName = groupNameNext30Days } else { groupName = contentTime.Format("2006-01") } @@ -1502,6 +1532,19 @@ func genGroup(view *av.View, attrView *av.AttributeView) { v.Name = name view.Groups = append(view.Groups, v) } + + if isGroupByDate(view) { + view.GroupUpdated = time.Now().UnixMilli() + } + + av.SaveAttributeView(attrView) +} + +func isGroupByDate(view *av.View) bool { + if nil == view.Group { + return false + } + return av.GroupMethodDateDay == view.Group.Method || av.GroupMethodDateWeek == view.Group.Method || av.GroupMethodDateMonth == view.Group.Method || av.GroupMethodDateYear == view.Group.Method || av.GroupMethodDateRelative == view.Group.Method } func renderViewableInstance(viewable av.Viewable, view *av.View, attrView *av.AttributeView, page, pageSize int) (err error) { From 03a10b7c88e71bd944ffc0b5a1f0575c8843e2cc Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 17:53:13 +0800 Subject: [PATCH 7/9] :art: Improve mirror databases refresh after updating database view layout https://github.com/siyuan-note/siyuan/issues/15233 --- kernel/model/attribute_view.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 12d8d0199..826ef3409 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -254,13 +254,26 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error continue } - node.AttributeViewType = string(view.LayoutType) + changed := false attrs := parse.IAL2Map(node.KramdownIAL) - attrs[av.NodeAttrView] = view.ID - err = setNodeAttrs(node, tree, attrs) - if err != nil { - logging.LogWarnf("set node [%s] attrs failed: %s", bID, err) - return + if blockID == bID { // 当前操作的镜像库 + attrs[av.NodeAttrView] = view.ID + node.AttributeViewType = string(view.LayoutType) + changed = true + } else { + if view.ID == attrs[av.NodeAttrView] { + // 仅更新和当前操作的镜像库指定的视图相同的镜像库 + node.AttributeViewType = string(view.LayoutType) + changed = true + } + } + + if changed { + err = setNodeAttrs(node, tree, attrs) + if err != nil { + logging.LogWarnf("set node [%s] attrs failed: %s", bID, err) + return + } } } From ed6a90ab8443ad061d276b3ae69207d42edce8a9 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 20:27:22 +0800 Subject: [PATCH 8/9] :art: Improve assets searching https://github.com/siyuan-note/siyuan/issues/15251 --- kernel/model/assets.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/kernel/model/assets.go b/kernel/model/assets.go index 2cba74ac0..d65e192ba 100644 --- a/kernel/model/assets.go +++ b/kernel/model/assets.go @@ -272,10 +272,10 @@ func NetAssets2LocalAssets(rootID string, onlyImg bool, originalURL string) (err func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) { ret = []*cache.Asset{} - keywords := strings.Split(keyword, " ") - + var keywords []string + keywords = append(keywords, keyword) + keywords = append(keywords, strings.Split(keyword, " ")...) pathHitCount := map[string]int{} - count := 0 filterByExt := 0 < len(exts) for _, asset := range cache.GetAssets() { if filterByExt { @@ -295,8 +295,18 @@ func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) { lowerHName := strings.ToLower(asset.HName) lowerPath := strings.ToLower(asset.Path) var hitNameCount, hitPathCount int - for _, k := range keywords { + for i, k := range keywords { lowerKeyword := strings.ToLower(k) + if 0 == i { + // 第一个是完全匹配,权重最高 + if strings.Contains(lowerHName, lowerKeyword) { + hitNameCount += 64 + } + if strings.Contains(lowerPath, lowerKeyword) { + hitPathCount += 64 + } + } + hitNameCount += strings.Count(lowerHName, lowerKeyword) hitPathCount += strings.Count(lowerPath, lowerKeyword) if 1 > hitNameCount && 1 > hitPathCount { @@ -318,10 +328,6 @@ func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) { Path: asset.Path, Updated: asset.Updated, }) - count++ - if Conf.Search.Limit <= count { - return - } } if 0 < len(pathHitCount) { @@ -333,6 +339,10 @@ func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) { return ret[i].Updated > ret[j].Updated }) } + + if Conf.Search.Limit <= len(ret) { + ret = ret[:Conf.Search.Limit] + } return } From bdc48a6d246e089e13657460d69b6b05e5e0cc77 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 8 Jul 2025 20:38:59 +0800 Subject: [PATCH 9/9] :bug: Virtual reference keyword exclusion list does not work in certain cases https://github.com/siyuan-note/siyuan/issues/15252 --- kernel/model/virutalref.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/model/virutalref.go b/kernel/model/virutalref.go index 88f7319b3..67a899028 100644 --- a/kernel/model/virutalref.go +++ b/kernel/model/virutalref.go @@ -252,12 +252,16 @@ func getVirtualRefKeywords(root *ast.Node) (ret []string) { if 0 < len(regexps) { tmp = nil for _, str := range ret { + matchExclude := false for _, re := range regexps { - if ok, regErr := regexp.MatchString(re, str); !ok && nil == regErr { - tmp = append(tmp, str) + if ok, _ := regexp.MatchString(re, str); ok { + matchExclude = true break } } + if !matchExclude { + tmp = append(tmp, str) + } } ret = tmp }