File indexing completed on 2024-05-12 05:11:15

0001 /*
0002  * This file is part of the KDE Akonadi Search Project
0003  * SPDX-FileCopyrightText: 2014-2024 Laurent Montel <montel@kde.org>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006  *
0007  */
0008 
0009 #include "calendarindexer.h"
0010 #include "akonadi_indexer_agent_calendar_debug.h"
0011 #include "xapiandocument.h"
0012 
0013 #include <KCalendarCore/Attendee>
0014 #include <KCalendarCore/FreeBusy>
0015 
0016 CalendarIndexer::CalendarIndexer(const QString &path)
0017     : AbstractIndexer()
0018 {
0019     try {
0020         m_db = std::make_unique<Akonadi::Search::XapianDatabase>(path, true);
0021     } catch (const Xapian::DatabaseCorruptError &err) {
0022         qCWarning(AKONADI_INDEXER_AGENT_CALENDAR_LOG) << "Database Corrupted - What did you do?";
0023         qCWarning(AKONADI_INDEXER_AGENT_CALENDAR_LOG) << err.get_error_string();
0024         m_db = nullptr;
0025     } catch (const Xapian::Error &e) {
0026         qCWarning(AKONADI_INDEXER_AGENT_CALENDAR_LOG) << QString::fromStdString(e.get_type()) << QString::fromStdString(e.get_description());
0027         m_db = nullptr;
0028     }
0029 }
0030 
0031 CalendarIndexer::~CalendarIndexer()
0032 {
0033     commit();
0034 }
0035 
0036 QStringList CalendarIndexer::mimeTypes() const
0037 {
0038     return {
0039         KCalendarCore::Event::eventMimeType(),
0040         KCalendarCore::Todo::todoMimeType(),
0041         KCalendarCore::Journal::journalMimeType(),
0042         KCalendarCore::FreeBusy::freeBusyMimeType(),
0043     };
0044 }
0045 
0046 void CalendarIndexer::index(const Akonadi::Item &item)
0047 {
0048     if (item.hasPayload<KCalendarCore::Event::Ptr>()) {
0049         indexEventItem(item, item.payload<KCalendarCore::Event::Ptr>());
0050     } else if (item.hasPayload<KCalendarCore::Journal::Ptr>()) {
0051         indexJournalItem(item, item.payload<KCalendarCore::Journal::Ptr>());
0052     } else if (item.hasPayload<KCalendarCore::Todo::Ptr>()) {
0053         indexTodoItem(item, item.payload<KCalendarCore::Todo::Ptr>());
0054     } else {
0055         return;
0056     }
0057 }
0058 
0059 void CalendarIndexer::commit()
0060 {
0061     if (!m_db) {
0062         return;
0063     }
0064 
0065     try {
0066         m_db->commit();
0067     } catch (const Xapian::Error &err) {
0068         qCWarning(AKONADI_INDEXER_AGENT_CALENDAR_LOG) << err.get_error_string();
0069     }
0070     qCDebug(AKONADI_INDEXER_AGENT_CALENDAR_LOG) << "Xapian Committed";
0071 }
0072 
0073 void CalendarIndexer::remove(const Akonadi::Item &item)
0074 {
0075     if (!m_db) {
0076         return;
0077     }
0078     try {
0079         m_db->deleteDocument(item.id());
0080     } catch (const Xapian::DocNotFoundError &) {
0081         return;
0082     }
0083 }
0084 
0085 void CalendarIndexer::remove(const Akonadi::Collection &collection)
0086 {
0087     if (!m_db) {
0088         return;
0089     }
0090     try {
0091         const Xapian::Query query('C' + QString::number(collection.id()).toStdString());
0092         Xapian::Enquire enquire(*(m_db->db()));
0093         enquire.set_query(query);
0094 
0095         Xapian::MSet mset = enquire.get_mset(0, m_db->db()->get_doccount());
0096         Xapian::MSetIterator end(mset.end());
0097         for (Xapian::MSetIterator it = mset.begin(); it != end; ++it) {
0098             const qint64 id = *it;
0099             remove(Akonadi::Item(id));
0100         }
0101     } catch (const Xapian::DocNotFoundError &) {
0102         return;
0103     }
0104 }
0105 
0106 void CalendarIndexer::move(Akonadi::Item::Id itemId, Akonadi::Collection::Id from, Akonadi::Collection::Id to)
0107 {
0108     if (!m_db) {
0109         return;
0110     }
0111     Xapian::Document doc;
0112     try {
0113         doc = m_db->db()->get_document(itemId);
0114     } catch (const Xapian::DocNotFoundError &) {
0115         return;
0116     }
0117 
0118     const QByteArray ft = 'C' + QByteArray::number(from);
0119     const QByteArray tt = 'C' + QByteArray::number(to);
0120 
0121     doc.remove_term(ft.data());
0122     doc.add_boolean_term(tt.data());
0123     m_db->replaceDocument(doc.get_docid(), doc);
0124 }
0125 
0126 void CalendarIndexer::indexEventItem(const Akonadi::Item &item, const KCalendarCore::Event::Ptr &event)
0127 {
0128     qCDebug(AKONADI_INDEXER_AGENT_CALENDAR_LOG) << "Indexing calendar event:" << normalizeString(event->summary()) << event->organizer().email();
0129 
0130     Akonadi::Search::XapianDocument doc;
0131 
0132     doc.indexText(event->organizer().email(), QStringLiteral("O"));
0133     doc.indexText(normalizeString(event->summary()), QStringLiteral("S"));
0134     doc.indexText(normalizeString(event->location()), QStringLiteral("L"));
0135     const KCalendarCore::Attendee::List attendees = event->attendees();
0136     for (const KCalendarCore::Attendee &attendee : attendees) {
0137         doc.addBoolTerm(attendee.email() + QString::number(attendee.status()), QStringLiteral("PS"));
0138     }
0139 
0140     // Parent collection
0141     Q_ASSERT_X(item.parentCollection().isValid(), "Akonadi::Search::CalenderIndexer::index", "Item does not have a valid parent collection");
0142 
0143     const Akonadi::Collection::Id colId = item.parentCollection().id();
0144     doc.addBoolTerm(colId, QStringLiteral("C"));
0145 
0146     m_db->replaceDocument(item.id(), doc);
0147 }
0148 
0149 void CalendarIndexer::indexJournalItem(const Akonadi::Item &item, const KCalendarCore::Journal::Ptr &journal)
0150 {
0151     // TODO
0152     Q_UNUSED(item)
0153     Q_UNUSED(journal)
0154 }
0155 
0156 void CalendarIndexer::indexTodoItem(const Akonadi::Item &item, const KCalendarCore::Todo::Ptr &todo)
0157 {
0158     // TODO
0159     Q_UNUSED(item)
0160     Q_UNUSED(todo)
0161 }
0162 
0163 void CalendarIndexer::updateIncidenceItem(const KCalendarCore::Incidence::Ptr &calInc)
0164 {
0165     // TODO
0166     Q_UNUSED(calInc)
0167 }