223 lines
6.4 KiB
C++
223 lines
6.4 KiB
C++
#include "databasemanager.h"
|
|
#include <QSqlQuery>
|
|
#include <QSqlError>
|
|
#include <QDebug>
|
|
#include <QVariant>
|
|
#include <QFileInfo>
|
|
|
|
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::ImageItem> DatabaseManager::getAllImages()
|
|
{
|
|
QList<ImageItem> 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::ImageItem> DatabaseManager::searchImages(const QString &searchText)
|
|
{
|
|
QList<ImageItem> 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;
|
|
} |