File indexing completed on 2024-05-26 05:27:32
0001 /* 0002 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm> 0003 * 0004 * This program is free software; you can redistribute it and/or modify 0005 * it under the terms of the GNU General Public License as published by 0006 * the Free Software Foundation; either version 2 of the License, or 0007 * (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU General Public License 0015 * along with this program; if not, write to the 0016 * Free Software Foundation, Inc., 0017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 0018 */ 0019 #include "threadindexer.h" 0020 0021 #include "typeindex.h" 0022 #include "log.h" 0023 #include "utils.h" 0024 0025 using namespace Sink; 0026 using namespace Sink::ApplicationDomain; 0027 0028 void ThreadIndexer::updateThreadingIndex(const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) 0029 { 0030 const auto messageId = entity.getProperty(Mail::MessageId::name); 0031 if (messageId.toByteArray().isEmpty()) { 0032 SinkWarning() << "Found an email without messageId. This is illegal and threading will break. Entity id: " << entity.identifier(); 0033 } 0034 const auto parentMessageIds = entity.getProperty(Mail::ParentMessageIds::name).value<QByteArrayList>(); 0035 0036 SinkTrace() << "Indexing thread. Entity: " << entity.identifier() << "Messageid: " << messageId << "ParentMessageId: " << parentMessageIds; 0037 0038 //check if a child already registered our thread. 0039 QVector<QByteArray> thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId); 0040 0041 if (!thread.isEmpty() && !parentMessageIds.isEmpty()) { 0042 //A child already registered our thread so we merge the childs thread 0043 //* check if we have a parent thread, if not just continue as usual 0044 //* get all messages that have the same threadid as the child 0045 //* switch all to the parents thread 0046 for (const auto &parentMessageId : parentMessageIds) { 0047 auto parentThread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); 0048 if (!parentThread.isEmpty()) { 0049 auto childThreadId = thread.first(); 0050 auto parentThreadId = parentThread.first(); 0051 //Can happen if the message is already available locally. 0052 if (childThreadId == parentThreadId) { 0053 //Nothing to do 0054 return; 0055 } 0056 SinkTrace() << "Merging child thread: " << childThreadId << " into parent thread: " << parentThreadId; 0057 0058 //Ensure this mail ends up in the correct thread 0059 index().unindex<Mail::MessageId, Mail::ThreadId>(messageId, childThreadId, transaction); 0060 //We have to copy the id here, otherwise it doesn't survive the subsequent writes 0061 thread = {QByteArray{parentThreadId.data(), parentThreadId.size()}}; 0062 0063 //Merge all child messages into the correct thread 0064 auto childThreadMessageIds = index().secondaryLookup<Mail::ThreadId, Mail::MessageId>(childThreadId); 0065 for (const auto &msgId : childThreadMessageIds) { 0066 SinkTrace() << "Merging child message: " << msgId; 0067 index().unindex<Mail::MessageId, Mail::ThreadId>(msgId, childThreadId, transaction); 0068 index().unindex<Mail::ThreadId, Mail::MessageId>(childThreadId, msgId, transaction); 0069 index().index<Mail::MessageId, Mail::ThreadId>(msgId, parentThreadId, transaction); 0070 index().index<Mail::ThreadId, Mail::MessageId>(parentThreadId, msgId, transaction); 0071 } 0072 break; 0073 } 0074 } 0075 } 0076 0077 //If parent is already available, add to thread of the first parent to match 0078 if (thread.isEmpty() && !parentMessageIds.isEmpty()) { 0079 for (const auto &parentMessageId : parentMessageIds) { 0080 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); 0081 if (!thread.isEmpty()) { 0082 break; 0083 } 0084 } 0085 SinkTrace() << "Found parent: " << thread; 0086 } 0087 0088 //Create a new thread as last resort 0089 if (thread.isEmpty()) { 0090 thread = {Sink::createUuid()}; 0091 SinkTrace() << "Created a new thread: " << thread; 0092 } 0093 0094 if (!parentMessageIds.isEmpty()) { 0095 //Register the first (closest) parent with thread for when it becomes available 0096 index().index<Mail::MessageId, Mail::ThreadId>(parentMessageIds.first(), thread.first(), transaction); 0097 } 0098 index().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction); 0099 index().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction); 0100 } 0101 0102 0103 void ThreadIndexer::add(const ApplicationDomain::ApplicationDomainType &entity) 0104 { 0105 updateThreadingIndex(entity, transaction()); 0106 } 0107 0108 void ThreadIndexer::modify(const ApplicationDomain::ApplicationDomainType &oldEntity, const ApplicationDomain::ApplicationDomainType &newEntity) 0109 { 0110 //TODO Implement to support thread changes. 0111 //Emails are immutable (for everything threading relevant), so we don't care about it so far. 0112 } 0113 0114 void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity) 0115 { 0116 const auto messageId = entity.getProperty(Mail::MessageId::name); 0117 const auto thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId); 0118 if (thread.isEmpty()) { 0119 SinkWarning() << "Failed to find the threadId for the entity " << entity.identifier() << messageId; 0120 return; 0121 } 0122 index().unindex<Mail::MessageId, Mail::ThreadId>(messageId.toByteArray(), thread.first(), transaction()); 0123 index().unindex<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId.toByteArray(), transaction()); 0124 } 0125 0126 QMap<QByteArray, int> ThreadIndexer::databases() 0127 { 0128 return {{"mail.index.messageIdthreadId", Sink::Storage::AllowDuplicates}, 0129 {"mail.index.threadIdmessageId", Sink::Storage::AllowDuplicates}}; 0130 } 0131