Kürzlich ist ein Unternehmen auf uns zugekommen, welches hunderte Unternehmenberichte in Word-Files besitzt und die Arbeit mit so vielen Files einfach zu kompliziert geworden ist. Wir sollten die Berichte durchsuchbar machen, um ihnen den Alltag zu erleichtern. In diesem Blogbeitrag möchte ich Ihnen zeigen, wie wir dieses Problem gelöst haben und welche Tools und Techniken wir dabei eingesetzt haben.
Hintergrund
Das Problem mit Word-Dokumenten
Viele Unternehmen schreiben Dokumente, Analysen und Berichte in einzelnen Word-Files und speichern sie ab. Kurzfristig bieten sie zwar hohen Komfort für die Benutzer, aber langfristig bildet sich damit eine Vielzahl isolierter Dokumente, die in einer Cloud-Umgebung oder an einem lokalen Ort wie ausrangierte Gegenstände ruhen.
Die typischen Probleme mit diesem Aufbau sind:
- Isolierte Dateninseln: Die Dokumente sind isoliert abgelegt und nicht gemeinsam durchsuchbar. Einzelne Berichte können nicht einfach miteinander verglichen werden und als Benutzer muss man sich durch Hunderte von Dateien klicken, um die gewünschten Informationen zu finden.
- Schwierige Versionierung: Versionierung muss manuell durchgeführt werden und führt zu einer Vielzahl von Dateinamen wie “Bericht_2024_v9.docx”. Dies erschwert wieder die Übersicht und wenn vergessen wird, eine neue Zwischenversion zu erstellen, können Änderungen schwer rückgängig gemacht werden.
- Fehleranfälligkeit: Bei der manuellen Eingabe von Daten in Word-Dateien kommt es zu Fehlern da keine automatische Prüfung möglich ist. Regeln für Überschriften, Formatierungen oder auch nur die Eingabe von Zahlen und Quellen sind selbst bei höchster Selbstdisziplin nicht immer einzuhalten. Eine automatische Prüfung ist jedoch nicht möglich.
Alternative: Eine Suchmaschine für Dokumente
Bei einer geringen Anzahl an Dokumenten ist die Flexibilität von Word wichtiger als diese Nachteile. Sobald jedoch Hunderte von Berichten vorliegen, wird es Zeit, eine Lösung zu finden, die die Vorteile von Word mit den Vorteilen einer Datenbank verbindet.
Hier ist eine Datenbank mit einem flexiblen Web-Interface die beste Lösung. Die Datenbank ermöglicht es, die Berichte zu durchsuchen, zu filtern und zu analysieren. Das Web-Interface bietet eine benutzerfreundliche Oberfläche, um die Berichte zu durchsuchen aber auch neue Berichte mit höchster digitaler Unterstützung anzulegen.
Eine Weboberfläche, kann die Eingaben von Nutzern während der Eingabe automatisiert prüfen, Fehler aufzeigen und Berechnungen sicher durchführen. Damit können sich Ihre Mitarbeiter:innen auf die Inhalte fokussieren.
Das Projekt
Eine der Aufgaben, die mich immer wieder begeistert, ist die Textanalyse. Immer wenn Textdaten für ein Unternehmen zu einem unüberwindbaren Problem werden, ist es spannend, eine passende Lösung zu finden.
In diesem Fall warteten Hunderte von Unternehmensberichten auf uns. All diese Berichte, die im Laufe der Jahre in stundenlanger Arbeit erstellt worden waren, konnten nicht mehr gemeinsam betrachtet und analysiert werden. Wir sagten uns, dass wir eine Anwendung für dieses Unternehmen erstellen und eine Umgebung vorbereiten sollten, in der sie am einfachsten auf die Informationen zugreifen können, und begannen zu recherchieren.
Bei solchen Projekten besteht der erste Schritt darin, zunächst zu planen, was wir am Ende sehen wollen, und dann nach den am besten geeigneten Anwendungen zu suchen und sie in die Praxis umzusetzen.
Schritt 1: Die Projektanforderungen
Das Unternehmen wollte die gewünschten Informationen aus Dutzenden von Berichten zu bestimmten Schlüsselwörtern abrufen. Es sollte ihnen ermöglichen, Hunderte von Berichten nach bestimmten Wörtern oder Wortgruppen zu durchsuchen und so leicht Vergleiche zwischen ihnen anzustellen - “was wurde zum Abschnitt X in allen Berichten der letzten Jahre geschrieben?“.
Dies führte zu einer wichtigen Entscheidung in der Implementierung: Jedes Kapitel musste als separates Dokument in die Suchmaschine hochgeladen werden. So konnten wir jedes Kapitel direkt als Ergebnis einer Abfrage darstellen, zusammen mit den zugehörigen Kapitelinformationen.
Schritt 2: Die Datenextraktion aus Word-Dokumenten
Direkte Extraktion aus Word
Für die Extraktion haben wir im ersten Schritt python-docx verwendet. Dieses Python-Paket ermöglicht die einfache Extraktion von Daten aus Word-Dokumenten. Folgend ein einfacher Beispiel-Code, wie Inhalte aus Word-Dokumenten extrahiert werden können:
from docx import Document
# Verwendung der Funktion
doc_path = "path_to_report.docx"
doc = Document(doc_path)
# Alle Textabschnitte inklusive Überschriften sind unter "paragraphs" verfügbar
for paragraph in doc.paragraphs:
print(paragraph.text)
# Tabellen sind separat unter "tables" verfügbar
for table in doc.tables:
print(table)
# Bilder sind separat unter "inline_shapes" verfügbar
for image in doc.inline_shapes:
print(image)
Dieses Paket ist leistungsstark, jedoch in der Handhabung nicht ideal für besonders große Berichte geeignet in denen viele Überschriften vorhanden sind und Tabellen und Bilder an allen Stellen vorkommen können. Das liegt daran, dass - wie im Code-Beispiel oben zu sehen ist - Texte, Tabellen und Bilder separat extrahiert werden und die Positionsbestimmung der Elemente im Dokument nicht immer einfach ist.
Konvertierung der Dokumente zu HTML
Da wir die Kapitelinformationen in unserer Anwendung im HTML-Format präsentieren wollten, mussten wir jedes Dokument zuerst in HTML konvertieren. Dies ermöglichte uns, die Formatierungen und Strukturen (Tabelle, Aufzählungen, Bilder), die im Word-Dokument enthalten waren, beizubehalten und dem Benutzer das Beste aus beiden Welten zu bieten: Volltextsuche in einer ansprechenden HTML-Darstellung.
Mit dem Python-Package Mammoth haben wir das Dokument in HTML konvertiert. Hier ein einfacher Beispielcode, wie die Konvertierung mit Mammoth umgesetzt werden kann:
from docx import Document
import mammoth
def convert_doc_to_html(doc_path):
with open(doc_path, "rb") as doc_file:
result = mammoth.convert_to_html(doc_file)
html_content = result.value # Das generierte HTML
return html_content
# Verwendung der Funktion
doc_path = "path_to_report.docx"
html_content = convert_doc_to_html(doc_path)
print(html_content)
Durch diese Umwandlung konnten wir Kapitel als einzelne HTML-Elemente extrahieren und sie auf den gewünschten Teil im Suchsystem abstimmen.
Schritt 3: Eine Demo-Anwendung entwickeln
Unsere Aufgabe war es, eine Demo zu entwickeln, um dem Unternehmen schnell zeigen zu können, wie die Lösung aussehen würde und wie damit gearbeitet werden kann. Flask ist in solchen Fällen ein sehr gutes Framework. Es ist nicht nur leichtgewichtig und schnell aufzusetzen, sondern bietet auch volle Flexibilität für die Entwicklung von API-basierten Anwendungen.
Nachdem wir ein einfaches, benutzerfreundliches Design erstellt hatten, konnten wir uns auf die APIs konzentrieren, die wir im Hintergrund benötigten. Flask ist ideal, um schnell eine API für den Datenzugriff zu erstellen – besonders bei einem Prototypen oder einer Demo.
Schritt 4: Die richtige Suchlösung auswählen – OpenSearch oder Typesense?
Das Unternehmen wollte die Berichte schnell durchsuchen können. Es sollte eine schnelle und präzise Volltextsuche geben, und dies stellte uns vor die Entscheidung: OpenSearch oder Typesense? Beide bieten leistungsstarke Suchfunktionen, doch je nach Projektanforderung haben sie ihre jeweiligen Stärken und Schwächen. Hier sind meine Erkenntnisse und Gründe für die Wahl von Typesense.
OpenSearch – Stabilität und Flexibilität für große Datenmengen
OpenSearch ist ein Fork des allseits bekannten Elasticsearch. Es basiert damit auf Elasticsearch und bietet ähnliche Funktionen aber hat eine freiere Lizenz. Es ist ein bewährtes Werkzeug und bekannt für seine Stabilität und Flexibilität. Es ist ideal für die Verarbeitung und Analyse von großen Datenmengen. Für Unternehmen, die bestehende Elasticsearch-Setups haben oder deren Daten stark strukturiert und regelmäßig aktualisiert werden, ist OpenSearch eine ausgezeichnete Wahl. Die Unterstützung für komplexe Abfragen und Aggregationen ist ein weiterer Vorteil.
Dennoch hat OpenSearch einige Herausforderungen:
- Setup und Konfiguration: OpenSearch ist mächtig, aber auch komplex. Der Einstieg erfordert mehr Aufwand, da die Konfiguration vielseitig ist, was für einfache Suchprojekte oft überdimensioniert ist.
- Performance: OpenSearch ist ressourcenintensiv und erfordert leistungsfähige Infrastruktur. Gerade bei kleineren Projekten oder begrenzten Ressourcen kann das zu Einschränkungen führen.
- Docker-Setup und API-Integration: Das Setup mit Docker ist gut dokumentiert, aber mit vielen Konfigurationsdetails verbunden, die einen schnellen Start erschweren. Die APIs sind flexibel, jedoch für kleine Projekte oft zu umfangreich.
Typesense – Schnelligkeit und Benutzerfreundlichkeit für spezifische Suchanfragen
Typesense hingegen ist auf Geschwindigkeit und Benutzerfreundlichkeit ausgerichtet und speziell für Volltextsuche und Fuzzy-Search optimiert. Der Anwendungsbereich ist kleiner, aber für spezifische Suchanfragen ideal.
Da das Unternehmen in unserem Fall Berichte durchsuchen wollte, ohne viele zusätzliche Analysen oder Aggregationen, war Typesense die passende Wahl.
Die wichtigsten Vorteile von Typesense für unser Projekt:
- Einfache Einrichtung: Typesense ließ sich schnell über Docker einrichten und war in wenigen Minuten einsatzbereit. Das einfache Setup spart bei kleineren Projekten wertvolle Zeit.
- API und Geschwindigkeit: Typesense bietet eine API, die speziell für die Volltextsuche optimiert ist. Die Geschwindigkeit ist beeindruckend und ideal, wenn schnelle und präzise Antworten für den Endbenutzer erforderlich sind.
- Fokus auf Relevanz und Fehlertoleranz: Typesense unterstützt Fuzzy-Search und erkennt Tippfehler, was gute Ergebnisse selbst bei ungenauen Suchanfragen bietet. Das ist besonders vorteilhaft, wenn Benutzer verschiedene Begriffe oder Synonyme verwenden.
Die Benutzerfreundlichkeit und Effizienz von Typesense ließen uns schnell eine Demo für das Unternehmen erstellen. Mit wenigen Zeilen Code konnten wir eine leistungsstarke Suchfunktion aufbauen. Die Fehlertoleranz und die direkte Integration mit unserem Flask-Backend waren wesentliche Pluspunkte.
Hier ist ein Beispiel, wie wir eine einfache Suche mit Typesense eingerichtet haben:
import typesense
client = typesense.Client({
'nodes': [{
'host': 'localhost',
'port': '8108',
'protocol': 'http'
}],
'api_key': 'xyz',
'connection_timeout_seconds': 2
})
# Schema für Typesense bzw. in welchem Format die Daten in Typesense hinterlegt werden
# Für unser Projekt war die separate Speicherung von Kapiteln als Dokumente wichtig
schema = {
"name": "reports",
"fields": [
{"name": "document_name", "type": "string"},
{"name": "content", "type": "string"},
{"name": "section_name", "type": "string"},
{"name": "created", "type": "string"},
],
"default_sorting_field": "created"
}
client.collections.create(schema)
# Dokumente indexieren
def index_document(section_name, document_name, content, created):
document = {
"section_name": section_name,
"document_name": document_name,
"content": content,
"created": created
}
client.collections['reports'].documents.create(document)
Erweiterte Suchmöglichkeiten und Autocomplete
Nachdem die Dokumente mit dieser Funktion indexiert wurden, kann nun die Suchfunktionalität eingebaut werden. Typesense bietet mit wenigen Zeilen Code erweiterte Suchoptionen und Autocomplete-Funktionalitäten, die für eine präzise und fehlertolerante Suche entscheidend sind. Hier ein Beispiel für die Implementierung der Suche und einer Autocomplete-Funktionalität in Flask:
@app.route('/search', methods=['GET'])
def search():
query = request.args.get('q', '*')
section_name = request.args.get('section_name', '')
# Filtert nach dem ausgewählten Kapitel, falls es als Filter angegeben wurde
filter_by = f'section_name:={section_name}' if section_name else ''
search_parameters = {
'q': query,
'query_by': 'document_name, content, section_name',
'filter_by': filter_by,
'per_page': 10,
# hier kann festgelegt werden, wie lange der Textausschnitt des Ergebnisses sein soll
'highlight_affix_num_tokens': 20,
}
results = client.collections['reports'].documents.search(search_parameters)
return jsonify(results)
@app.route('/autocomplete', methods=['GET'])
def autocomplete():
query = request.args.get('q')
search_parameters = {
'q': query,
'query_by': 'document_name, content, section_name',
'num_typos': 1,
'per_page': 5
}
results = client.collections['reports'].documents.search(search_parameters)
return jsonify(results)
Das Autocomplete-Feature verbessert die Benutzerfreundlichkeit erheblich, da es auch bei Tippfehlern relevante Vorschläge bietet und die Suchergebnisse für den Endnutzer beschleunigt.
Zusammenfassung: OpenSearch vs. Typesense
In der folgenden Tabelle ist eine Übersicht, die zeigt, wann sich OpenSearch und wann sich Typesense besser eignet:
Kriterien | OpenSearch | Typesense |
---|---|---|
Einrichtung | Komplex mit detaillierten Konfigurationsmöglichkeiten | Einfach und schnell über Docker |
Leistungsfähigkeit | Ideal für große Datenmengen und Aggregationen | Optimiert für kleine und mittlere Suchanfragen |
Fehlertoleranz | Grundlegende Fuzzy-Search | Hervorragende Fuzzy-Search-Funktionalität |
API | Flexibel, aber umfangreich | Einfach, fokussiert auf Volltextsuche |
Fazit
Die Entscheidung für Typesense war für dieses Projekt genau richtig, da das Unternehmen schnelle und präzise Suchergebnisse wollte, ohne sich mit komplexen Aggregationen zu befassen. OpenSearch bleibt eine starke Alternative, wenn komplexe Datenanalyse und Skalierbarkeit im Vordergrund stehen.
So wurde eine zunächst scheinbar unlösbare Aufgabe zur Chance, eine intelligente Lösung für das Unternehmen zu schaffen, die nicht nur die Gegenwart, sondern auch die Zukunft der Datennutzung positiv beeinflusst.
Wenn Sie auch zu viele Reports und Dokumente in Word und Excel haben, dann lassen Sie uns darüber reden. Wir beraten und unterstützen Sie gerne in der Migration zu einer skalierbaren Datenbanklösung die Fehler reduziert, neue Analysen ermöglicht und Ihnen Zeit und damit auch Kosten spart!