#include "databasemanager.h" #include #include #include #include #include DatabaseManager::DatabaseManager(QObject *parent) : QObject(parent) , m_initialized(false) { // Initialize search cache m_searchCache.clear(); // Create index on ocr_text if it doesn't exist // This will be executed once the database is initialized } DatabaseManager::~DatabaseManager() { if (m_db.isOpen()) { m_db.close(); } } bool DatabaseManager::initialize(const QString &dbPath) { // Check if database is already initialized if (m_initialized) { return true; } // Check if file exists QFileInfo fileInfo(dbPath); if (!fileInfo.exists() || !fileInfo.isFile()) { qDebug() << "Database file does not exist:" << dbPath; return false; } // Set up database connection m_db = QSqlDatabase::addDatabase("QSQLITE"); m_db.setDatabaseName(dbPath); // Open database if (!m_db.open()) { qDebug() << "Failed to open database:" << m_db.lastError().text(); return false; } // Verify required table exists QSqlQuery query; if (!query.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='ocr_results'")) { qDebug() << "Failed to execute query:" << query.lastError().text(); m_db.close(); return false; } if (!query.next()) { qDebug() << "The required table 'ocr_results' does not exist in the database."; m_db.close(); return false; } // Verify the table has the required columns if (!query.exec("PRAGMA table_info(ocr_results)")) { qDebug() << "Failed to get table info:" << query.lastError().text(); m_db.close(); return false; } bool hasFullPath = false; bool hasOcrText = false; while (query.next()) { QString columnName = query.value(1).toString(); if (columnName == "full_path") hasFullPath = true; if (columnName == "ocr_text") hasOcrText = true; } if (!hasFullPath || !hasOcrText) { qDebug() << "Missing required columns in ocr_results table. Need 'full_path' and 'ocr_text'"; m_db.close(); return false; } // Create an index on the ocr_text column if it doesn't exist // This will speed up text searches dramatically query.exec("CREATE INDEX IF NOT EXISTS idx_ocr_text ON ocr_results(ocr_text)"); m_initialized = true; qDebug() << "Database initialized successfully."; return true; } QList DatabaseManager::getAllImages() { QList images; if (!m_initialized) { qDebug() << "Database not initialized."; return images; } // Verify database is still connected if (!m_db.isOpen() && !m_db.open()) { qDebug() << "Database connection lost and cannot be reopened:" << m_db.lastError().text(); m_initialized = false; return images; } // Start transaction to speed up query m_db.transaction(); QSqlQuery query; query.prepare("SELECT full_path, ocr_text FROM ocr_results"); if (!query.exec()) { qDebug() << "Failed to fetch images:" << query.lastError().text(); return images; } // Check if files exist as we add them // Reserve space for results to avoid reallocations images.reserve(query.size() > 0 ? query.size() : 100); while (query.next()) { ImageItem item; item.filePath = query.value(0).toString(); item.ocrText = query.value(1).toString(); // Only add images that have a non-empty path if (!item.filePath.isEmpty()) { images.append(item); } } m_db.commit(); return images; } QList DatabaseManager::searchImages(const QString &searchText) { QList images; if (!m_initialized) { qDebug() << "Database not initialized."; return images; } // Verify database is still connected if (!m_db.isOpen() && !m_db.open()) { qDebug() << "Database connection lost and cannot be reopened:" << m_db.lastError().text(); m_initialized = false; return images; } // If search text is empty, return all images if (searchText.isEmpty()) { // Clear the search cache when empty search is performed m_searchCache.clear(); return getAllImages(); } // Check if we have a cached result for this search query if (m_searchCache.contains(searchText)) { return m_searchCache[searchText]; } // Start transaction to speed up queries m_db.transaction(); QSqlQuery query; // Optimize the query based on length of search text if (searchText.length() <= 3) { // For short search terms, use a more targeted approach query.prepare("SELECT full_path, ocr_text FROM ocr_results WHERE ocr_text LIKE :search"); query.bindValue(":search", "%" + searchText + "%"); } else { // For longer search terms, use LIKE with a more specific pattern at start // which can utilize indexes better if they exist query.prepare("SELECT full_path, ocr_text FROM ocr_results WHERE ocr_text LIKE :search OR ocr_text LIKE :wordstart"); query.bindValue(":search", "%" + searchText + "%"); query.bindValue(":wordstart", "% " + searchText + "%"); } if (!query.exec()) { qDebug() << "Failed to search images:" << query.lastError().text(); m_db.rollback(); return images; } // Reserve space for results to avoid reallocations images.reserve(query.size() > 0 ? query.size() : 100); while (query.next()) { ImageItem item; item.filePath = query.value(0).toString(); item.ocrText = query.value(1).toString(); // Only add images that have a non-empty path if (!item.filePath.isEmpty()) { images.append(item); } } m_db.commit(); // Cache the result for future queries if (images.size() > 0) { m_searchCache.insert(searchText, images); // Limit cache size to avoid memory issues if (m_searchCache.size() > MAX_CACHE_SIZE) { // Remove the first key (oldest entry) if (!m_searchCache.isEmpty()) { m_searchCache.remove(m_searchCache.firstKey()); } } } return images; }