Blog der SHI GmbH

Document Expiration

Wie lange „lebt“ ein Dokument? Oder: Wie man ein Ablaufdatum an Inhalte heftet

Seit Solr 4.8 gibt es ein Feature, das aktuell noch nicht besonders dokumentiert und sich daher auch noch nicht weiter Verbreitung erfreuen kann: Document Expiration. Mit dieser Funktionalität kann ich Dokumente basierend auf einem Datumsfeld in regelmäßigen Löschvorgängen aus einem Solr Index löschen sowie ein „Ablaufdatum“ für meine Dokumente berechnen lassen, die wiederum auf einem „time to live“-Wert (TTL) basieren.
Beide Funktionen können mittels DocExpirationUpdateProcessorFactory eingebunden werden, also zur Indexierungszeit, nachdem das Dokument an den UpdateHandler geschickt wurde. Um Dokumente in regelmäßigen Zyklen basierend auf einem Feldwert zu löschen, sind nur zwei Optionen notwendig. Mittels expirationFieldName wird definiert, in welchem Feld das Datum zu finden ist, mittels autoDeletePeriodSeconds in welchem zeitlichen Abstand in Sekunden der Timer zum Löschen angestoßen werden soll.
Nachfolgend ist eine Beispielkonfiguration zu finden, die eine UpdateRequestProcessorChain definiert, die alle 60 Sekunden den Index nach Dokumenten durchsucht, die im Feld expires_dt einen Wert haben, der älter ist als der jetzige Zeitpunkt. Alle Dokumente, die gefunden werden, werden gelöscht und diese Änderungen per Soft Commit sichtbar (oder eher unsichtbar) gemacht. Soft Commits werden im Übrigen nur ausgeführt, wenn sich tatsächlich etwas am Index verändert hat. Es findet demnach keine häufige Cache-Invalidierung statt, nur weil sehr kurze Perioden definiert sind. Die UpdateRequestProcessorChain ist in den Standard UpdateHandler /update eingebunden.

<requestHandler name="/update" class="solr.UpdateRequestHandler">
 <lst name="defaults">
 <str name="update.chain">ttl</str>
 </lst>
 </requestHandler>
<updateRequestProcessorChain name="ttl">
 <processor class="solr.processor.DocExpirationUpdateProcessorFactory">
 <str name="expirationFieldName">expires_dt</str>
 <int name="autoDeletePeriodSeconds">60</int>
 </processor>
 <processor class="solr.LogUpdateProcessorFactory" />
 <processor class="solr.RunUpdateProcessorFactory" />
 </updateRequestProcessorChain>

Fügt man nun ein Dokument zu diesem Index hinzu, das ein Datum im Feld expires_dt indexiert, welches fünf Minuten in der Zukunft liegt, wird dieses mit einem entsprechenden Wert im Feld expires_dt indexiert:

curl -X POST -H 'Content-Type: application/json' 'https://localhost:8983/solr/time_to_live/update?commit=true' -d '

Frägt man die Collection nun nach diesem Dokument ab, findet man es mit einem Timestamp in expires_at, der fünf Minuten in der Zukunft liegt (Zeitzone der JVM beachten!). Nach einer Wartezeit von fünf Minuten führt man die Query erneut aus und findet dieses Dokument nicht mehr.
Möchte man kein Löschen von „abgelaufenen“ Dokumenten bewirken, sondern nur ein Ablaufdatum berechnen lassen und diese dann per FilterQuery vom Finden ausschließen, kann man ohne autoDeletePeriodSeconds arbeiten und stattdessen einen Datumswert berechnen lassen, mit Hilfe von dem man die Gültigkeit eines Dokuments festlegt.
Dafür definiert man in einem UpdateRequestProcessor , der keine Einstellung mehr für autoDeletePeriodSeconds:

<updateRequestProcessorChain name="ttl">
   <processor class="solr.processor.DocExpirationUpdateProcessorFactory">
     <str name="ttlFieldName">time_to_live_s</str>
     <str name="expirationFieldName">expires_dt</str>
   </processor>
   <processor class="solr.FirstFieldValueUpdateProcessorFactory">
     <str name="fieldName">expires_dt</str>
   </processor>
   <processor class="solr.LogUpdateProcessorFactory" />
   <processor class="solr.RunUpdateProcessorFactory" />
 </updateRequestProcessorChain>

Zur Indexierungszeit kann nun entweder ein Parameter (_ttl_) übergeben werden, der für die Berechnung des Wertes für expires_dt relativ zum Zeitpunkt NOW sorgt. Als Alternative kann auch ein String im Feld time_to_live_s übergeben werden, der einen Date Math-gültigen Ausdruck enthält. Angenommen zwei Dokumente werden indexiert:

curl -X POST -H 'Content-Type: application/json' 'https://localhost:8983/solr/time_to_live/update?commit=true&_ttl_=%2B5MINUTES' -d ''

Führt man eine Suche nach diesen beiden Dokumenten durch, bekommt man diese beiden Dokumente mit den berechneten Datumswerten zurück:

"response":{"numFound":2,"start":0,"docs":
 }}

Weder nach fünf Minuten noch nach zwei Stunden werden diese beiden Dokumente nun gelöscht. Sie stehen prinzipiell zur Suche zur Verfügung. Möchte man sie vom Ergebnis ausschließen, wenn sie abgelaufen sind, gelingt dies per FilterQuery: -fq=expires_dt:[* TO NOW]
Das automatische Löschen als auch das Berechnen eines Ablaufdatums per DocExpirationUpdateProcessorFactory sind miteinander kombinierbar, man muss sie nicht individuell bzw. getrennt voneinander einsetzen.
Die Use Cases für dieses Feature sind vielfältig:

  • In Log-Auswertungs-Szenarien interessieren nicht immer alle Logdateien, sondern nur die der letzten X Tage.
  • Pressemitteilungen, AGBs, Mitarbeiteranweisungen, Verträge etc. haben oftmals nur eine begrenzte Gültigkeit.
  • In Online-Shops haben bestimmte Angebote nur eine gewisse Gültigkeit.

Insgesamt ist Document Expiration ein ziemlich mächtiges Feature, das durchaus auch unterschiedlichen Zwecken dienen kann und ein eher umständliches Skript und das Einrichten eines regelmäßig ablaufenden Jobs wesentlich eleganter löst. Es ist also nicht davon auszugehen, dass dieses Feature auf lange Sicht selten verwendet wird.

Die Beispiele in diesem Blog sind angelehnt an diejenigen aus dem Blog von Chris Hostetter.
Die bisherigen Quellen sind einerseits das Auftauchen in der Liste der verfügbaren UpdateRequestProcessors im Solr Reference Guide, die JavaDocs sowie ein Artikel im Blog von Lucidworks

Daniel Wrigley

Daniel Wrigley