Apache Solr – DataImportHandler – Import von MySql

Wer eine eigene Apache Solr Instanz aufsetzt, sieht sich recht bald mit der Frage konfrontiert wie denn nun die Daten in den Solr kommen sollen. Da gäbe es zunächst die Möglichkeit das per Skript zu erledigen und die fertigen Datensätze an die /update API zu schicken. Solr bietet aber auch die Option über einen DataImportHandler den Datenimport direkt zu erledigen.

Wie das genau von statten geht, soll in diesem Beitrag erläutert werden.

Vorbereitung

In der solrconfig.xml muss folgendes eingefügt/angepasst werden.

Damit definieren wir einen neuen Endpunkt in der API namens /dataimport. Darüber können wir den Import anstoßen und steuern. Die genaue Konfiguration übernimmt dann der Übersicht halber eine andere Datei (db-data-config.xml).

Das Gerüst

In die db-data-config.xml fügen wir folgenden Code ein.

In datasource definieren wir den MySql-Treiber und hinterlegen die Verbindungsdaten. Wer auf Nummer sicher gehen möchte kann noch ein readOnly="true" hinzufügen. Aber Achtung: Wenn Stored Procedures verwendet werden sollen, dann darf die Verbindung nicht ready only sein.
In entity steht nun endlich wie genau Solr die Daten beziehen soll. Der Name kann frei gewählt werden und hat nichts mit dem Namen des cores oder sonst irgendeiner Konfiguration zu tun. Er sollte – wie wir später sehen – dennoch sinnvoll sein.

SQL Statements und Zusammenbau der Entity

Der einfachste Fall – den natürlich nie einer hat – wäre jetzt folgender:

Solr würde jetzt selbstständig bekannte (d. h. in schema.xml definierte) Spalten übernehmen. Also wenn es ein DB-Feld namens id und ein Solr-Feld namens id gibt, dann werden die Daten genau dahin übernommen. Unbekannte Spalten, die von der DB kommen, werden einfach ignoriert.

Zuordnung der Spalten

Da die Spalten aber natürlich unterschiedlich heißen können und vermutlich auch diverse aggregierte Informationen geliefert werden, gibt es zwei Möglichkeiten diese entsprechend zuzuordnen. Zunächst können wir natürlich im SQL die Spalten umbenenen.

Solr bietet aber auch eine eigene Variante an.

Dabei ist column der Name, den Solr kennt, und name der Name in der DB.

Abfragen mehrerer Tabellen

Da in der DB die Daten vermutlich normalisiert und somit über mehrere Tabellen verteilt sind, müssen diese beim Import zusammengeführt werden. Dies gilt insbesondere für Felder, die in Solr mehrer Werte enthalten. Wie etwa
<field name="keywords" multiValued="true" ... />
Hier kommen wir mit einem JOIN nicht mehr hin. Dafür bietet Solr die Möglichkeit der Sub-Entity.

An dieser Stelle wird ein weiteres Query ausgelöst, dass den Inhalt des keywords Feldes des Datensatzes befüllt. Auch hier können wieder beide Varianten der Zuordnung genutzt werden. Mit einer Sub-Entity lassen sich auch mehrere Felder des Datensatzes gleichzeitig füllen. Ein wichtiger Unterschied ist, dass wir hier eine WHERE-Bedingung im Statement verwenden um nur die zugehörigen keywords zu selektieren. Dazu bietet uns Solr den Zugriff auf die Felder bereits abgefragter Entities. Wir können also mit ${company.id} auf den Wert des Id-Feldes der company – definiert durch name="company" – zugreifen.

Zu beachten ist hier, dass pro Sub-Entity für jeden Datensatz ein separates Query abgeschickt wird. Die Anzahl an Queries schnellt hier also drastisch in die Höhe!

Inkrementeller Import

Natürlich wollen wir nicht bei jeder Änderung der Daten einen vollen Import fahren. Wir benötigen also eine Möglichkeit Änderungen festzustellen und dann ein entsprechendes Update zu erzeugen. Dies geschieht mit der Definition zweier weiterer Queries.

Dabei stellt deltaQuery das SQL-Statement dar mit dem geänderte Einträge erkannt werden. Solr liefert mit ${dih.last_index_time} einen Timestamp (entspricht einem MySql DATETIME), der den letzten Delta-Import oder den letzten vollen Import repräsentiert. Als Rückgabe erwartet Solr ein Feld, mit dem es den Datensatz identifizieren kann (den primary key).
In deltaImportQuery definieren wir das SQL-Statement mit dem wir das Update erzeugen. In den meisten Fällen wird es wohl identisch zum query und lediglich um die WHERE-Bedingung für den einen Datensatz erweitert sein. Dabei können wir mit ${dih.delta.id} auf die Rückgabe von deltaQuery zugreifen. Es ist möglich das Query hier kleiner zu halten und Felder, die sich nicht ändern können, auszulassen. Solr wird den alten Datensatz nicht löschen, sondern lediglich die tatsächlich erhaltenen Felder aktualisieren.
Wenn Solr abfragt welche Einträge geändert wurden (deltaQuery), spielen die Sub-Entities keine Rolle. Beim erzeugen des Updates danach, werden sie genau wie bei query mitgenommen. Die Sub-Entities dürfen allerdings selbst eine deltaQuery Definition enthalten um Änderungen hier feststellen zu können.

Stored Procedures

Aus diversen Gründen (Performance, Übersicht, …) bietet es sich an die Queries nicht direkt in die Konfiguration des Solr zu schreiben, sondern als Stored Procedure in der DB zu hinterlegen. Wie oben bereits erwähnt, darf die DB-Verbindung nicht read only sein. Mehr muss nicht beachtet werden.

Zusammengefasst

Auslösen des Imports

Nachdem die Konfiguration erledigt ist, muss der Import nur noch gestartet werden.

Konfiguration neu einlesen: solrhost:8080/solr/corename/dataimport?command=reload-config
Kompletten Import starten: solrhost:8080/solr/corename/dataimport?command=full-import
Delta Import starten: solrhost:8080/solr/corename/dataimport?command=delta-import
Status abfragen: solrhost:8080/solr/corename/dataimport
Abbrechen: solrhost:8080/solr/corename/dataimport?command=abort