Durchsuchen von Rich-Format-Dokumenten, die in einem DBMS gespeichert sind

Von Jonck van der Kogel

Einführung

Da Unternehmen immer mehr Daten sammeln, wird die Möglichkeit, diese Daten zu durchsuchen, immer wichtiger. Besonders bei Altsystemen kann dies manchmal eine ziemliche Herausforderung sein. Eine Situation, auf die Sie stoßen könnten, ist die, dass Dokumente in umfangreichen Formaten wie PDF, MS Word/Excel/Powerpoint usw. als BLOBs in einer SQL-Datenbank gespeichert sind. Ihre erste Reaktion könnte sein, dass dies eine Menge Arbeit wäre, da Solr einen solchen Import nicht von Haus aus unterstützt. Aber durch die Verwendung von DataImportHandler von Solr und einem benutzerdefinierten Transformer wird es tatsächlich ziemlich einfach und unkompliziert.

Damit Solr erkennt, dass wir mit dem DataImportHandler und einem benutzerdefinierten Transformer arbeiten, müssen wir zunächst einige Konfigurationen vornehmen. Die drei Dateien, die geändert werden müssen, sind:

  • data-config.xml
  • solrconfig.xml
  • schema.xml

data-config.xml

Der erste Schritt besteht darin, Ihre data-config.xml einzurichten. Diese Datei enthält Ihre Datenquellendefinition, die Felder, die indiziert werden sollen, und die Abfrage, wie die Daten aus der Datenbank abgerufen werden sollen. Hier ist ein Beispiel für eine Datenkonfiguration:

<dataConfig>
	<dataSource driver="oracle.jdbc.driver.OracleDriver"
	 url="jdbc:oracle:thin:@myServer:1521:myDb"
	 user="username"
	 password="password" />
	<document name="distributionDoc">
		<entity name="distribution" query="SELECT distr.id, distr.auhor, distr.titel, distr.summary, distr.publishdate FROM myDistrubutions distr">

		<field column="ID" name="id" />
		<field column="AUTHOR" name="author" />
		<field column="TITEL" name="titel" />
		<field column="SUMMARY" name="summary" />
		<field column="PUBLISHDATE" name="publishDate" />
		<entity name="attachment" query="SELECT attach.ID, attach.file FROM attachments attach
			WHERE attach.ID_DISTRIBUTION='${ distribution.ID}'"
			transformer="com.hinttech.solr.BlobTransformer">

			<field column="ID" name="fileId" />
			<field column="FILE" name="file" blob="true" />
		</entity>
		</entity>
	</document>
</dataConfig>

Der Abschnitt dataSource spricht für sich selbst. Hier legen Sie fest, wie Sie sich mit Ihrer Datenquelle verbinden. Denken Sie daran, den von Ihnen angegebenen Treiber in den Klassenpfad von Solr aufzunehmen! In meinem Fall habe ich Solr mit Tomcat ausgeführt und musste daher den Oracle-Treiber in das WEB-INF/lib-Verzeichnis der Solr-Webapp aufnehmen.

Der Abschnitt document der Datei data-config.xml beschreibt, wie Solr Ihre Daten indizieren soll. In meinem Beispiel habe ich Dokumente namens „Distributionen“, die nach Feldern wie Autor, Titel, Zusammenfassung und Veröffentlichungsdatum durchsuchbar sein müssen. An jede Verteilung können mehrere PDF-Dateien angehängt sein. Um dies einzurichten, definieren Sie eine untergeordnete Entität innerhalb des Entity-Tags. Die Verteilungstabelle hat einen Primärschlüssel „id“, der zur Verknüpfung der Anhänge verwendet wird. Beachten Sie, wie wir Expression Language in der Abfrage der untergeordneten Entität verwenden. Das Attribut transformer der untergeordneten Entität teilt Solr mit, welche Implementierung von Transformer diese Entität benötigt, um sie in etwas umzuwandeln, das Solr indizieren kann. Das Feld, das das eigentliche BLOB enthält, hat ein zusätzliches Attribut blob=“true“; dazu kommen wir gleich noch.

solrconfig.xml

Nachdem die data-config.xml korrekt konfiguriert wurde, ist es nun an der Zeit, die DataImportHandler in Solr und teilen Sie Solr mit, wo sich die data-config.xml befindet. Dazu fügen Sie Folgendes zur solrconfig.xml hinzu:

<requestHandler name="/dataimport">
	<lst name="defaults">
	<str name="config">/home/username/data-config.xml</str>
	</lst>
</requestHandler>

 

schema.xml

 

Der letzte Schritt bei der Konfiguration von Solr besteht darin, dass Sie die Felder Ihrer data-config.xml in schema.xml beschreiben müssen. In meinem Beispiel würde das so aussehen:

<fields>
	<field name="id" type="integer" indexed="true" stored="true" required="true" />
	<field name="author" type="string" indexed="true" stored="true" required="false" />
	<field name="titel" type="text" indexed="true" stored="true" required="false" />
	<field name="summary" type="text" indexed="true" stored="true" required="false" />
	<field name="publishDate" type="date" indexed="true" stored="true" required="false" />
	<field name="file" type="text" indexed="true" stored="false" required="false" multiValued="true" />
	<field name="fileId" type="long" indexed="true" stored="true" required="false" multiValued="true" />
</fields>

Wie Sie sehen können, entsprechen die Felddefinitionen wortwörtlich dem, was Sie in data-config.xml eingerichtet haben. Das Einzige, was mir etwas Kopfzerbrechen bereitet hat, war, dass ich es versäumt hatte, das Attribut multiValued=“true“ für die Felder der untergeordneten Entität zu setzen. Ohne dieses Attribut wurde nur der erste Anhang indiziert, was dazu führte, dass viele Abfragen nicht das fanden, was ich wollte. Nach einer Menge Fehlersuche und einem Blick in den Quellcode von DataImportHandler entdeckte ich, dass es einfach nur darum ging, das Attribut multiValued zu setzen. Danach wurden alle Anhänge korrekt indiziert.

 

Kundenspezifischer Transformator

 

Lassen Sie uns nun unseren benutzerdefinierten Transformer programmieren. Ich habe PDFBox 0.7.3 zum Parsen der PDF-Dateien verwendet. Obwohl Apache das PDFBox-Projekt übernommen hat, gab es zum Zeitpunkt der Erstellung dieses Artikels noch keine offizielle Version von PDFBox von Apache. Ich habe mich daher für die letzte stabile Version von PDFBox entschieden. PDFBox ist von FontBox abhängig und wenn Ihre PDFs Sicherheitseinschränkungen haben, benötigt PDFBox auch einige BouncyCastle Jars. Nachfolgend finden Sie das Maven2 POM für dieses Projekt:

<?xml version="1.0" encoding="UTF-8"?>
<project 
	xsi_schemaLocation="http://maven.apache.org/POM/4.0.0 <a href="http://maven.apache.org/maven-v4_0_0.xsd">
" title="http://maven.apache.org/maven-v4_0_0.xsd">
">http://maven.apache.org/maven-v4_0_0.xsd">
</a>	<!-- POM version for Maven 2 -->
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.hinttech</groupId>
	<artifactId>customSolr</artifactId>
	<packaging>jar</packaging>
	<version>1.0</version
	<name>Custom Solr</name></span>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId
				<executions>
					<execution>
						<id>copy-extra-dependencies</id>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.pdfbox</groupId>
			<artifactId>PDFBox</artifactId>
			<version>0.7.3</version>
		</dependency>
		<dependency>
			<groupId>org.fontbox</groupId>
			<artifactId>FontBox</artifactId>
			<version>0.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.solr</groupId>
			<artifactId>solr-dataimporthandler</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
 			<version>1.1.1</version>
		</dependency>
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk16</artifactId>
			<version>143</version>
		</dependency>
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcmail-jdk16</artifactId>
			<version>143</version>
		</dependency>
	</dependencies>
</project>

Beachten Sie, dass ich die Jars von PDFBox und FontBox manuell herunterladen und in unser lokales Maven-Repository aufnehmen musste.

Der benutzerdefinierte Transformer muss von der Klasse org.apache.solr.handler.dataimport.Transformer abgeleitet sein und die Methode transformRow außer Kraft setzen. Der Methode wird die aktuelle Zeile in der Ergebnismenge der Abfrage der untergeordneten Entität query als Map<String, Object> und ein Context-Objekt übergeben, aus dem Sie alle Felder der untergeordneten Entität abrufen können. Indem Sie alle Felder der aktuellen Entität in einer Schleife durchlaufen, können Sie testen, ob ein Feld das Attribut „blob“ hat. Wenn ja, wissen wir, dass wir es mit einem BLOB zu tun haben, und können es aus der aktuellen Zeile abrufen. Wir lesen den Text mit PDFBox aus der PDF-Datei und fügen den geparsten Text wieder in das Zeilenobjekt ein, wobei wir den Spaltennamen (in unserem Fall „FILE“) als Schlüssel verwenden. Das BLOB wurde nun in Text umgewandelt, der dann an Solr zurückgegeben wird, das nun wiederum seine Indizierungsmagie entfesseln kann.

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.solr.handler.dataimport.Context;
import org.apache.solr.handler.dataimport.Transformer;
import org.pdfbox.cos.COSDocument;
import org.pdfbox.pdfparser.PDFParser;
import org.pdfbox.pdmodel.PDDocument;
import org.pdfbox.util.PDFTextStripper;
public class BlobTransformer extends Transformer {
	private static Log LOGGER = LogFactory.getLog(BlobTransformer.class);

	@Override
	public Object transformRow(Map<String, Object> row, Context context) {
		List<Map<String, String>> fields = context.getAllEntityFields();

			BigDecimal id = null;
			for (Map<String, String> field : fields) {
				String name = field.get("name");
				if ("fileId".equals(name)) {
					String columnName = field.get("column");
					id = (BigDecimal) row.get(columnName);
					LOGGER.info("Processing file with ID: " + id);
				}
				String isBlob = field.get("blob");
				if("true".equals(isBlob)) {
					String columnName = field.get("column");
					Blob blob = (Blob) row.get(columnName);
					COSDocument cd = null;
					InputStream inputStream = null;
					try {
						inputStream = blob.getBinaryStream();
						PDFParser parser = new PDFParser(inputStream);
						parser.parse();
						cd = parser.getDocument();
						PDFTextStripper stripper = new PDFTextStripper();
						String text = stripper.getText(new PDDocument(cd));

						row.put(columnName, text);
						LOGGER.info("Processed file with ID: " + id);
					} catch (SQLException sqle) {
						LOGGER.error(sqle);
						row.put(columnName, "");
					} catch (IOException ioe) {
						LOGGER.error(ioe);
						row.put(columnName, "");
 					}  finally {
						try {
							if (cd != null) {
								cd.close();
							}
							if (inputStream != null) {
								inputStream.close();
							}
						} catch (IOException ioe) {
							LOGGER.error(ioe);
						}
					}
				}
			}
			return row;
		}
}

Erstellen Sie ein jar, das die obige Klasse enthält, und legen Sie es zusammen mit seinen Abhängigkeiten in den Klassenpfad von Solr. Starten Sie Solr neu. Wenn Sie Solr lokal ausführen, können Sie nun einen vollständigen Import starten, indem Sie diese Url aufrufen:

http://localhost:8080/solr/dataimport?command=full-import

In unserem Fall hat die Indizierung etwa 20 Minuten gedauert. Sobald er fertig ist, gehen Sie zu

http://localhost:8080/solr/admin/

die Ihnen die Solr-Verwaltungsseite zeigt, auf der Sie einige Testabfragen durchführen können. Wenn alles in Ordnung ist, sollten Sie jetzt in der Lage sein, die PDF-Dateien zu durchsuchen, die als BLOBs in Ihrer SQL-Datenbank gespeichert sind.

 

Jonck van der Kogel ist Softwareentwickler und Leiter des Java Competence Center bei HintTech in den Niederlanden. Er verfügt über mehr als 10 Jahre Erfahrung in der Softwareentwicklung mit Schwerpunkt auf J2EE-Webanwendungen und Webservices.

You Might Also Like

4 bewährte KI-Suchlösungen für die Tarifverwaltung

Entdecken Sie, wie KI-Suchlösungen für das Tarifmanagement Einzelhändlern helfen, Margen und Kundenzufriedenheit...

Read More

KI-Agenten dominieren den Einkauf. Ist Ihre Website auf die KI-gestützte Suche vorbereitet?

Generative KI-Agenten wie ChatGPT definieren die Produktsuche neu. Erfahren Sie, wie Sie...

Read More

Vom Suchunternehmen zum praktischen KI-Pionier: Unsere Vision für 2025 und darüber hinaus

CEO Mike Sinoway gibt Einblicke in die Zukunft der KI und stellt...

Read More

Quick Links