Warning, file /pim/akonadi-search/agent/agent.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * This file is part of the KDE Akonadi Search Project 0003 * SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in> 0004 * SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 * 0008 */ 0009 0010 #include "agent.h" 0011 0012 #include "akonotesindexer.h" 0013 #include "calendarindexer.h" 0014 #include "collectionupdatejob.h" 0015 #include "contactindexer.h" 0016 #include "emailindexer.h" 0017 #include "indexeradaptor.h" 0018 0019 #include "priority.h" 0020 0021 #include <Akonadi/AttributeFactory> 0022 #include <Akonadi/ChangeRecorder> 0023 #include <Akonadi/CollectionFetchScope> 0024 #include <Akonadi/EntityDisplayAttribute> 0025 #include <Akonadi/IndexPolicyAttribute> 0026 #include <Akonadi/ItemFetchScope> 0027 0028 #include <Akonadi/AgentManager> 0029 #include <Akonadi/ServerManager> 0030 0031 #include "akonadi_indexer_agent_debug.h" 0032 #include <KConfig> 0033 #include <KConfigGroup> 0034 #include <KLocalizedString> 0035 0036 #define INDEXING_AGENT_VERSION 5 0037 0038 AkonadiIndexingAgent::AkonadiIndexingAgent(const QString &id) 0039 : AgentBase(id) 0040 , m_scheduler(m_index, config(), QSharedPointer<JobFactory>(new JobFactory)) 0041 { 0042 lowerIOPriority(); 0043 lowerSchedulingPriority(); 0044 lowerPriority(); 0045 0046 Akonadi::AttributeFactory::registerAttribute<Akonadi::IndexPolicyAttribute>(); 0047 0048 KConfigGroup cfg = config()->group(QStringLiteral("General")); 0049 const int agentIndexingVersion = cfg.readEntry("agentIndexingVersion", 0); 0050 bool respectDiacriticAndAccents = cfg.readEntry("respectDiacriticAndAccents", true); 0051 if (agentIndexingVersion < INDEXING_AGENT_VERSION) { 0052 m_index.removeDatabase(); 0053 // Don't respect Diacritic and Accents in new Database so search will be more easy. 0054 respectDiacriticAndAccents = false; 0055 QTimer::singleShot(0, &m_scheduler, &Scheduler::scheduleCompleteSync); 0056 cfg.writeEntry("agentIndexingVersion", INDEXING_AGENT_VERSION); 0057 cfg.writeEntry("respectDiacriticAndAccents", respectDiacriticAndAccents); 0058 cfg.sync(); 0059 } 0060 m_index.setRespectDiacriticAndAccents(respectDiacriticAndAccents); 0061 if (!m_index.createIndexers()) { 0062 Q_EMIT status(Broken, i18nc("@info:status", "No indexers available")); 0063 setOnline(false); 0064 } else { 0065 setOnline(true); 0066 } 0067 connect(this, &Akonadi::AgentBase::abortRequested, this, &AkonadiIndexingAgent::onAbortRequested); 0068 connect(this, &Akonadi::AgentBase::onlineChanged, this, &AkonadiIndexingAgent::onOnlineChanged); 0069 0070 connect(&m_scheduler, SIGNAL(status(int, QString)), this, SIGNAL(status(int, QString))); 0071 connect(&m_scheduler, &Scheduler::percent, this, &Akonadi::AgentBase::percent); 0072 connect(&m_scheduler, &Scheduler::collectionIndexingFinished, this, &AkonadiIndexingAgent::collectionIndexingFinished); 0073 0074 changeRecorder()->setAllMonitored(true); 0075 changeRecorder()->itemFetchScope().setCacheOnly(true); 0076 changeRecorder()->itemFetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); 0077 changeRecorder()->itemFetchScope().setFetchRemoteIdentification(false); 0078 changeRecorder()->itemFetchScope().setFetchModificationTime(false); 0079 changeRecorder()->itemFetchScope().fetchFullPayload(true); 0080 changeRecorder()->collectionFetchScope().fetchAttribute<Akonadi::IndexPolicyAttribute>(); 0081 changeRecorder()->collectionFetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All); 0082 changeRecorder()->collectionFetchScope().ancestorFetchScope().fetchAttribute<Akonadi::EntityDisplayAttribute>(); 0083 changeRecorder()->collectionFetchScope().setListFilter(Akonadi::CollectionFetchScope::Index); 0084 changeRecorder()->setChangeRecordingEnabled(false); 0085 changeRecorder()->fetchCollection(true); 0086 changeRecorder()->setExclusive(true); 0087 0088 new IndexerAdaptor(this); 0089 0090 // Cleanup agentsrc after migration to 4.13/KF6 0091 Akonadi::AgentManager *agentManager = Akonadi::AgentManager::self(); 0092 const Akonadi::AgentInstance::List allAgents = agentManager->instances(); 0093 // Cannot use agentManager->instance(oldInstanceName) here, it wouldn't find broken instances. 0094 for (const Akonadi::AgentInstance &inst : allAgents) { 0095 if (inst.identifier() == QLatin1StringView("akonadi_nepomuk_feeder")) { 0096 qCDebug(AKONADI_INDEXER_AGENT_LOG) << "Removing old nepomuk feeder" << inst.identifier(); 0097 agentManager->removeInstance(inst); 0098 } else if (inst.identifier() == QLatin1StringView("akonadi_baloo_indexer")) { 0099 qCDebug(AKONADI_INDEXER_AGENT_LOG) << "Removing old Baloo indexer" << inst.identifier(); 0100 agentManager->removeInstance(inst); 0101 } 0102 } 0103 } 0104 0105 AkonadiIndexingAgent::~AkonadiIndexingAgent() = default; 0106 0107 void AkonadiIndexingAgent::reindexAll() 0108 { 0109 qCDebug(AKONADI_INDEXER_AGENT_LOG) << "Reindexing everything"; 0110 m_scheduler.abort(); 0111 m_index.removeDatabase(); 0112 m_index.createIndexers(); 0113 QTimer::singleShot(0, &m_scheduler, &Scheduler::scheduleCompleteSync); 0114 } 0115 0116 void AkonadiIndexingAgent::reindexCollection(const qlonglong id) 0117 { 0118 qCDebug(AKONADI_INDEXER_AGENT_LOG) << "Reindexing collection " << id; 0119 m_scheduler.scheduleCollection(Akonadi::Collection(id), true); 0120 } 0121 0122 void AkonadiIndexingAgent::reindexCollections(const QList<qlonglong> &ids) 0123 { 0124 qCDebug(AKONADI_INDEXER_AGENT_LOG) << "Reindexing collections " << ids; 0125 for (qlonglong id : ids) { 0126 m_scheduler.scheduleCollection(Akonadi::Collection(id), true); 0127 } 0128 } 0129 0130 qlonglong AkonadiIndexingAgent::indexedItems(const qlonglong id) 0131 { 0132 return m_index.indexedItems(id); 0133 } 0134 0135 void AkonadiIndexingAgent::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) 0136 { 0137 if (!shouldIndex(collection)) { 0138 return; 0139 } 0140 0141 m_scheduler.addItem(item); 0142 } 0143 0144 void AkonadiIndexingAgent::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers) 0145 { 0146 if (!shouldIndex(item)) { 0147 return; 0148 } 0149 0150 // We don't index certain parts so we don't care when they change 0151 QSet<QByteArray> pi = partIdentifiers; 0152 QMutableSetIterator<QByteArray> it(pi); 0153 while (it.hasNext()) { 0154 it.next(); 0155 if (!it.value().startsWith("PLD:")) { 0156 it.remove(); 0157 } 0158 } 0159 0160 if (pi.isEmpty()) { 0161 return; 0162 } 0163 m_scheduler.addItem(item); 0164 } 0165 0166 void AkonadiIndexingAgent::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags) 0167 { 0168 // We optimize and skip the "shouldIndex" call for each item here, since it's 0169 // cheaper to just let Xapian throw an exception for items that were not 0170 // indexed. 0171 // In most cases the entire batch comes from the same collection, so we will 0172 // only suffer penalty in case of larger batches from non-indexed collections, 0173 // but we assume that that's a much less common case than collections with 0174 // indexing enabled. 0175 0176 // Akonadi always sends batch of items of the same type 0177 m_index.updateFlags(items, addedFlags, removedFlags); 0178 m_index.scheduleCommit(); 0179 } 0180 0181 void AkonadiIndexingAgent::itemsRemoved(const Akonadi::Item::List &items) 0182 { 0183 // We optimize and skip the "shouldIndex" call for each item here, since it's 0184 // cheaper to just let Xapian throw an exception for items that were not 0185 // indexed instead of filtering the list here. 0186 0187 m_index.remove(items); 0188 m_index.scheduleCommit(); 0189 } 0190 0191 void AkonadiIndexingAgent::itemsMoved(const Akonadi::Item::List &items, 0192 const Akonadi::Collection &sourceCollection, 0193 const Akonadi::Collection &destinationCollection) 0194 { 0195 const bool indexSource = shouldIndex(sourceCollection); 0196 const bool indexDest = shouldIndex(destinationCollection); 0197 0198 if (indexSource && indexDest) { 0199 m_index.move(items, sourceCollection, destinationCollection); 0200 m_index.scheduleCommit(); 0201 } else if (!indexSource && indexDest) { 0202 for (const auto &item : items) { 0203 m_scheduler.addItem(item); 0204 } 0205 m_index.scheduleCommit(); 0206 } else if (indexSource && !indexDest) { 0207 m_index.remove(items); 0208 m_index.scheduleCommit(); 0209 } else { 0210 // nothing to do 0211 } 0212 } 0213 0214 void AkonadiIndexingAgent::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) 0215 { 0216 Q_UNUSED(parent) 0217 0218 if (!shouldIndex(collection)) { 0219 return; 0220 } 0221 0222 m_index.index(collection); 0223 m_index.scheduleCommit(); 0224 } 0225 0226 void AkonadiIndexingAgent::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes) 0227 { 0228 if (changedAttributes.contains(Akonadi::IndexPolicyAttribute().type())) { 0229 const auto attr = collection.attribute<Akonadi::IndexPolicyAttribute>(); 0230 if (attr && !attr->indexingEnabled()) { 0231 // The indexing attribute has changed and is now disabled: remove 0232 // collection and all indexed items 0233 m_index.remove(collection); 0234 } else { 0235 // The indexing attribute has changed and is now missing or enabled, 0236 // schedule full collection sync. 0237 m_scheduler.scheduleCollection(collection, true); 0238 } 0239 } 0240 0241 QSet<QByteArray> changes = changedAttributes; 0242 changes.remove("collectionquota"); 0243 changes.remove("timestamp"); 0244 changes.remove("imapquota"); 0245 0246 if (changes.isEmpty()) { 0247 return; 0248 } 0249 0250 if (changes.contains("ENTITYDISPLAY")) { 0251 // If the name changed we have to reindex all subcollections 0252 auto job = new CollectionUpdateJob(m_index, collection, this); 0253 job->start(); 0254 } else { 0255 m_index.index(collection); 0256 m_index.scheduleCommit(); 0257 } 0258 } 0259 0260 void AkonadiIndexingAgent::collectionRemoved(const Akonadi::Collection &collection) 0261 { 0262 // We intentionally don't call "shouldIndex" here to make absolutely sure 0263 // that all items are wiped from the index 0264 0265 m_index.remove(collection); 0266 m_index.scheduleCommit(); 0267 } 0268 0269 void AkonadiIndexingAgent::collectionMoved(const Akonadi::Collection &collection, 0270 const Akonadi::Collection &collectionSource, 0271 const Akonadi::Collection &collectionDestination) 0272 { 0273 Q_UNUSED(collectionSource) 0274 Q_UNUSED(collectionDestination) 0275 0276 if (!shouldIndex(collection)) { 0277 return; 0278 } 0279 0280 m_index.remove(collection); 0281 auto job = new CollectionUpdateJob(m_index, collection, this); 0282 job->start(); 0283 } 0284 0285 void AkonadiIndexingAgent::cleanup() 0286 { 0287 // Remove all the databases 0288 Akonadi::AgentBase::cleanup(); 0289 } 0290 0291 int AkonadiIndexingAgent::numberOfCollectionQueued() 0292 { 0293 return m_scheduler.numberOfCollectionQueued(); 0294 } 0295 0296 void AkonadiIndexingAgent::onAbortRequested() 0297 { 0298 KConfigGroup group = config()->group(QStringLiteral("General")); 0299 group.writeEntry("aborted", true); 0300 group.sync(); 0301 m_scheduler.abort(); 0302 } 0303 0304 void AkonadiIndexingAgent::onOnlineChanged(bool online) 0305 { 0306 // Ignore everything when offline 0307 changeRecorder()->setAllMonitored(online); 0308 0309 // Index items that might have changed while we were offline 0310 if (online) { 0311 // We only reindex if this is not a regular start 0312 KConfigGroup cfg = config()->group(QStringLiteral("General")); 0313 bool aborted = cfg.readEntry("aborted", false); 0314 if (aborted) { 0315 cfg.writeEntry("aborted", false); 0316 cfg.sync(); 0317 m_scheduler.scheduleCompleteSync(); 0318 } 0319 } else { 0320 // Abort ongoing indexing when switched to offline 0321 onAbortRequested(); 0322 } 0323 } 0324 0325 bool AkonadiIndexingAgent::shouldIndex(const Akonadi::Collection &col) const 0326 { 0327 return !col.isVirtual() && (!col.hasAttribute<Akonadi::IndexPolicyAttribute>() || col.attribute<Akonadi::IndexPolicyAttribute>()->indexingEnabled()); 0328 } 0329 0330 bool AkonadiIndexingAgent::shouldIndex(const Akonadi::Item &item) const 0331 { 0332 return shouldIndex(item.parentCollection()); 0333 } 0334 0335 AKONADI_AGENT_MAIN(AkonadiIndexingAgent) 0336 0337 #include "moc_agent.cpp"