Filter-Ausdrücke

Filter-Ausdrücke

Ein Filter-Ausdruck ist eine kleine Suchanfrage, mit der du die Notizen des aktuellen Kontexts durchforstest und auswertest. Du kannst damit alle Zeilen finden, die zu einer Bedingung passen — etwa „alle offenen Aufgaben, die diese Woche fällig sind" — oder eine Kennzahl berechnen, etwa „wie viele Aufgaben sind erledigt?" oder „was ist die Summe aller Beträge?". Die Sprache dahinter ist bewusst knapp gehalten, folgt aber festen Regeln. Dieser Text erklärt diese Regeln Schritt für Schritt und zeigt am Ende, wie die mitgelieferten Standard-Filter aufgebaut sind.

Lies den Text ruhig einmal von oben nach unten: Jeder Abschnitt baut auf dem vorigen auf. Wenn du die Grundbausteine — Hashtags, Verknüpfungen und Werte — verstanden hast, ergibt sich der Rest fast von selbst.

Was ausgewertet wird: Zeilen

Die kleinste Einheit einer Auswertung ist die einzelne Textzeile einer Notiz, nicht die Notiz als Ganzes. Jede Notiz wird dazu in ihre Zeilen zerlegt, und jede Bedingung wird für jede Zeile einzeln geprüft. Ein Prädikat „trifft zu" für eine Zeile oder eben nicht. Liefert ein Filter-Ausdruck am Ende eine Trefferliste, so besteht diese aus genau den Zeilen, für die die Bedingung zutrifft — die jüngste zuerst. Ein Tipp auf eine Trefferzeile öffnet die zugehörige Notiz an genau dieser Stelle.

Ausgewertet werden immer nur die Notizen des Kontexts, in dem du gerade bist (die Modul-Notizen bzw. die Notizen eines Timeslots).

Hashtags und Attribute

Angelpunkt fast jeder Auswertung sind die Hashtags in deinen Notizen — Wörter mit vorangestelltem #, etwa #todo oder #done (Umlaute sind erlaubt). Optional kann direkt dahinter in Klammern ein Attribut stehen, ein Zusatzwert: #todo(d251231), #amount(-49.90) oder #priorität(hoch). Der Klammer-Inhalt bleibt zunächst roher Text; erst im Vergleich wird er je nach Bedarf als Zahl oder als Datum gedeutet.

Im Filter-Ausdruck schreibst du ein Hashtag genauso wie in der Notiz, aber ohne die Klammer: Du beziehst dich mit #todo auf alle Vorkommen dieses Namens und vergleichst deren Attribut dann gesondert. Die Schreibweise des Namens wird dabei genau genommen — #todo und #Todo gelten als verschiedene Hashtags.

Das Grundprinzip: Prädikate und Werte

Es gibt in der Sprache zwei grundverschiedene Arten von Ausdrücken, und es lohnt sich, sie sauber auseinanderzuhalten:

Ein Prädikat ist eine Ja/Nein-Bedingung an eine Zeile. #todo ist ein Prädikat („enthält diese Zeile das Hashtag #todo?"), ebenso #todo <= :today oder eine Verknüpfung mehrerer solcher Bedingungen. Ein Prädikat liefert als Ergebnis eine Liste von Zeilen.

Ein Wert ist dagegen ein einzelner Datenpunkt, der rechts von einem Vergleich steht: eine Zahl wie 15, ein Datum wie d251231 oder :today, oder ein Text wie "dringend". Werte stehen nie allein, sondern immer als rechte Seite eines Vergleichs.

Die Aggregat-Funktionen (weiter unten) bilden eine dritte Kategorie: Sie fassen viele Zeilen zu einer einzigen Zahl zusammen und liefern statt einer Trefferliste ein Rechenergebnis.

Wichtig ist die Trennung, weil manche Stellen zwingend ein Prädikat verlangen: and, or, not und die where-Klausel verknüpfen ausschließlich Prädikate, niemals nackte Werte oder Zahlen. Ein Ausdruck wie #done and 5 ist deshalb ungültig.

Hashtags als einfachstes Prädikat

Der einfachste Filter-Ausdruck ist ein einzelnes Hashtag: #todo findet alle Zeilen, in denen dieses Hashtag vorkommt — unabhängig davon, ob es ein Attribut trägt oder nicht. Ob #todo oder #todo(morgen) in der Zeile steht, spielt für dieses reine Existenz-Prädikat keine Rolle.

Logische Verknüpfung: and, or, not

Mehrere Prädikate lassen sich zu einer zusammengesetzten Bedingung verknüpfen. #todo and #dringend trifft nur zu, wenn beide Hashtags in derselben Zeile stehen; #done or #cancelled trifft zu, wenn mindestens eines von beiden vorkommt. Der Präfix-Operator not kehrt eine Bedingung um: not #done findet alle Zeilen ohne dieses Hashtag.

Wie in der Mathematik gibt es eine feste Rangfolge (Präzedenz): not bindet am stärksten, dann and, am schwächsten or. Ohne Klammern wird also not #a and #b als (not #a) and #b gelesen und #a and #b or #c als (#a and #b) or #c. Mit runden Klammern ( … ) steuerst du die Gruppierung selbst und machst den Ausdruck zugleich lesbarer: (#todo or #due) and not #done ist etwas ganz anderes als #todo or (#due and not #done). Im Zweifel lieber eine Klammer mehr setzen.

Die Schlüsselwörter and, or, not (und where) darfst du in beliebiger Groß-/Kleinschreibung schreiben.

Vergleiche

Um nicht nur die Existenz eines Hashtags zu prüfen, sondern seinen Attributwert, dient ein Vergleich. Links steht immer ein Hashtag, in der Mitte ein Vergleichsoperator, rechts ein Wert: #amount > 100, #todo <= :today, #priorität = "hoch". Links von einem Vergleich muss ein Hashtag stehen — Konstruktionen wie 5 < #a sind nicht erlaubt.

Es gibt sechs Operatoren: = (gleich), != (ungleich), < (kleiner), > (größer), <= (kleiner oder gleich) und >= (größer oder gleich). Vergleiche binden stärker als and/or, sodass #a < 15 and #b > 2 ganz natürlich als (#a < 15) and (#b > 2) gruppiert.

Ein Vergleich ist existenziell gemeint: Er trifft für eine Zeile zu, sobald mindestens ein gleichnamiges Hashtag ein passendes Attribut mit passendem Wert besitzt. Ein Hashtag ohne Attribut oder mit einem Attribut, das sich nicht zum verglichenen Werttyp deuten lässt (etwa Text, wo eine Zahl erwartet wird), trägt einfach nicht bei — er verursacht keinen Fehler, sondern zählt schlicht nicht als Treffer.

Werte: Zahlen

Ein Zahl-Wert ist eine ganze oder eine Kommazahl, auch negativ: 15, 3.14, -42.90. Als Dezimaltrennzeichen dient stets der Punkt. #amount >= 0 findet Zeilen mit einem nicht-negativen Betrag, #dauer < 30 solche mit einer Dauer unter dreißig.

Werte: Datum

Datumsangaben schreibst du als Datums-Literal: der Buchstabe d gefolgt von reinen Ziffern. Genau drei Längen sind zulässig — vier, sechs oder acht Ziffern: d1231 bedeutet den 31.12. des laufenden Jahres (Format mmtt), d251231 den 31.12.2025 (jjmmtt) und d20251231 denselben Tag vollständig ausgeschrieben (jjjjmmtt). Bei der zweistelligen Jahreszahl liegt das Jahr in einem gleitenden Fenster um die Gegenwart, sodass nahe Jahre wie erwartet ins 21. Jahrhundert fallen. Unmögliche Kalendertage (etwa der 30.02.) werden abgewiesen.

Damit ein Vergleich greift, muss auch das Attribut in der Notiz ein Datum in derselben d-Schreibweise sein, also z. B. #todo(d251231). Optional darf nach einem einzelnen Leerzeichen eine Uhrzeit folgen, im Format hmm oder hhmm — Stunden und Minuten, ohne Sekunden: d251231 930 meint 9:30 an jenem Tag, d20251231 1700 meint 17:00. Dasselbe gilt für das Attribut in der Notiz, etwa #todo(d251231 1700). Die Stunde reicht von 0 bis 23, die Minute von 0 bis 59.

Der Vergleich erfolgt auf der gröberen der beiden Auflösungen: Nur wenn beide Seiten — das Attribut und der Vergleichswert — eine Uhrzeit tragen, wird minutengenau verglichen. Fehlt sie auf einer Seite, werden beide auf den Tagesbeginn reduziert und tagesweise verglichen, genau wie bisher. So findet #todo = d251231 auch ein Attribut #todo(d251231 1700) (derselbe Tag), während #todo = d251231 1700 genau diese Minute verlangt.

Werte: Datums-Variablen

Statt eines festen Datums kannst du eine Variable einsetzen, die sich zur Auswertungszeit auflöst — praktisch für Filter, die dauerhaft „mitwandern" sollen. Eine Variable beginnt mit : und meint — bis auf :now — stets einen konkreten Tag: :today (heute), :yesterday (gestern), :tomorrow (morgen), :startofweek und :endofweek (erster bzw. letzter Tag der laufenden Woche), :startofmonth und :endofmonth, sowie :startofyear und :endofyear. Der Wochenbeginn richtet sich nach deinen Regions-Einstellungen. So findet #todo <= :endofweek alles, was bis zum Ende dieser Woche fällig ist, ganz gleich, welcher Tag gerade ist.

Die besondere Variable :now löst sich auf heute inklusive der aktuellen Uhrzeit (minutengenau) auf, statt auf den Tagesbeginn. Zusammen mit einer Uhrzeit am Attribut ermöglicht sie minutengenaue Filter: #todo <= :now findet alles, was bis zu dieser Minute fällig ist, und #meeting >= :now die heute noch bevorstehenden Termine.

Werte: Wiederholung

Neben einem festen Datum kannst du auch eine Wiederholungs-Regel angeben — ein lesbares Muster, das sich regelmäßig wiederholt. Eine Regel beginnt mit dem Wort every, gefolgt von einer Tagesangabe und optional einer Uhrzeit-Angabe nach at. Beispiele: every day at 0900 (täglich um 9:00), every 15 at 0900 (an jedem 15. eines Monats), every mon,wed at 0900,1800 (montags und mittwochs, jeweils um 9:00 und 18:00) und every 1st,3rd mon at 0900 (am ersten und dritten Montag im Monat). Die Uhrzeiten schreibst du wie beim Datum als hmm oder hhmm.

Die Tagesangabe kennt vier Formen: das Wort day für jeden Tag; eine Liste von Monatstagen wie 15 oder 1,15; eine Liste von Wochentagen aus mon, tue, wed, thu, fri, sat, sun; und n-te Wochentage im Monat, indem du den Wochentagen ein Ordinal 1st, 2nd, 3rd, 4th, 5th oder last voranstellst — etwa every last fri at 1700 für den letzten Freitag im Monat. Mehrere Angaben trennst du mit Komma; die Groß-/Kleinschreibung spielt keine Rolle.

Als Hashtag-Attribut in einer Notiz erzeugt eine Regel wiederkehrende Mitteilungen — eine Uhrzeit (at …) ist dafür Pflicht, genau wie bei einem einzelnen Datum. #erinnerung(every mon,wed at 0900,1800) meldet sich also viermal pro Woche. Eine Regel ohne Uhrzeit ist ein reines Metadatum und löst keine Mitteilung aus.

Im Vergleich prüft eine Regel, ob ein Datum auf ihr Muster passt. Weil ein Muster keine Ordnung hat, sind nur = und != erlaubt: #termin = every 1st mon findet die Termine, die auf einen ersten Montag fallen, #termin != every sat,sun alles außerhalb des Wochenendes. Trägt sowohl die Regel als auch das Attribut eine Uhrzeit, muss auch diese übereinstimmen; fehlt sie auf einer Seite, zählt nur der Tag.

Werte: Text und reguläre Ausdrücke

Ein Text-Wert steht in doppelten Anführungszeichen: "hoch". Seine Deutung hängt vom Operator ab. Bei = und != wird der Text als regulärer Ausdruck (Regex) verstanden und ohne Beachtung der Groß-/Kleinschreibung gegen das Attribut geprüft — ein Teiltreffer genügt. #status = "wart" fände so auch ein Attribut abwartend. Bei den Ordnungs-Operatoren <, >, <=, >= wird derselbe Text hingegen wörtlich genommen und alphabetisch (lexikografisch) mit dem Attribut verglichen.

Innerhalb der Anführungszeichen gibt es keine Sonderbehandlung für Backslashes; Regex-Klassen wie \d (Ziffer) oder \w (Wortzeichen) kommen also unverändert an. Ein " selbst kann im Muster nicht vorkommen. Ein fehlerhaftes Regex-Muster wird beim Abschicken als Fehler gemeldet, statt still nichts zu finden. Die Filterzeile wandelt außerdem typografische Anführungszeichen automatisch in gerade " um.

Textzeilen durchsuchen ohne Hashtag

Steht ein Text-Literal allein da — ohne Hashtag und Operator davor —, wird es zu einem Prädikat, das den gesamten Zeilentext gegen den regulären Ausdruck prüft (unverankert, Groß-/Kleinschreibung egal). "Rechnung" findet damit jede Zeile, in der dieses Wort irgendwo vorkommt, unabhängig von Hashtags. Das lässt sich frei mit den übrigen Prädikaten kombinieren, etwa "Miete" and #done.

Zeilengruppen und Einrückung: partof

Planungsnotizen sind oft eingerückt gegliedert: Eine weniger stark eingerückte Zeile bildet die Überschrift einer Gruppe, die stärker eingerückten Zeilen darunter sind ihre Mitglieder. Eine Zeile gehört dabei zu jeder Überschrift, die in der Einrückung über ihr steht — also nicht nur zur unmittelbar übergeordneten, sondern zu allen übergeordneten Ebenen bis nach ganz oben. Die Zugehörigkeit bleibt innerhalb einer Notiz; über Notizgrenzen hinweg gibt es keine Gruppen.

Das Präfix-Prädikat partof wählt Zeilen anhand ihrer Überschrift aus: partof <Bedingung> trifft für eine Zeile zu, wenn mindestens eine ihrer (übergeordneten) Gruppenüberschriften die Bedingung erfüllt. Als Bedingung darf alles stehen, was auch sonst ein Prädikat ist — ein Hashtag, ein Text-Muster oder ein ganzes geklammertes Prädikat: partof #head findet die Zeilen, deren Überschrift das Hashtag #head trägt; partof "Projekt" die Zeilen, deren Überschrift den regulären Ausdruck Projekt enthält (unverankert, Groß-/Kleinschreibung egal, genau wie ein nacktes Text-Muster); partof (#todo and not #done) die Zeilen, deren Überschrift #todo, aber nicht #done trägt.

Da partof genau eine solche Bedingung an sich bindet, lässt es sich frei mit den übrigen Prädikaten verknüpfen: partof "Projekt" and #done wird als (partof "Projekt") and #done gelesen und findet die #done-Zeilen innerhalb der Projekt-Gruppen. Ebenso funktioniert es in Aggregaten, etwa count(#done where (#todo or #due) and partof "Projekt"). Die Überschriftszeile selbst gehört nicht zu ihrer Gruppe und erscheint daher nicht in der Trefferliste einer reinen partof-Bedingung.

Attribut vorhanden: hasAttr

Manchmal willst du nur wissen, ob ein Hashtag überhaupt ein Attribut trägt, ohne dessen Wert zu vergleichen. Dafür gibt es die Funktion hasAttr(#tag): Sie trifft für eine Zeile zu, wenn dort ein gleichnamiges Hashtag mit einem Attribut steht. hasAttr(#todo) findet also alle Zeilen mit einem gesetzten Fälligkeitsdatum, ganz gleich welchem.

Aggregat-Funktionen: eine Zahl statt einer Liste

Die bisherigen Ausdrücke liefern Zeilen. Die Aggregat-Funktionen dagegen rechnen über viele Zeilen hinweg und liefern eine einzelne Zahl, die in der Ergebnisliste als Wert erscheint. Es gibt fünf davon.

count( … ) zählt. Übergibst du ein einzelnes Hashtag, so werden alle Vorkommen dieses Namens gezählt — kommt es in einer Zeile zweimal vor, zählt es auch zweimal: count(#todo). Übergibst du dagegen ein zusammengesetztes Prädikat, so werden die Zeilen gezählt, die es erfüllen: count(#done or #cancelled) liefert die Anzahl der erledigten oder abgebrochenen Aufgaben-Zeilen.

sum(#tag) bildet die Summe der numerischen Attribute aller gleichnamigen Hashtags, avg(#tag) deren Durchschnitt (auf zwei Nachkommastellen gerundet), max(#tag) den größten und min(#tag) den kleinsten Attributwert. Attribute, die sich nicht als Zahl deuten lassen, gehen dabei nicht ein. Ist gar kein passender Wert vorhanden, ergeben diese Funktionen 0.

Die where-Klausel in Aggregaten

Alle Aggregate außer hasAttr lassen sich mit einer nachgestellten where-Klausel auf bestimmte Zeilen einschränken. Nach where folgt ein beliebiges Prädikat, und gerechnet wird nur über die Zeilen, die es erfüllen: sum(#amount where #todo <= :today) summiert nur die Beträge, deren Fälligkeit heute oder früher liegt. Auch count(#todo where #dringend) ist möglich — es zählt die #todo-Vorkommen in dringend markierten Zeilen.

Prädikat oder Zahl — was das Ergebnis bestimmt

Ob ein Filter-Ausdruck eine Trefferliste oder eine Zahl liefert, entscheidet allein seine oberste Ebene: Steht dort ein Prädikat, bekommst du Zeilen; steht dort eine Aggregat-Funktion, bekommst du eine Zahl. Beides lässt sich nicht mischen. Eine Aggregat-Funktion darf deshalb nicht als Operand von and, or, not oder in einer where-Klausel auftauchen — dort werden ausschließlich Prädikate erwartet. Umgekehrt darfst du ein Prädikat aber problemlos innerhalb eines Aggregats verwenden (als Argument von count oder in einer where-Klausel). Passt der Ausdruck nicht zusammen, meldet die Auswertung einen klaren Fehler, statt ein irreführendes Ergebnis zu liefern.

Die Standard-Filter als Beispiele

Die mitgelieferten Standard-Filter zeigen die Sprache im Zusammenspiel. Es lohnt sich, sie als Vorlage für eigene Ausdrücke zu lesen.

Fällige Aufgaben: (#due <= :today or #todo) and not (#done or #cancelled). Gefunden werden Zeilen, die entweder ein bis heute fälliges #due-Datum oder überhaupt ein #todo tragen — und die zugleich weder als #done noch als #cancelled markiert sind. Schön zu sehen ist hier das Zusammenspiel von Klammern, or, and und not.

Fällige Aufgaben (heute): #due = :today and not (#done or #cancelled). Statt „bis heute" (<=) wird hier auf genau den heutigen Tag geprüft (=).

Fällige Aufgaben (diese Woche): #due <= :endofweek and not (#done or #cancelled). Die Variable :endofweek verschiebt die Grenze automatisch auf das Wochenende.

Offene Aufgaben: #todo and not (#done or #cancelled). Alle noch nicht abgeschlossenen Aufgaben, unabhängig von einem Datum.

Offene Aufgaben (Anzahl): count(#todo and not (#done or #cancelled)). Derselbe Prädikat-Ausdruck, aber in count( … ) gehüllt — er liefert nun die Anzahl der Zeilen statt der Zeilen selbst.

Erledigte Aufgaben: #done or #cancelled und als Zählung Erledigte Aufgaben (Anzahl): count(#done or #cancelled). Dasselbe Muster: einmal als Liste, einmal als Zahl.

Kontostand (heute): sum(#amount where #todo <= :today). Die Summe aller #amount-Beträge, deren #todo-Datum heute oder früher liegt — ein kleines laufendes Konto. Kontostand (Ende des Monats): sum(#amount where #todo <= :endofmonth) erweitert den Zeitraum bis zum Monatsende.

Aus diesen Bausteinen lässt sich fast jede eigene Auswertung zusammensetzen. Häufig gebrauchte Ausdrücke kannst du als benannte Filter dauerhaft sichern — mehr dazu in der Hilfe zum Notiz-Bereich.