Categories
Database Development

MongoDB

Was ist MongoDB?

MongoDB ist eine dokumentenbasierte NoSQL-Datenbank, die JSON-ähnliche Datenstrukturen (BSON) verwendet. Sie wurde entwickelt, um eine hohe Flexibilität und Skalierbarkeit zu bieten, und ist besonders geeignet für Anwendungen mit dynamischen oder unstrukturierten Daten.

Vorteile von MongoDB

  • Schemaflexibilität: Dokumente in einer Collection können unterschiedliche Felder und Strukturen aufweisen.
  • Hohe Skalierbarkeit: Unterstützt horizontales Sharding zur Verteilung von Daten über mehrere Server.
  • JSON-ähnliches Format: BSON erleichtert die Integration mit modernen Programmiersprachen.
  • Eingebaute Replikation: Daten werden automatisch über Replikatsets gesichert.
  • Leistungsfähige Abfragen: Unterstützung für Indexe, Aggregationen und komplexe Suchanfragen.
  • Open Source: Große Community und kostenlose Nutzung (mit kostenpflichtigen Enterprise-Optionen).

Typische Einsatzgebiete

  • Web- und Mobilanwendungen: Speicherung von Benutzerdaten, Sitzungsinformationen oder dynamischen Inhalten.
  • IoT: Speicherung und Verarbeitung von Sensordaten.
  • Content-Management-Systeme (CMS): Flexible Datenmodelle für Inhalte und Metadaten.
  • Echtzeit-Analysen: Verarbeitung von Ereignisdaten für Dashboards oder Monitoring.
  • Geodaten-Anwendungen: Speicherung und Abfragen von Standortdaten.

Mit MongoDB lassen sich schnell und effizient Anwendungen entwickeln, die mit dynamischen Datenstrukturen und wachsendem Datenvolumen umgehen können.

Installation von MongoDB mit Docker

Schritte zur Installation von MongoDB mit Docker

  1. MongoDB-Image herunterladen:

    docker pull mongo:latest

    Dies lädt das neueste MongoDB-Docker-Image aus dem offiziellen Docker Hub.

  2. MongoDB-Container starten:

    docker run -d \
     --name mongodb \
     -p 27017:27017 \
     -v mongodb_data:/data/db \
     mongo:latest
    • -d: Startet den Container im Hintergrund.
    • --name mongodb: Gibt dem Container den Namen mongodb.
    • -p 27017:27017: Bindet den MongoDB-Port (Standard: 27017) an den Host.
    • -v mongodb_data:/data/db: Erstellt ein Docker-Volume für die persistente Speicherung der Daten unter /data/db.
  3. Überprüfung des Containers:

    docker ps

    Dies zeigt eine Liste der laufenden Container. Der mongodb-Container sollte in der Liste erscheinen.

  4. Logs überprüfen (optional): Um sicherzustellen, dass der Container ordnungsgemäß läuft, kannst du die Logs abrufen:

    docker logs mongodb
  5. Container stoppen und entfernen (bei Bedarf):

    • Stoppen:
      docker stop mongodb
    • Entfernen:
      docker rm mongodb

Hinweis

Die Konfiguration verwendet keine Authentifizierung. Für produktive Umgebungen sollte ein Benutzer mit Passwort eingerichtet werden, und der Zugriff auf die Datenbank sollte über eine Firewall geschützt sein.

Clients

1. MongoDB Compass

  • Beschreibung: Das offizielle GUI-Tool von MongoDB.
  • Vorteile:
    • Einfache Installation und Nutzung.
    • Intuitive Benutzeroberfläche für Abfragen und Datenvisualisierung.
    • Unterstützt erweiterte Funktionen wie Aggregations-Pipelines.
  • Nachteile:
    • Kann ressourcenintensiv sein.
    • Nicht optimal für sehr große Datenmengen.
  • Link: MongoDB Compass herunterladen

2. DBeaver (Community Edition)

  • Beschreibung: Open-Source-Datenbank-Tool mit Unterstützung für viele Datenbanken.
  • Vorteile:
    • Multi-Datenbank-Support in einer einzigen Anwendung.
    • Solide SQL-Editor-Funktionen.
  • Nachteile:
    • Die Community Edition unterstützt MongoDB nicht direkt (Pro-Version erforderlich).
    • Keine native JSON-Visualisierung.
  • Link: DBeaver herunterladen

3. MongoSH

 docker exec -it mongodb mongosh

MongoDB in der Cloud

kostenlose MongoDB im Atlas-Clusters

  1. Registrierung bei MongoDB Atlas:

    • Besuche die MongoDB Atlas Website.
    • Erstelle ein kostenloses Konto oder melde dich mit deinem bestehenden Konto an.
  2. Erstellen eines neuen Clusters:

    • Klicke auf "Create a Cluster".
    • Wähle die Option Shared Cluster (kostenlos) und klicke auf "Create".
    • Konfiguriere dein Cluster:
      • Cloud-Provider: AWS, Google Cloud oder Azure (Standard ist AWS).
      • Region: Wähle die nächstgelegene Region, um Latenzzeiten zu minimieren.
      • Cluster Name: Gib einen Namen für deinen Cluster ein oder verwende den Standardnamen.
    • Klicke auf "Create Cluster". Der Vorgang kann ein paar Minuten dauern.
  3. Einrichten von Zugriff und Netzwerk:

    • Gehe nach der Cluster-Erstellung zu Network Access:
      • Klicke auf "Add IP Address".
      • Wähle "Allow Access from Anywhere" (für Testzwecke) oder gib eine spezifische IP-Adresse an.
    • Gehe zu Database Access:
      • Erstelle einen neuen Benutzer, indem du auf "Add New Database User" klickst.
      • Gib einen Benutzernamen und ein Passwort ein.
      • Setze die Rolle auf "Read and Write to Any Database" (für Testumgebungen).
  4. Verbindung herstellen:

    • Gehe zu Connect im Cluster-Dashboard.
    • Wähle "Connect Your Application".
    • Kopiere die bereitgestellte Verbindung-URI, z. B.:
      mongodb+srv://:@.mongodb.net/?retryWrites=true&w=majority
    • Ersetze und mit den von dir erstellten Anmeldedaten.
  5. Testen der Verbindung (Beispiel mit Python):

    • Installiere die offizielle Python-Bibliothek pymongo:
      pip install pymongo
    • Beispielcode:

      from pymongo import MongoClient
      
      # Verbindung zu Atlas-Cluster herstellen
      uri = "mongodb+srv://:@.mongodb.net/?retryWrites=true&w=majority"
      client = MongoClient(uri)
      
      # Test der Verbindung
      try:
       print("MongoDB-Version:", client.server_info()["version"])
      except Exception as e:
       print("Verbindung fehlgeschlagen:", e)
  6. Cluster-Verwaltung:

    • Über das Atlas-Dashboard kannst du:
      • Datenbanken erstellen und Collections verwalten.
      • Aggregationen und Abfragen durchführen.
      • Performance überwachen (beschränkt in der Free-Tier-Version).

Vorteile von Atlas-Free-Tier:

  • Kostenlos: Bis zu 512 MB Speicherplatz.
  • Hohe Verfügbarkeit: Automatische Replikation auf mehrere Nodes.
  • Einfache Verwaltung: Intuitive Oberfläche zur Verwaltung von Clustern und Benutzern.
  • Schnelle Skalierbarkeit: Möglichkeit, bei Bedarf auf kostenpflichtige Pläne zu upgraden.

Einschränkungen:

  • Speicherplatz und Performance sind begrenzt.
  • Eingeschränkte Konfigurationsmöglichkeiten (kein vollständiger Root-Zugriff).
  • Free-Tier-Datenbanken können nach 30 Minuten Inaktivität schlafen gehen.

Lokale DB in die Cloud kopieren

  1. Dump erstellen

    docker exec -it mongodb mongodump
  2. Dump in die Cloud kopieren

    docker exec -it mongodb mongorestore --uri="mongodb+srv://:@.mongodb.net/"
  3. Index erstellen Mit Cloud DB verbinden:

    docker exec -it mongodb mongosh "mongodb+srv://.mongodb.net/" --apiVersion 1 --username 

    Index erstellen:

    use 
    db.fullsite.createIndex(
    { cleaned_text: "text" },  // Das Feld, das durchsucht werden soll
    { default_language: "german" }  // Sprache für den Textindex
    );

Beispiel

Ich möchte die Datenbank aus dem letzten Artikel Webseitendaten Assistent KI in die Cloud bringen. Die Datenmenge ist allerdings zu groß, so dass ich zuerste die Rohdaten löschen muss. Anschließend kann ich den Dump erstellen, in die Cloud hoch laden und abschließend den Index erstellen.

Datenmenge verkleinern

docker exec -it CompanyDataAI-mongo mongosh
use firmendaten

show dbs
db.stats()

db.fullsite.updateMany(
  {},
  { $unset: { fieldName: "raw_html" } }
)

show dbs
db.stats()

Dump erstellen

docker exec -it CompanyDataAI-mongo mongodump --db firmendaten

Datenbank importieren

docker exec -it CompanyDataAI-mongo mongorestore --db firmendaten --uri="mongodb+srv://:@.mongodb.net/"

Problem

Laut show dbs ist die Datenbank 335 MB groß, der Dump ist allerdings 1,3 GB groß. Die Erklärung ist vermutlich, dass die Daten in der Datenbank komprimiert sind, im Dump hingegen nicht. Die freie Cloud Datenbank hat aber nur einen Speicher von 512 MB. Als Lösungsansatz versuchen wir einen komprimierten Dump (190 MB):

docker exec -it CompanyDataAI-mongo mongodump --db firmendaten --gzip
docker exec -it CompanyDataAI-mongo mongorestore --gzip --db firmendaten --uri="mongodb+srv://:@.mongodb.net/" /dump/firmendaten

Leider wird auch dadurch das Problem nicht gelöst:

Failed: firmendaten.fullsite: error restoring from /dump/firmendaten/fullsite.bson.gz: (AtlasError) you are over your space quota, using 526 MB of 512 MB

Daher habe ich den nicht komplett importierten Dump gelöscht und dann das Scraping-Tool mit der Cloud-DB verbunden und neu durchlaufen lassen. Das hat dann auch funktioniert, allerdings war das setzen des Index dann zu groß. Vielleicht hätte der Dump/Restore funktioniert, wenn ich vorher den Index in der lokalen DB gelöscht hätte?

Index setzen

docker exec -it mongodb mongosh "mongodb+srv://.mongodb.net/" --apiVersion 1 --username 

Index erstellen:

use firmendaten
db.fullsite.createIndex(
  { cleaned_text: "text" },  // Das Feld, das durchsucht werden soll
  { default_language: "german" }  // Sprache für den Textindex
);

Leider bricht der Vorgang ab: you are over your space quota.

Fazit

Die Datenbankgröße von 512MB im Free Tier ist nicht ausreichend. Denn leider ist damit nicht der Speicher auf dem Filesystem gemeint (storageSize), den man mit show dbs sehen kann, sondern die Größe der Daten (dataSize) die man mit db.stats() sehen kann. Damit ist die Datenbank leider nicht für mein Projekt zu gebrauchen.

Workaround

Um mit meinem PoC weiter zu kommen, lösche ich irgendwelche Daten und überlege mir später eine andere Lösung:

docker exec -it mongodb mongosh "mongodb+srv://.mongodb.net/" --apiVersion 1 --username 
use firmendaten;

# Index löschen
db.fullsite.dropIndexes();

# Dokumente ohne Inhalt löschen
db.fullsite.deleteMany({
    $or: [
        { cleaned_text: { $exists: false } }, // Optional
        { cleaned_text: "" },
        { cleaned_text: null }
    ]
});

# Die letzten 50 Dokument löschen
db.fullsite.find()
    .sort({ _id: -1 })
    .limit(50)
    .forEach(doc => db.fullsite.deleteOne({ _id: doc._id }));

# Rohdaten löschen
db.fullsite.updateMany({}, { $unset: { fieldName: "raw_html" } });

# Index setzen
db.fullsite.createIndex(
  { cleaned_text: "text" },  // Das Feld, das durchsucht werden soll
  { default_language: "german" }  // Sprache für den Textindex
);
Categories
AI Database Development

Webseitendaten Assistent KI

GitHub

Das Projekt ist in GitHub gespeichert.

Ausgangssituation

Ich möchte einen virtuellen Assistenten erstellen, der auf Informationen von mehreren Webseiten basiert. Ziel ist es, aus den Daten relevante Informationen bereitzustellen und auf Fragen der Benutzer zu antworten.

Da ich keine klaren Informationen über die Struktur der Webseiteninhalte hatte, wollte ich zunächst alle Seiten vollständig speichern und später bereinigen. Die Lösung soll dynamisch erweiterbar sein und folgende Schwerpunkte abdecken:

  1. Web Scraping: Automatisches Sammeln von Rohdaten von verschiedenen Webseiten.
  2. Speicherung: Daten in MongoDB speichern, sowohl Rohdaten als auch bereinigte Daten.
  3. Durchsuchbarkeit: Daten mit einem Full-Text-Index durchsuchbar machen.
  4. KI-Integration: Eine lokale KI-Instanz (Teuken-7B von OpenGPT-X) verwenden, die mit allen 24 europäischen Amtssprachen trainiert wurde, um Benutzerfragen in natürlicher Sprache zu beantworten.
  5. Benutzeroberfläche: Ein Web-Interface für eine einfache und intuitive Nutzung der Lösung.

Lösungsansatz

  1. Web Scraping mit Scrapy:

    • Automatisches Sammeln von HTML-Rohdaten von mehreren Webseiten.
    • Dynamisches Einlesen von Start-URLs.
    • Bereinigung der Daten während des Scrapings (HTML-Tags entfernen, Boilerplate entfernen, Texte kürzen).
  2. Datenhaltung mit MongoDB:

    • Rohdaten und bereinigte Texte wurden parallel gespeichert, um flexibel zu bleiben.
    • Full-Text-Index mit deutscher Spracheinstellung eingerichtet, um die bereinigten Texte effizient zu durchsuchen.
  3. KI-Integration mit Teuken-7B:

    • Übergabe der MongoDB-Ergebnisse als Kontext an das Sprachmodell Teuken-7B.
    • Das Modell generiert eine präzise Antwort auf die Benutzerfrage, basierend auf den bereitgestellten Daten.
  4. Web-App mit Flask:

    • Einfache Benutzeroberfläche, um Fragen zu stellen und KI-Antworten anzuzeigen.
    • Verbindung von Flask mit MongoDB und der KI für dynamische Abfragen.

Architektur

1. Datensammlung

  • Tool: Scrapy.
  • Datenquellen: Liste von Start-URLs (mehrere Domains).
  • Prozess:
    1. Besuch der Startseiten.
    2. Rekursive Erfassung aller Links innerhalb der erlaubten Domains.
    3. Speicherung der Rohdaten (HTML) und bereinigten Daten (Text).

2. Datenhaltung

  • Datenbank: MongoDB.
  • Struktur:
    {
    "url": "https://www.example.com/about",
    "raw_html": "...",
    "cleaned_text": "This is an example text.",
    "timestamp": "2024-11-26T12:00:00Z"
    }
  • Full-Text-Index:
    • Feld: cleaned_text.
    • Sprache: Deutsch.

3. Datenanalyse

  • Abfragen:
    • MongoDB-Textsuche mit Unterstützung für Wortstämme (z. B. „Dienstleistung“ und „Dienstleistungen“).
    • Priorisierung der Ergebnisse nach Relevanz (score).

4. KI-Integration

  • KI-Tool: Teuken-7B (OpenGPT-X).
  • Prozess:
    1. Übergabe der MongoDB-Ergebnisse als Kontext an die KI.
    2. Generierung einer präzisen Antwort basierend auf der Benutzerfrage.

5. Benutzeroberfläche

  • Framework: Flask.
  • Funktionen:
    • Eingabeformular für Benutzerfragen.
    • Anzeige der KI-Antwort und der relevanten Daten.
    • Einfache und intuitive Navigation.

Implementierung

1. Überblick über die Implementierungsschritte

Wir setzen die zuvor beschriebenen Schritte um:

  1. Web Scraping mit Scrapy: Erfassen von Daten von mehreren Webseiten.
  2. Datenhaltung mit MongoDB: Speicherung der Roh- und bereinigten Daten.
  3. Full-Text-Index: Einrichten eines deutschen Index in MongoDB.
  4. KI-Integration mit Teuken-7B: Verarbeitung von Benutzerfragen mit einer lokalen Instanz.
  5. Benutzeroberfläche mit Flask: Web-Interface zur Interaktion mit dem virtuellen Assistenten.

2. Web Scraping: FullSiteSpider

Erstelle einen Scrapy-Spider (spiders/fullsite_spider.py), der mehrere Domains und Seiten crawlt.

import scrapy
from bs4 import BeautifulSoup

class FullSiteSpider(scrapy.Spider):
    name = "fullsite"

    # Liste der erlaubten Domains und Start-URLs
    allowed_domains = ["example.com", "example2.com", "example3.org"]
    start_urls = [
        "https://www.example.com",
        "https://www.example2.com",
        "https://www.example3.org/start"
    ]

    def parse(self, response):
        # Rohdaten speichern
        raw_html = response.body.decode('utf-8')

        # Bereinigung der HTML-Daten
        cleaned_text = self.clean_html(raw_html)

        # Speichern der Daten
        yield {
            'url': response.url,
            'raw_html': raw_html,
            'cleaned_text': cleaned_text,
            'timestamp': response.headers.get('Date', '').decode('utf-8'),
        }

        # Folge allen Links auf der Seite
        for link in response.css('a::attr(href)').getall():
            if link.startswith('http') or link.startswith('/'):
                yield response.follow(link, self.parse)

    def clean_html(self, html_content):
        """Bereinigt HTML und extrahiert lesbaren Text."""
        soup = BeautifulSoup(html_content, 'html.parser')
        text = soup.get_text(separator=" ").strip()
        return " ".join(text.split())

3. Datenhaltung: MongoDB Pipeline

Speichere die gescrapten Daten direkt in MongoDB.

import pymongo
import json

class MongoPipeline:
    def __init__(self):
        # Konfiguration aus Datei laden
        with open('config.json') as config_file:
            config = json.load(config_file)
            self.mongo_uri = config['MONGO_URI']
            self.mongo_db = config['MONGO_DATABASE']

    def open_spider(self, spider):
        # Verbindung zur MongoDB herstellen
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        # Verbindung schließen
        self.client.close()

    def process_item(self, item, spider):
        # Daten in MongoDB speichern
        collection = self.db[spider.name]
        collection.insert_one({
            'url': item['url'],
            'raw_html': item['raw_html'],
            'cleaned_text': item['cleaned_text'],
            'timestamp': item['timestamp'],
        })
        return item

Konfiguration (config.json):

{
  "MONGO_URI": "mongodb://localhost:27017",
  "MONGO_DATABASE": "firmendaten"
}

Aktiviere die Pipeline in settings.py:

ITEM_PIPELINES = {
    'firmendaten.pipelines.MongoPipeline': 300,
}

4. Full-Text-Index in MongoDB

Richte den deutschen Full-Text-Index ein:

use firmendaten;
db.fullsite.createIndex(
  { cleaned_text: "text" },
  { default_language: "german" }
);

5. KI-Integration mit Teuken-7B

Implementiere die Integration in ki_helper.py:

from openai import OpenAI

# Verbindung zur lokalen KI
local_ai = OpenAI(base_url="http://127.0.0.1:1234/v1", api_key="lm-studio")

def generate_response(question, results):
    """
    Generiert eine Antwort mit der lokalen KI basierend auf den MongoDB-Ergebnissen.
    """
    # Kontext aus den MongoDB-Ergebnissen erstellen
    context = "\n".join(
        [f"URL: {doc['url']}\nText: {doc['cleaned_text']}" for doc in results]
    )

    # Nachrichtenformat für die KI
    messages = [
        {"role": "system", "content": "Du bist ein virtueller Assistent für Firmendaten."},
        {"role": "user", "content": f"Hier sind die Daten:\n{context}\n\nFrage: {question}"}
    ]

    # Anfrage an die lokale KI
    response = local_ai.chat.completions.create(
        model="teuken-7b",
        messages=messages,
        temperature=0.7
    )

    return response.choices[0].message.content.strip()

6. Benutzeroberfläche mit Flask

Erstelle die Flask-App (app.py):

from flask import Flask, render_template, request
from pymongo import MongoClient
from ki_helper import generate_response

# Flask-App initialisieren
app = Flask(__name__)

# Verbindung zur MongoDB
client = MongoClient("mongodb://localhost:27017/")
db = client["firmendaten"]
collection = db["fullsite"]

def search_mongodb(question):
    """
    Führt eine Volltextsuche in MongoDB aus und gibt relevante Ergebnisse zurück.
    """
    results = collection.find(
        {"$text": {"$search": question}},
        {"score": {"$meta": "textScore"}}
    ).sort("score", {"$meta": "textScore"}).limit(3)
    return list(results)

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        question = request.form['question']
        results = search_mongodb(question)

        if not results:
            return render_template('result.html', question=question, response="Keine relevanten Daten gefunden.")

        response = generate_response(question, results)
        return render_template('result.html', question=question, response=response)

    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)

HTML-Templates: Siehe GitHub im Ordner webapp.


7. Ausführung und Tests

  1. Scrapy starten:

    scrapy crawl fullsite
  2. Flask-App starten:

    python app.py
  3. App im Browser öffnen:

    • URL: http://127.0.0.1:5000