SiYuan Database: Template-Spalte (Referenz)
Diese Datei beschreibt, wie die Template-Spalte in SiYuan Databases (Attribute View / AV) funktioniert, welche Daten du im Template zur Verfuegung hast und welche Funktionen du benutzen kannst.
Quellcode-Referenzen:
- Rendering + Datenmodell:
kernel/sql/av.go(renderTemplateField) - Template-Funktionen (Sprig + SiYuan):
kernel/filesys/template.go(BuiltInTemplateFuncs) - SQL-Helferfunktionen:
kernel/sql/database.go(SQLTemplateFuncs)
1) Syntax (Go text/template mit .action{...})
Die Template-Spalte nutzt Go text/template, aber mit anderen Delimitern:
- Start:
.action{ - Ende:
}
Beispiele:
Titel: .action{ index . "Name" }
.action{ if gt (len (index . "Tags")) 0 }Tags: .action{ join ", " (index . "Tags") }.action{ end }
Wichtige Hinweise:
.(Dot) ist ein Map/Objekt (Datenmodell pro Zeile).- Feldzugriff (Identifier-Spaltenname): Wenn der Spaltenname ein gueltiger Go-Identifier ist (z.B.
Status,Cost,dueDate), dann.action{ .Status }. - Feldzugriff (beliebiger Spaltenname): Sonst (Leerzeichen, Sonderzeichen, Bindestriche), dann
.action{ index . "Spaltenname" }. - Variablen:
.action{ $x := index . "Preis" } ... .action{ $x } - Pipes:
.action{ index . "Tags" | join ", " | upper }
2) Datenmodell (Welche Daten sind in . enthalten?)
Die Template-Spalte wird pro Datenbank-Zeile gerendert. Im Template-Kontext (.) liegen:
2.1 IAL / Block-Attribute (Strings)
Alle IAL-Attribute des zugehoerigen Blocks werden in das Datenmodell kopiert (z.B. id).
Zusatz: Die Template-Spalte bietet ausserdem zwei eingebaute Zeitvariablen:
created(Typ:time.Time): Wird aus dem Blockidabgeleitet (erste 14 Zeichen alsYYYYMMDDhhmmss).updated(Typ:time.Time): Wird aus dem IAL-Attributupdatedgeparst.
Beispiel:
ID: .action{ .id }
Created: .action{ date "2006-01-02 15:04" .created }
Updated: .action{ date "2006-01-02 15:04" .updated }
2.2 Spaltenwerte nach Spaltennamen
Jede Spalte ist ueber ihren Spaltennamen im Datenmodell verfuegbar.
Die Typen werden je nach Spaltentyp gemappt (vereinfachte Uebersicht):
| Spaltentyp | Zugriff | Typ im Template |
|---|---|---|
| Number | index . "Amount" |
float64 |
| Date | index . "Due" |
time.Time (Start) |
| Date (Ende) | index . "Due_end" |
time.Time |
| Multi-Select | index . "Tags" |
[]string |
| Multi-Select (als String) | index . "Tags_str" |
string (gerendert wie UI) |
| Relation | index . "Rel" |
[]string (gerenderte Inhalte) |
| Rollup | index . "Rollup" |
[]float64 oder []string (je nach Inhalt) |
| Block/Primary Key | index . "Name" |
string (gerendert) |
| Sonstiges (Text, Select, Checkbox, URL, ... ) | index . "..." |
i.d.R. string (gerendert) |
Spezialfall Block/Primary Key:
entryCreated(Typ:time.Time) undentryUpdated(Typ:time.Time) werden gesetzt, wenn die Zeile einen Block-Wert hat.
2.3 Raw-Zugriff pro Spalte (*_raw)
Zu jeder Spalte existiert zusaetzlich ein Raw-Eintrag:
Spaltenname_rawenthaelt den Rohwert (AV Value Objekt), z.B.:
Type: .action{ (index . "Due_raw").Type }
Raw: .action{ printf "%#v" (index . "Due_raw") }
2.4 Zugriff nach Spalten-ID (id_mod, id_mod_raw)
Zusatzmaps:
id_mod: Map vonSpaltenID -> gemappter Wert(wie bei Spaltennamen)id_mod_raw: Map vonSpaltenID -> Raw-Value
Beispiel:
.action{ $byID := index . "id_mod" }
.action{ index $byID "20240101010101-abcdefg" }
3) Funktionsuebersicht
Die Template-Spalte hat:
- Go
text/templateBuilt-ins - Sprig v3 (
sprig.TxtFuncMap()), mit wenigen entfernten Funktionen - SiYuan Custom-Funktionen (zusaetzlich zu Sprig)
- SQL-Helferfunktionen (SiYuan)
3.1 Go text/template Built-in Funktionen
Diese Funktionen sind immer verfuegbar (Go text/template):
andornotcallhtmlindexjslenprintprintfprintlnurlqueryeqneltlegtge
Wichtige Actions (keine "Funktionen", aber Teil der Sprache):
if,else,endrange,with- Variablen:
$x := ...
3.2 SiYuan zusaetzliche Funktionen (ueber Sprig hinaus)
Aus kernel/filesys/template.go:
Weekday(date time.Time) int(Sonntag=0 ... Samstag=6)WeekdayCN(date time.Time) string(weekday als chinesisches Zeichen, Variante 1)WeekdayCN2(date time.Time) string(weekday als chinesisches Zeichen, Variante 2)ISOWeek(date time.Time) intISOYear(date time.Time) intISOMonth(date time.Time) intISOWeekDate(day int, date time.Time) time.Time(day: Montag=1 ... Sonntag=7)pow(a, b any) int64powf(a, b any) float64log(a, b any) int64(Logarithmus vonazur Basisb)logf(a, b any) float64parseTime(dateStr string) time.Time(parst viele Datumsformate; bei Fehler:now)FormatFloat(format string, n float64) string(z.B.FormatFloat "#,###.##" 1234.56)getHPathByID(id string) string(liefert HPath eines Blocks)statBlock(id string) *BlockStatResult(Rueckgabe-Felder:RuneCount,WordCount,LinkCount,ImageCount,RefCount,BlockCount)runeCount(s string) intwordCount(s string) intmarkdown2text(md string) string(Markdown -> Text)markdown2content(md string) string(Markdown -> Content)
3.3 SQL-Funktionen (SiYuan)
Aus kernel/sql/database.go:
querySQL(stmt string) []map[string]any: Fuehrt SQL aus und gibt Zeilen als Maps zurueck (Limit intern).queryBlocks(stmt string, args ...string) []*Block: Ersetzt?im SQL-String nacheinander durchargs(String-Replacement) und laedt Blocks.querySpans(stmt string, args ...string) []*Span: WiequeryBlocks, aber fuer Spans.getBlock(arg any) *Block:argkannstring(Block-ID) odermapmit Keyidsein.
Performance-Hinweis: SQL-Funktionen laufen beim Rendering der Template-Spalte (potenziell pro Zeile). Nutze sie sparsam.
3.4 Sprig (Masterminds/sprig v3) Funktionen
SiYuan nutzt sprig.TxtFuncMap() (Sprig v3.3.0 laut kernel/go.mod), mit entfernten Funktionen aus Sicherheitsgruenden:
- Entfernt:
env,expandenv,getHostByName
Sprig-Funktionen (nach Kategorie, Funktionsnamen):
Strings
trimtrimAlltrimSuffixtrimPrefixupperlowertitleuntitlerepeatsubstrnospacetruncabbrevabbrevbothinitialsrandAlphaNumrandAlpharandNumericrandAsciiwrapwrapWithcontainshasPrefixhasSuffixquotesquotecatindentnindentreplacepluralsnakecasecamelcasekebabcaseswapcaseshuffleregexMatchmustRegexMatchregexFindAllmustRegexFindAllregexFindmustRegexFindregexReplaceAllmustRegexReplaceAllregexReplaceAllLiteralmustRegexReplaceAllLiteralregexSplitmustRegexSplitregexQuoteMeta
String Slices
joinsplitListsplitsplitnsortAlpha
Lists
listfirstmustFirstrestmustRestlastmustLastinitialmustInitialappendmustAppendprependmustPrependconcatreversemustReverseuniqmustUniqwithoutmustWithouthasmustHascompactmustCompactslicemustSlicechunk
Dicts
dictgetsetunsethasKeypluckdigmergemustMergemergeOverwritemustMergeOverwritekeyspickomitvaluesdeepCopymustDeepCopy
Defaults / JSON / Ternary
defaultemptycoalesceallanyfromJsonmustFromJsontoJsonmustToJsontoPrettyJsonmustToPrettyJsontoRawJsonmustToRawJsonternary
Type Conversion
atoifloat64intint64toDecimaltoStringtoStrings
Encoding
b64encb64decb32encb32dec
Paths
basedircleanextisAbsosBaseosDirosCleanosExtosIsAbs
Math (Integer)
addadd1subdivmodmulmaxminfloorceilroundrandInt
Math (Float)
addfadd1fsubfdivfmulfmaxfminf
Integer Slices
untiluntilStepseq
Date
nowagodatedateInZonedurationdurationRoundunixEpochdateModifymustDateModify
Hinweis: In Sprig-Beispielen taucht teils auch date_modify auf. Falls dateModify nicht funktioniert, probiere date_modify.
Flow Control
fail
UUID
uuidv4
OS (in SiYuan entfernt)
env(entfernt)expandenv(entfernt)
SemVer
semversemverCompare
Reflection
kindOfkindIstypeOftypeIstypeIsLikedeepEqual
Network (in SiYuan entfernt)
getHostByName(entfernt)
URL
urlParseurlJoin
Crypto
sha1sumsha256sumsha512sumadler32sumbcrypthtpasswdrandBytesderivePasswordgenPrivateKeybuildCustomCertgenCAgenCAWithKeygenSelfSignedCertgenSelfSignedCertWithKeygenSignedCertgenSignedCertWithKeyencryptAESdecryptAES
4) Praktische Beispiele (inkl. Bedingungen)
4.1 if / else if / else (Bedingungen)
.action{ $status := index . "Status" }
.action{ if eq $status "Done" }Fertig
.action{ else if eq $status "Doing" }In Arbeit
.action{ else }Offen
.action{ end }
Vergleiche (Go Built-ins): eq, ne, lt, le, gt, ge
.action{ if gt (index . "Amount") 0 }Positiv.action{ end }
Logik (Go Built-ins): and, or, not
.action{ if and (ge (index . "Progress") 0.8) (not (eq (index . "Status") "Done")) }Fast fertig.action{ end }
4.2 "case/switch" nachbauen
Go Templates haben kein echtes switch/case. Uebliche Patterns:
Pattern A: else if Kette (siehe oben).
Pattern B: Mapping via dict + get + default (Sprig):
.action{ $map := dict "Done" "Fertig" "Doing" "In Arbeit" "Open" "Offen" }
.action{ $status := index . "Status" }
.action{ default "Unbekannt" (get $map $status) }
Pattern C: kurze Ja/Nein Logik via ternary (Sprig):
.action{ ternary "Hat Tags" "Keine Tags" (gt (len (default (list) (index . "Tags"))) 0) }
4.3 Umgang mit leeren / fehlenden Werten
Fehlende Keys liefern im Template oft <no value>. Diese Ausgabe wird von SiYuan am Ende zu leerem Text gemacht, aber fuer Bedingungen ist es besser, aktiv zu "normalisieren".
default (Sprig) ist dafuer praktisch:
Name: .action{ default "-" (index . "Name") }
empty (Sprig) prueft "Leerheit" (nil, "", 0, leere Liste, ...):
.action{ if empty (index . "Tags") }Keine Tags.action{ else }Tags: .action{ join ", " (index . "Tags") }.action{ end }
4.4 with (Scope + Null-Check)
with setzt den Dot-Kontext und laeuft nur, wenn der Ausdruck nicht "empty" ist:
.action{ with index . "Due" }Faellig: .action{ date "2006-01-02" . }.action{ else }Keine Deadline.action{ end }
4.5 range (Schleifen)
Ueber eine Multi-Select Liste iterieren:
Tags:
.action{ range $i, $t := index . "Tags" }- .action{ $t }
.action{ else }(keine)
.action{ end }
Ueber SQL-Ergebnis iterieren (siehe querySQL unten):
.action{ range $row := querySQL "select type, count(*) as c from blocks group by type limit 5" }
.action{ index $row "type" }: .action{ index $row "c" }
.action{ end }
Ueber Dict/Map iterieren:
.action{ $m := dict "a" 1 "b" 2 }
.action{ range $k, $v := $m }.action{ $k }=.action{ $v } .action{ end }
Kommentar (Go Template):
.action{/* das wird ignoriert */}
4.6 define/template (wiederverwendbare Snippets)
Du kannst kleine Hilfs-Templates definieren und spaeter wiederverwenden:
.action{ define "tags" }Tags: .action{ join ", " (default (list) (index . "Tags")) }.action{ end }
.action{ template "tags" . }
4.7 Debugging / Guardrails
printf (Go) um Typen/Strukturen zu inspizieren:
.action{ printf "%T" (index . "Tags") }
Weitere Go Built-ins, die manchmal nuetzlich sind:
Query: .action{ urlquery "a b&c" }
Escaped HTML: .action{ html "<b>x</b>" }
Print: .action{ print "A=" (index . "Amount") }
fail (Sprig) um bewusst Fehler zu werfen (z.B. beim Bauen eines Templates):
.action{ if empty (index . "Name") } .action{ fail "Name ist Pflicht" } .action{ end }
4.8 Beispiele fuer haeufige Aufgaben
Multi-Select als Text:
.action{ join ", " (default (list) (index . "Tags")) }
Wenn leer, dann "-":
.action{ ternary (join ", " (default (list) (index . "Tags"))) "-" (gt (len (default (list) (index . "Tags"))) 0) }
Date formatieren:
.action{ with index . "Due" } .action{ date "2006-01-02" . } .action{ end }
Weekday / ISO-Woche (SiYuan-Funktionen):
.action{ with index . "Due" }Wochentag: .action{ Weekday . }, ISO-Woche: .action{ ISOWeek . }.action{ end }
Block-Statistik (SiYuan-Funktion):
.action{ $s := statBlock .id }Words: .action{ $s.WordCount }, Links: .action{ $s.LinkCount }
Markdown in Text umwandeln (SiYuan-Funktion):
.action{ markdown2text (default "" (index . "Notes")) }
SQL: Ein Feld aus dem ersten Ergebnis lesen (Sprig first + index):
.action{ $rows := querySQL "select count(*) as c from blocks" }
.action{ index (first $rows) "c" }
4.9 Bedingungen Cheatsheet (Funktionen)
Vergleich + Logik (Go Built-ins):
.action{ $a := default 0 (index . "A") }
.action{ $b := default 0 (index . "B") }
.action{ if eq $a $b }gleich.action{ end }
.action{ if ne $a $b }ungleich.action{ end }
.action{ if lt $a $b }a<b.action{ end }
.action{ if le $a $b }a<=b.action{ end }
.action{ if gt $a $b }a>b.action{ end }
.action{ if ge $a $b }a>=b.action{ end }
.action{ if and (gt $a 0) (lt $a 10) }1..9.action{ end }
.action{ if or (empty (index . "Due")) (empty (index . "Status")) }unvollstaendig.action{ end }
.action{ if not (empty (index . "Name")) }hat Name.action{ end }
Sprig-Helper, die bei Bedingungen oft helfen:
.action{ default "-" (index . "Name") }
.action{ coalesce (index . "Name") (index . "Title") "-" }
.action{ ternary "Ja" "Nein" (gt (len (default (list) (index . "Tags"))) 0) }
.action{ if has "Urgent" (default (list) (index . "Tags")) }Hat Tag Urgent.action{ end }
.action{ if hasKey . "Status" }Spalte Status existiert.action{ end }
.action{ if contains "TODO" (default "" (index . "Notes")) }enthaelt TODO.action{ end }
.action{ if regexMatch "^https?://" (default "" (index . "URL")) }ist URL.action{ end }
No comments to display
No comments to display