summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--common/CMakeLists.txt3
-rw-r--r--common/datastorequery.cpp4
-rw-r--r--common/definitions.cpp2
-rw-r--r--common/domain/typeimplementations.cpp4
-rw-r--r--common/fulltextindex.cpp149
-rw-r--r--common/fulltextindex.h39
-rw-r--r--common/indexer.cpp3
-rw-r--r--common/indexer.h6
-rw-r--r--common/mail/fulltextindexer.cpp64
-rw-r--r--common/mail/fulltextindexer.h39
-rw-r--r--common/mail/threadindexer.cpp5
-rw-r--r--common/mail/threadindexer.h1
-rw-r--r--common/mailpreprocessor.cpp40
-rw-r--r--common/mailpreprocessor.h1
-rw-r--r--common/query.cpp3
-rw-r--r--common/query.h3
-rw-r--r--common/storage/entitystore.cpp22
-rw-r--r--common/typeindex.cpp46
-rw-r--r--common/typeindex.h11
-rw-r--r--synchronizer/CMakeLists.txt1
-rw-r--r--synchronizer/main.cpp4
-rw-r--r--tests/dummyresourcewritebenchmark.cpp42
-rw-r--r--tests/querytest.cpp90
24 files changed, 519 insertions, 64 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb6df73..3327afd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,6 +36,7 @@ find_package(KF5 COMPONENTS REQUIRED Mime Contacts)
36find_package(FlatBuffers REQUIRED 1.4.0) 36find_package(FlatBuffers REQUIRED 1.4.0)
37find_package(KAsync REQUIRED 0.1.2) 37find_package(KAsync REQUIRED 0.1.2)
38find_package(LMDB REQUIRED 0.9) 38find_package(LMDB REQUIRED 0.9)
39find_package(Xapian REQUIRED 1.4)
39 40
40if (${ENABLE_MEMCHECK}) 41if (${ENABLE_MEMCHECK})
41 message("Enabled memcheck") 42 message("Enabled memcheck")
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index ec83f6f..76579dd 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -75,11 +75,13 @@ set(command_SRCS
75 storage/entitystore.cpp 75 storage/entitystore.cpp
76 indexer.cpp 76 indexer.cpp
77 mail/threadindexer.cpp 77 mail/threadindexer.cpp
78 mail/fulltextindexer.cpp
78 notification.cpp 79 notification.cpp
79 commandprocessor.cpp 80 commandprocessor.cpp
80 inspector.cpp 81 inspector.cpp
81 propertyparser.cpp 82 propertyparser.cpp
82 utils.cpp 83 utils.cpp
84 fulltextindex.cpp
83 ${storage_SRCS}) 85 ${storage_SRCS})
84 86
85add_library(${PROJECT_NAME} SHARED ${command_SRCS}) 87add_library(${PROJECT_NAME} SHARED ${command_SRCS})
@@ -127,6 +129,7 @@ PRIVATE
127 Qt5::Gui 129 Qt5::Gui
128 KF5::Mime 130 KF5::Mime
129 KF5::Contacts 131 KF5::Contacts
132 ${XAPIAN_LIBRARIES}
130) 133)
131install(TARGETS ${PROJECT_NAME} 134install(TARGETS ${PROJECT_NAME}
132 EXPORT SinkTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ${LIBRARY_NAMELINK} ) 135 EXPORT SinkTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ${LIBRARY_NAMELINK} )
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp
index 870daf8..3218d1a 100644
--- a/common/datastorequery.cpp
+++ b/common/datastorequery.cpp
@@ -160,6 +160,10 @@ public:
160 for (const auto &filterProperty : propertyFilter.keys()) { 160 for (const auto &filterProperty : propertyFilter.keys()) {
161 const auto property = entity.getProperty(filterProperty); 161 const auto property = entity.getProperty(filterProperty);
162 const auto comparator = propertyFilter.value(filterProperty); 162 const auto comparator = propertyFilter.value(filterProperty);
163 //We can't deal with a fulltext filter
164 if (comparator.comparator == QueryBase::Comparator::Fulltext) {
165 continue;
166 }
163 if (!comparator.matches(property)) { 167 if (!comparator.matches(property)) {
164 SinkTraceCtx(mDatastore->mLogCtx) << "Filtering entity due to property mismatch on filter: " << entity.identifier() << "Property: " << filterProperty << property << " Filter:" << comparator.value; 168 SinkTraceCtx(mDatastore->mLogCtx) << "Filtering entity due to property mismatch on filter: " << entity.identifier() << "Property: " << filterProperty << property << " Filter:" << comparator.value;
165 return false; 169 return false;
diff --git a/common/definitions.cpp b/common/definitions.cpp
index b22137a..642b68c 100644
--- a/common/definitions.cpp
+++ b/common/definitions.cpp
@@ -90,5 +90,5 @@ QString Sink::resourceStorageLocation(const QByteArray &resourceInstanceIdentifi
90 90
91qint64 Sink::latestDatabaseVersion() 91qint64 Sink::latestDatabaseVersion()
92{ 92{
93 return 1; 93 return 2;
94} 94}
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp
index 47a9cf7..29da7ea 100644
--- a/common/domain/typeimplementations.cpp
+++ b/common/domain/typeimplementations.cpp
@@ -27,6 +27,7 @@
27#include "entitybuffer.h" 27#include "entitybuffer.h"
28#include "entity_generated.h" 28#include "entity_generated.h"
29#include "mail/threadindexer.h" 29#include "mail/threadindexer.h"
30#include "mail/fulltextindexer.h"
30#include "domainadaptor.h" 31#include "domainadaptor.h"
31#include "typeimplementations_p.h" 32#include "typeimplementations_p.h"
32 33
@@ -45,7 +46,8 @@ typedef IndexConfig<Mail,
45 SortedIndex<Mail::Folder, Mail::Date>, 46 SortedIndex<Mail::Folder, Mail::Date>,
46 SecondaryIndex<Mail::MessageId, Mail::ThreadId>, 47 SecondaryIndex<Mail::MessageId, Mail::ThreadId>,
47 SecondaryIndex<Mail::ThreadId, Mail::MessageId>, 48 SecondaryIndex<Mail::ThreadId, Mail::MessageId>,
48 CustomSecondaryIndex<Mail::MessageId, Mail::ThreadId, ThreadIndexer> 49 CustomSecondaryIndex<Mail::MessageId, Mail::ThreadId, ThreadIndexer>,
50 CustomSecondaryIndex<Mail::Subject, Mail::Subject, FulltextIndexer>
49 > MailIndexConfig; 51 > MailIndexConfig;
50 52
51typedef IndexConfig<Folder, 53typedef IndexConfig<Folder,
diff --git a/common/fulltextindex.cpp b/common/fulltextindex.cpp
new file mode 100644
index 0000000..33972b7
--- /dev/null
+++ b/common/fulltextindex.cpp
@@ -0,0 +1,149 @@
1/*
2 * Copyright (C) 2018 Christian Mollekopf <mollekopf@kolabsys.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19//xapian.h needs to be included first to build
20#include <xapian.h>
21#include "fulltextindex.h"
22
23#include <QFile>
24#include <QDir>
25
26#include "log.h"
27#include "definitions.h"
28
29FulltextIndex::FulltextIndex(const QByteArray &resourceInstanceIdentifier, Sink::Storage::DataStore::AccessMode accessMode)
30 : mName("fulltext"),
31 mDbPath{QFile::encodeName(Sink::resourceStorageLocation(resourceInstanceIdentifier) + '/' + "fulltext")}
32{
33 try {
34 if (QDir{}.mkpath(mDbPath)) {
35 if (accessMode == Sink::Storage::DataStore::ReadWrite) {
36 mDb = new Xapian::WritableDatabase(mDbPath.toStdString(), Xapian::DB_CREATE_OR_OPEN);
37 } else {
38 mDb = new Xapian::Database(mDbPath.toStdString(), Xapian::DB_OPEN);
39 }
40 } else {
41 SinkError() << "Failed to open database" << mDbPath;
42 }
43 } catch (const Xapian::DatabaseError& e) {
44 SinkError() << "Failed to open database" << mDbPath << ":" << QString::fromStdString(e.get_msg());
45 }
46}
47
48FulltextIndex::~FulltextIndex()
49{
50 delete mDb;
51}
52
53static std::string idTerm(const QByteArray &key)
54{
55 return "Q" + key.toStdString();
56}
57
58void FulltextIndex::add(const QByteArray &key, const QString &value)
59{
60 add(key, {{{}, value}});
61}
62
63void FulltextIndex::add(const QByteArray &key, const QList<QPair<QString, QString>> &values)
64{
65 if (!mDb) {
66 return;
67 }
68 Xapian::TermGenerator generator;
69 Xapian::Document document;
70 generator.set_document(document);
71
72 for (const auto &entry : values) {
73 if (!entry.second.isEmpty()) {
74 generator.index_text(entry.second.toStdString());
75 }
76 }
77 document.add_value(0, key.toStdString());
78
79 const auto idterm = idTerm(key);
80 document.add_boolean_term(idterm);
81
82 writableDatabase()->replace_document(idterm, document);
83}
84
85void FulltextIndex::commitTransaction()
86{
87 if (mHasTransactionOpen) {
88 Q_ASSERT(mDb);
89 writableDatabase()->commit_transaction();
90 mHasTransactionOpen = false;
91 }
92}
93
94void FulltextIndex::abortTransaction()
95{
96 if (mHasTransactionOpen) {
97 Q_ASSERT(mDb);
98 writableDatabase()->cancel_transaction();
99 mHasTransactionOpen = false;
100 }
101}
102
103Xapian::WritableDatabase* FulltextIndex::writableDatabase()
104{
105 Q_ASSERT(dynamic_cast<Xapian::WritableDatabase*>(mDb));
106 auto db = static_cast<Xapian::WritableDatabase*>(mDb);
107 if (!mHasTransactionOpen) {
108 db->begin_transaction();
109 mHasTransactionOpen = true;
110 }
111 return db;
112}
113
114void FulltextIndex::remove(const QByteArray &key)
115{
116 if (!mDb) {
117 return;
118 }
119 writableDatabase()->delete_document(idTerm(key));
120}
121
122QVector<QByteArray> FulltextIndex::lookup(const QString &searchTerm)
123{
124 if (!mDb) {
125 return {};
126 }
127 QVector<QByteArray> results;
128
129 try {
130 Xapian::QueryParser parser;
131 auto query = parser.parse_query(searchTerm.toStdString(), Xapian::QueryParser::FLAG_WILDCARD|Xapian::QueryParser::FLAG_PHRASE|Xapian::QueryParser::FLAG_BOOLEAN|Xapian::QueryParser::FLAG_LOVEHATE);
132 Xapian::Enquire enquire(*mDb);
133 enquire.set_query(query);
134
135 auto limit = 1000;
136 Xapian::MSet mset = enquire.get_mset(0, limit);
137 Xapian::MSetIterator it = mset.begin();
138 for (;it != mset.end(); it++) {
139 auto doc = it.get_document();
140 const auto data = doc.get_value(0);
141 results << QByteArray{data.c_str(), int(data.length())};
142 }
143 }
144 catch (const Xapian::Error &error) {
145 // Nothing to do, move along
146 }
147 return results;
148}
149
diff --git a/common/fulltextindex.h b/common/fulltextindex.h
new file mode 100644
index 0000000..e06f29d
--- /dev/null
+++ b/common/fulltextindex.h
@@ -0,0 +1,39 @@
1#pragma once
2
3#include "sink_export.h"
4
5#include <string>
6#include <functional>
7#include <QString>
8#include <memory>
9#include "storage.h"
10#include "log.h"
11
12namespace Xapian {
13 class Database;
14 class WritableDatabase;
15};
16
17class SINK_EXPORT FulltextIndex
18{
19public:
20 FulltextIndex(const QByteArray &resourceInstanceIdentifier, Sink::Storage::DataStore::AccessMode mode = Sink::Storage::DataStore::ReadOnly);
21 ~FulltextIndex();
22
23 void add(const QByteArray &key, const QString &value);
24 void add(const QByteArray &key, const QList<QPair<QString, QString>> &values);
25 void remove(const QByteArray &key);
26
27 void commitTransaction();
28 void abortTransaction();
29
30 QVector<QByteArray> lookup(const QString &key);
31
32private:
33 Xapian::WritableDatabase* writableDatabase();
34 Q_DISABLE_COPY(FulltextIndex);
35 Xapian::Database *mDb{nullptr};
36 QString mName;
37 QString mDbPath;
38 bool mHasTransactionOpen{false};
39};
diff --git a/common/indexer.cpp b/common/indexer.cpp
index 1b223b3..c18170c 100644
--- a/common/indexer.cpp
+++ b/common/indexer.cpp
@@ -20,10 +20,11 @@
20 20
21using namespace Sink; 21using namespace Sink;
22 22
23void Indexer::setup(TypeIndex *index, Storage::DataStore::Transaction *transaction) 23void Indexer::setup(TypeIndex *index, Storage::DataStore::Transaction *transaction, const QByteArray &resourceId)
24{ 24{
25 mTypeIndex = index; 25 mTypeIndex = index;
26 mTransaction = transaction; 26 mTransaction = transaction;
27 mResourceInstanceIdentifier = resourceId;
27} 28}
28 29
29Storage::DataStore::Transaction &Indexer::transaction() 30Storage::DataStore::Transaction &Indexer::transaction()
diff --git a/common/indexer.h b/common/indexer.h
index 26887fb..f0b32f5 100644
--- a/common/indexer.h
+++ b/common/indexer.h
@@ -33,16 +33,18 @@ public:
33 virtual ~Indexer() = default; 33 virtual ~Indexer() = default;
34 typedef QSharedPointer<Indexer> Ptr; 34 typedef QSharedPointer<Indexer> Ptr;
35 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) = 0; 35 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) = 0;
36 virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) = 0;
37 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) = 0; 36 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) = 0;
37 virtual void commitTransaction() {};
38 virtual void abortTransaction() {};
38 39
39protected: 40protected:
40 Storage::DataStore::Transaction &transaction(); 41 Storage::DataStore::Transaction &transaction();
41 TypeIndex &index(); 42 TypeIndex &index();
43 QByteArray mResourceInstanceIdentifier;
42 44
43private: 45private:
44 friend class ::TypeIndex; 46 friend class ::TypeIndex;
45 void setup(TypeIndex *, Storage::DataStore::Transaction *); 47 void setup(TypeIndex *, Storage::DataStore::Transaction *, const QByteArray &resourceId);
46 Storage::DataStore::Transaction *mTransaction; 48 Storage::DataStore::Transaction *mTransaction;
47 TypeIndex *mTypeIndex; 49 TypeIndex *mTypeIndex;
48}; 50};
diff --git a/common/mail/fulltextindexer.cpp b/common/mail/fulltextindexer.cpp
new file mode 100644
index 0000000..a980752
--- /dev/null
+++ b/common/mail/fulltextindexer.cpp
@@ -0,0 +1,64 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#include "fulltextindexer.h"
20
21#include "typeindex.h"
22#include "fulltextindex.h"
23#include "log.h"
24#include "utils.h"
25
26using namespace Sink;
27using namespace Sink::ApplicationDomain;
28
29
30void FulltextIndexer::add(const ApplicationDomain::ApplicationDomainType &entity)
31{
32 if (!index) {
33 index.reset(new FulltextIndex{mResourceInstanceIdentifier, Storage::DataStore::ReadWrite});
34 }
35 index->add(entity.identifier(), entity.getProperty("index").value<QList<QPair<QString, QString>>>());
36}
37
38void FulltextIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity)
39{
40 if (!index) {
41 index.reset(new FulltextIndex{mResourceInstanceIdentifier, Storage::DataStore::ReadWrite});
42 }
43 index->remove(entity.identifier());
44}
45
46void FulltextIndexer::commitTransaction()
47{
48 if (index) {
49 index->commitTransaction();
50 }
51}
52
53void FulltextIndexer::abortTransaction()
54{
55 if (index) {
56 index->abortTransaction();
57 }
58}
59
60QMap<QByteArray, int> FulltextIndexer::databases()
61{
62 return {};
63}
64
diff --git a/common/mail/fulltextindexer.h b/common/mail/fulltextindexer.h
new file mode 100644
index 0000000..ef41455
--- /dev/null
+++ b/common/mail/fulltextindexer.h
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include "indexer.h"
22
23class FulltextIndex;
24namespace Sink {
25
26class FulltextIndexer : public Indexer
27{
28public:
29 typedef QSharedPointer<FulltextIndexer> Ptr;
30 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
31 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
32 virtual void commitTransaction() Q_DECL_OVERRIDE;
33 virtual void abortTransaction() Q_DECL_OVERRIDE;
34 static QMap<QByteArray, int> databases();
35private:
36 QSharedPointer<FulltextIndex> index;
37};
38
39}
diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp
index 2e6a6e7..fb47118 100644
--- a/common/mail/threadindexer.cpp
+++ b/common/mail/threadindexer.cpp
@@ -98,11 +98,6 @@ void ThreadIndexer::add(const ApplicationDomain::ApplicationDomainType &entity)
98 updateThreadingIndex(entity.identifier(), entity, transaction()); 98 updateThreadingIndex(entity.identifier(), entity, transaction());
99} 99}
100 100
101void ThreadIndexer::modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity)
102{
103
104}
105
106void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity) 101void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity)
107{ 102{
108 auto messageId = entity.getProperty(Mail::MessageId::name); 103 auto messageId = entity.getProperty(Mail::MessageId::name);
diff --git a/common/mail/threadindexer.h b/common/mail/threadindexer.h
index 60d0863..b2e939a 100644
--- a/common/mail/threadindexer.h
+++ b/common/mail/threadindexer.h
@@ -27,7 +27,6 @@ class ThreadIndexer : public Indexer
27public: 27public:
28 typedef QSharedPointer<ThreadIndexer> Ptr; 28 typedef QSharedPointer<ThreadIndexer> Ptr;
29 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; 29 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
30 virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
31 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; 30 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
32 static QMap<QByteArray, int> databases(); 31 static QMap<QByteArray, int> databases();
33private: 32private:
diff --git a/common/mailpreprocessor.cpp b/common/mailpreprocessor.cpp
index 8f5a77d..58cb15b 100644
--- a/common/mailpreprocessor.cpp
+++ b/common/mailpreprocessor.cpp
@@ -21,9 +21,11 @@
21 21
22#include <QFile> 22#include <QFile>
23#include <QDir> 23#include <QDir>
24#include <QTextDocument>
24#include <KMime/KMime/KMimeMessage> 25#include <KMime/KMime/KMimeMessage>
25 26
26#include "pipeline.h" 27#include "pipeline.h"
28#include "fulltextindex.h"
27#include "definitions.h" 29#include "definitions.h"
28#include "applicationdomaintype.h" 30#include "applicationdomaintype.h"
29 31
@@ -45,13 +47,34 @@ static QList<Sink::ApplicationDomain::Mail::Contact> getContactList(const KMime:
45 return list; 47 return list;
46} 48}
47 49
50static QList<QPair<QString, QString>> processPart(KMime::Content* content)
51{
52 if (KMime::Headers::ContentType* type = content->contentType(false)) {
53 if (type->isMultipart() && !type->isSubtype("encrypted")) {
54 QList<QPair<QString, QString>> list;
55 for (const auto c : content->contents()) {
56 list << processPart(c);
57 }
58 return list;
59 } else if (type->isHTMLText()) {
60 // Only get HTML content, if no plain text content
61 QTextDocument doc;
62 doc.setHtml(content->decodedText());
63 return {{{}, {doc.toPlainText()}}};
64 } else if (type->isEmpty()) {
65 return {{{}, {content->decodedText()}}};
66 }
67 }
68 return {};
69}
70
48void MailPropertyExtractor::updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, const QByteArray &data) 71void MailPropertyExtractor::updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, const QByteArray &data)
49{ 72{
50 if (data.isEmpty()) { 73 if (data.isEmpty()) {
51 return; 74 return;
52 } 75 }
53 auto msg = KMime::Message::Ptr(new KMime::Message); 76 auto msg = KMime::Message::Ptr(new KMime::Message);
54 msg->setHead(KMime::CRLFtoLF(data)); 77 msg->setContent(KMime::CRLFtoLF(data));
55 msg->parse(); 78 msg->parse();
56 if (!msg) { 79 if (!msg) {
57 return; 80 return;
@@ -103,6 +126,20 @@ void MailPropertyExtractor::updatedIndexedProperties(Sink::ApplicationDomain::Ma
103 if (!parentMessageId.isEmpty()) { 126 if (!parentMessageId.isEmpty()) {
104 mail.setExtractedParentMessageId(parentMessageId); 127 mail.setExtractedParentMessageId(parentMessageId);
105 } 128 }
129 QList<QPair<QString, QString>> contentToIndex;
130 contentToIndex.append({{}, msg->subject()->asUnicodeString()});
131 if (KMime::Content* mainBody = msg->mainBodyPart("text/plain")) {
132 contentToIndex.append({{}, mainBody->decodedText()});
133 } else {
134 contentToIndex << processPart(msg.data());
135 }
136 contentToIndex.append({{}, msg->from(true)->asUnicodeString()});
137 contentToIndex.append({{}, msg->to(true)->asUnicodeString()});
138 contentToIndex.append({{}, msg->cc(true)->asUnicodeString()});
139 contentToIndex.append({{}, msg->bcc(true)->asUnicodeString()});
140
141 //Prepare content for indexing;
142 mail.setProperty("index", QVariant::fromValue(contentToIndex));
106} 143}
107 144
108void MailPropertyExtractor::newEntity(Sink::ApplicationDomain::Mail &mail) 145void MailPropertyExtractor::newEntity(Sink::ApplicationDomain::Mail &mail)
@@ -114,4 +151,3 @@ void MailPropertyExtractor::modifiedEntity(const Sink::ApplicationDomain::Mail &
114{ 151{
115 updatedIndexedProperties(newMail, newMail.getMimeMessage()); 152 updatedIndexedProperties(newMail, newMail.getMimeMessage());
116} 153}
117
diff --git a/common/mailpreprocessor.h b/common/mailpreprocessor.h
index d2e79ca..c0eacaf 100644
--- a/common/mailpreprocessor.h
+++ b/common/mailpreprocessor.h
@@ -29,4 +29,3 @@ public:
29protected: 29protected:
30 static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, const QByteArray &data); 30 static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, const QByteArray &data);
31}; 31};
32
diff --git a/common/query.cpp b/common/query.cpp
index 3dc8f99..5f6d095 100644
--- a/common/query.cpp
+++ b/common/query.cpp
@@ -34,6 +34,8 @@ QDebug operator<<(QDebug dbg, const Sink::QueryBase::Comparator &c)
34 dbg.nospace() << "contains " << c.value; 34 dbg.nospace() << "contains " << c.value;
35 } else if (c.comparator == Sink::Query::Comparator::In) { 35 } else if (c.comparator == Sink::Query::Comparator::In) {
36 dbg.nospace() << "in " << c.value; 36 dbg.nospace() << "in " << c.value;
37 } else if (c.comparator == Sink::Query::Comparator::Fulltext) {
38 dbg.nospace() << "fulltext contains " << c.value;
37 } else { 39 } else {
38 dbg.nospace() << "unknown comparator: " << c.value; 40 dbg.nospace() << "unknown comparator: " << c.value;
39 } 41 }
@@ -169,6 +171,7 @@ bool QueryBase::Comparator::matches(const QVariant &v) const
169 return false; 171 return false;
170 } 172 }
171 return value.value<QByteArrayList>().contains(v.toByteArray()); 173 return value.value<QByteArrayList>().contains(v.toByteArray());
174 case Fulltext:
172 case Invalid: 175 case Invalid:
173 default: 176 default:
174 break; 177 break;
diff --git a/common/query.h b/common/query.h
index 5b37cdd..1e7b41d 100644
--- a/common/query.h
+++ b/common/query.h
@@ -35,7 +35,8 @@ public:
35 Invalid, 35 Invalid,
36 Equals, 36 Equals,
37 Contains, 37 Contains,
38 In 38 In,
39 Fulltext
39 }; 40 };
40 41
41 Comparator(); 42 Comparator();
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp
index 4ad3eaf..8fbc2ad 100644
--- a/common/storage/entitystore.cpp
+++ b/common/storage/entitystore.cpp
@@ -162,23 +162,27 @@ void EntityStore::startTransaction(Sink::Storage::DataStore::AccessMode accessMo
162{ 162{
163 SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode; 163 SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode;
164 Q_ASSERT(!d->transaction); 164 Q_ASSERT(!d->transaction);
165 Sink::Storage::DataStore store(Sink::storageLocation(), dbLayout(d->resourceContext.instanceId()), accessMode); 165 d->transaction = Sink::Storage::DataStore(Sink::storageLocation(), dbLayout(d->resourceContext.instanceId()), accessMode).createTransaction(accessMode);
166 d->transaction = store.createTransaction(accessMode);
167} 166}
168 167
169void EntityStore::commitTransaction() 168void EntityStore::commitTransaction()
170{ 169{
171 SinkTraceCtx(d->logCtx) << "Committing transaction"; 170 SinkTraceCtx(d->logCtx) << "Committing transaction";
171
172 for (const auto &type : d->indexByType.keys()) {
173 d->typeIndex(type).commitTransaction();
174 }
175
172 Q_ASSERT(d->transaction); 176 Q_ASSERT(d->transaction);
173 d->transaction.commit(); 177 d->transaction.commit();
174 d->transaction = Storage::DataStore::Transaction(); 178 d->transaction = {};
175} 179}
176 180
177void EntityStore::abortTransaction() 181void EntityStore::abortTransaction()
178{ 182{
179 SinkTraceCtx(d->logCtx) << "Aborting transaction"; 183 SinkTraceCtx(d->logCtx) << "Aborting transaction";
180 d->transaction.abort(); 184 d->transaction.abort();
181 d->transaction = Storage::DataStore::Transaction(); 185 d->transaction = {};
182} 186}
183 187
184bool EntityStore::hasTransaction() const 188bool EntityStore::hasTransaction() const
@@ -195,7 +199,7 @@ bool EntityStore::add(const QByteArray &type, ApplicationDomain::ApplicationDoma
195 199
196 SinkTraceCtx(d->logCtx) << "New entity " << entity; 200 SinkTraceCtx(d->logCtx) << "New entity " << entity;
197 201
198 d->typeIndex(type).add(entity.identifier(), entity, d->transaction); 202 d->typeIndex(type).add(entity.identifier(), entity, d->transaction, d->resourceContext.instanceId());
199 203
200 //The maxRevision may have changed meanwhile if the entity created sub-entities 204 //The maxRevision may have changed meanwhile if the entity created sub-entities
201 const qint64 newRevision = maxRevision() + 1; 205 const qint64 newRevision = maxRevision() + 1;
@@ -262,8 +266,8 @@ bool EntityStore::modify(const QByteArray &type, const ApplicationDomain::Applic
262{ 266{
263 SinkTraceCtx(d->logCtx) << "Modified entity: " << newEntity; 267 SinkTraceCtx(d->logCtx) << "Modified entity: " << newEntity;
264 268
265 d->typeIndex(type).remove(current.identifier(), current, d->transaction); 269 d->typeIndex(type).remove(current.identifier(), current, d->transaction, d->resourceContext.instanceId());
266 d->typeIndex(type).add(newEntity.identifier(), newEntity, d->transaction); 270 d->typeIndex(type).add(newEntity.identifier(), newEntity, d->transaction, d->resourceContext.instanceId());
267 271
268 const qint64 newRevision = DataStore::maxRevision(d->transaction) + 1; 272 const qint64 newRevision = DataStore::maxRevision(d->transaction) + 1;
269 273
@@ -304,7 +308,7 @@ bool EntityStore::remove(const QByteArray &type, const Sink::ApplicationDomain::
304 return false; 308 return false;
305 } 309 }
306 310
307 d->typeIndex(type).remove(current.identifier(), current, d->transaction); 311 d->typeIndex(type).remove(current.identifier(), current, d->transaction, d->resourceContext.instanceId());
308 312
309 SinkTraceCtx(d->logCtx) << "Removed entity " << current; 313 SinkTraceCtx(d->logCtx) << "Removed entity " << current;
310 314
@@ -422,7 +426,7 @@ QVector<QByteArray> EntityStore::indexLookup(const QByteArray &type, const Query
422 SinkTraceCtx(d->logCtx) << "Database is not existing: " << type; 426 SinkTraceCtx(d->logCtx) << "Database is not existing: " << type;
423 return QVector<QByteArray>(); 427 return QVector<QByteArray>();
424 } 428 }
425 return d->typeIndex(type).query(query, appliedFilters, appliedSorting, d->getTransaction()); 429 return d->typeIndex(type).query(query, appliedFilters, appliedSorting, d->getTransaction(), d->resourceContext.instanceId());
426} 430}
427 431
428QVector<QByteArray> EntityStore::indexLookup(const QByteArray &type, const QByteArray &property, const QVariant &value) 432QVector<QByteArray> EntityStore::indexLookup(const QByteArray &type, const QByteArray &property, const QVariant &value)
diff --git a/common/typeindex.cpp b/common/typeindex.cpp
index 0228ecb..f2c67a1 100644
--- a/common/typeindex.cpp
+++ b/common/typeindex.cpp
@@ -20,6 +20,7 @@
20 20
21#include "log.h" 21#include "log.h"
22#include "index.h" 22#include "index.h"
23#include "fulltextindex.h"
23#include <QDateTime> 24#include <QDateTime>
24#include <QDataStream> 25#include <QDataStream>
25 26
@@ -158,7 +159,7 @@ void TypeIndex::addPropertyWithSorting<ApplicationDomain::Reference, QDateTime>(
158 addPropertyWithSorting<QByteArray, QDateTime>(property, sortProperty); 159 addPropertyWithSorting<QByteArray, QDateTime>(property, sortProperty);
159} 160}
160 161
161void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) 162void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId)
162{ 163{
163 for (const auto &property : mProperties) { 164 for (const auto &property : mProperties) {
164 const auto value = entity.getProperty(property); 165 const auto value = entity.getProperty(property);
@@ -172,7 +173,7 @@ void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::
172 indexer(add, identifier, value, sortValue, transaction); 173 indexer(add, identifier, value, sortValue, transaction);
173 } 174 }
174 for (const auto &indexer : mCustomIndexer) { 175 for (const auto &indexer : mCustomIndexer) {
175 indexer->setup(this, &transaction); 176 indexer->setup(this, &transaction, resourceInstanceId);
176 if (add) { 177 if (add) {
177 indexer->add(entity); 178 indexer->add(entity);
178 } else { 179 } else {
@@ -182,14 +183,28 @@ void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::
182 183
183} 184}
184 185
185void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) 186void TypeIndex::commitTransaction()
186{ 187{
187 updateIndex(true, identifier, entity, transaction); 188 for (const auto &indexer : mCustomIndexer) {
189 indexer->commitTransaction();
190 }
188} 191}
189 192
190void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) 193void TypeIndex::abortTransaction()
191{ 194{
192 updateIndex(false, identifier, entity, transaction); 195 for (const auto &indexer : mCustomIndexer) {
196 indexer->abortTransaction();
197 }
198}
199
200void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId)
201{
202 updateIndex(true, identifier, entity, transaction, resourceInstanceId);
203}
204
205void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId)
206{
207 updateIndex(false, identifier, entity, transaction, resourceInstanceId);
193} 208}
194 209
195static QVector<QByteArray> indexLookup(Index &index, QueryBase::Comparator filter) 210static QVector<QByteArray> indexLookup(Index &index, QueryBase::Comparator filter)
@@ -211,13 +226,22 @@ static QVector<QByteArray> indexLookup(Index &index, QueryBase::Comparator filte
211 return keys; 226 return keys;
212} 227}
213 228
214QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction) 229QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId)
215{ 230{
216 QVector<QByteArray> keys; 231 const auto baseFilters = query.getBaseFilters();
232 for (auto it = baseFilters.constBegin(); it != baseFilters.constEnd(); it++) {
233 if (it.value().comparator == QueryBase::Comparator::Fulltext) {
234 FulltextIndex fulltextIndex{resourceInstanceId};
235 const auto keys = fulltextIndex.lookup(it.value().value.toString());
236 appliedFilters << it.key();
237 return keys;
238 }
239 }
240
217 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { 241 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) {
218 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) { 242 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) {
219 Index index(indexName(it.key(), it.value()), transaction); 243 Index index(indexName(it.key(), it.value()), transaction);
220 keys << indexLookup(index, query.getFilter(it.key())); 244 const auto keys = indexLookup(index, query.getFilter(it.key()));
221 appliedFilters << it.key(); 245 appliedFilters << it.key();
222 appliedSorting = it.value(); 246 appliedSorting = it.value();
223 SinkTraceCtx(mLogCtx) << "Index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; 247 SinkTraceCtx(mLogCtx) << "Index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys.";
@@ -227,14 +251,14 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr
227 for (const auto &property : mProperties) { 251 for (const auto &property : mProperties) {
228 if (query.hasFilter(property)) { 252 if (query.hasFilter(property)) {
229 Index index(indexName(property), transaction); 253 Index index(indexName(property), transaction);
230 keys << indexLookup(index, query.getFilter(property)); 254 const auto keys = indexLookup(index, query.getFilter(property));
231 appliedFilters << property; 255 appliedFilters << property;
232 SinkTraceCtx(mLogCtx) << "Index lookup on " << property << " found " << keys.size() << " keys."; 256 SinkTraceCtx(mLogCtx) << "Index lookup on " << property << " found " << keys.size() << " keys.";
233 return keys; 257 return keys;
234 } 258 }
235 } 259 }
236 SinkTraceCtx(mLogCtx) << "No matching index"; 260 SinkTraceCtx(mLogCtx) << "No matching index";
237 return keys; 261 return {};
238} 262}
239 263
240QVector<QByteArray> TypeIndex::lookup(const QByteArray &property, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction) 264QVector<QByteArray> TypeIndex::lookup(const QByteArray &property, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)
diff --git a/common/typeindex.h b/common/typeindex.h
index 890c3db..b8b4d52 100644
--- a/common/typeindex.h
+++ b/common/typeindex.h
@@ -71,10 +71,10 @@ public:
71 mCustomIndexer << CustomIndexer::Ptr::create(); 71 mCustomIndexer << CustomIndexer::Ptr::create();
72 } 72 }
73 73
74 void add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); 74 void add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
75 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); 75 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
76 76
77 QVector<QByteArray> query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction); 77 QVector<QByteArray> query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
78 QVector<QByteArray> lookup(const QByteArray &property, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction); 78 QVector<QByteArray> lookup(const QByteArray &property, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction);
79 79
80 template <typename Left, typename Right> 80 template <typename Left, typename Right>
@@ -104,10 +104,13 @@ public:
104 template <typename LeftType, typename RightType> 104 template <typename LeftType, typename RightType>
105 void unindex(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction); 105 void unindex(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction);
106 106
107 void commitTransaction();
108 void abortTransaction();
109
107 110
108private: 111private:
109 friend class Sink::Storage::EntityStore; 112 friend class Sink::Storage::EntityStore;
110 void updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); 113 void updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
111 QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; 114 QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const;
112 Sink::Log::Context mLogCtx; 115 Sink::Log::Context mLogCtx;
113 QByteArray mType; 116 QByteArray mType;
diff --git a/synchronizer/CMakeLists.txt b/synchronizer/CMakeLists.txt
index 2f8b128..9c422e6 100644
--- a/synchronizer/CMakeLists.txt
+++ b/synchronizer/CMakeLists.txt
@@ -10,6 +10,7 @@ add_executable(${PROJECT_NAME} ${sinksynchronizer_SRCS})
10target_link_libraries(${PROJECT_NAME} 10target_link_libraries(${PROJECT_NAME}
11 sink 11 sink
12 Qt5::Core 12 Qt5::Core
13 Qt5::Gui
13 Qt5::Network 14 Qt5::Network
14 KAsync 15 KAsync
15 ${CMAKE_DL_LIBS} 16 ${CMAKE_DL_LIBS}
diff --git a/synchronizer/main.cpp b/synchronizer/main.cpp
index 3f79207..f4bac73 100644
--- a/synchronizer/main.cpp
+++ b/synchronizer/main.cpp
@@ -17,7 +17,7 @@
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */ 18 */
19 19
20#include <QCoreApplication> 20#include <QGuiApplication>
21#include <QLockFile> 21#include <QLockFile>
22#include <QDir> 22#include <QDir>
23 23
@@ -226,7 +226,7 @@ int main(int argc, char *argv[])
226 226
227 qInstallMessageHandler(qtMessageHandler); 227 qInstallMessageHandler(qtMessageHandler);
228 228
229 QCoreApplication app(argc, argv); 229 QGuiApplication app(argc, argv);
230 app.setQuitLockEnabled(false); 230 app.setQuitLockEnabled(false);
231 231
232 QByteArrayList arguments; 232 QByteArrayList arguments;
diff --git a/tests/dummyresourcewritebenchmark.cpp b/tests/dummyresourcewritebenchmark.cpp
index 07f57f6..e0ec503 100644
--- a/tests/dummyresourcewritebenchmark.cpp
+++ b/tests/dummyresourcewritebenchmark.cpp
@@ -20,6 +20,7 @@
20#include "hawd/formatter.h" 20#include "hawd/formatter.h"
21 21
22#include "event_generated.h" 22#include "event_generated.h"
23#include "mail_generated.h"
23#include "entity_generated.h" 24#include "entity_generated.h"
24#include "metadata_generated.h" 25#include "metadata_generated.h"
25#include "createentity_generated.h" 26#include "createentity_generated.h"
@@ -27,38 +28,36 @@
27#include "getrssusage.h" 28#include "getrssusage.h"
28#include "utils.h" 29#include "utils.h"
29 30
31#include <KMime/Message>
32
30static QByteArray createEntityBuffer(size_t attachmentSize, int &bufferSize) 33static QByteArray createEntityBuffer(size_t attachmentSize, int &bufferSize)
31{ 34{
32 uint8_t rawData[attachmentSize];
33 flatbuffers::FlatBufferBuilder eventFbb; 35 flatbuffers::FlatBufferBuilder eventFbb;
34 eventFbb.Clear(); 36 eventFbb.Clear();
35 { 37 {
36 uint8_t *rawDataPtr = Q_NULLPTR;
37 auto data = eventFbb.CreateUninitializedVector<uint8_t>(attachmentSize, &rawDataPtr);
38 auto summary = eventFbb.CreateString("summary");
39 Sink::ApplicationDomain::Buffer::EventBuilder eventBuilder(eventFbb);
40 eventBuilder.add_summary(summary);
41 eventBuilder.add_attachment(data);
42 auto eventLocation = eventBuilder.Finish();
43 Sink::ApplicationDomain::Buffer::FinishEventBuffer(eventFbb, eventLocation);
44 memcpy((void *)rawDataPtr, rawData, attachmentSize);
45 }
46 38
47 flatbuffers::FlatBufferBuilder localFbb; 39 auto msg = KMime::Message::Ptr::create();
48 { 40 msg->subject()->from7BitString("Some subject");
49 auto uid = localFbb.CreateString("testuid"); 41 msg->setBody("This is the body now.");
50 auto localBuilder = Sink::ApplicationDomain::Buffer::EventBuilder(localFbb); 42 msg->assemble();
51 localBuilder.add_uid(uid); 43
52 auto location = localBuilder.Finish(); 44 const auto data = msg->encodedContent();
53 Sink::ApplicationDomain::Buffer::FinishEventBuffer(localFbb, location); 45
46 auto summary = eventFbb.CreateString("summary");
47 auto mimeMessage = eventFbb.CreateString(data.constData(), data.length());
48 Sink::ApplicationDomain::Buffer::MailBuilder eventBuilder(eventFbb);
49 eventBuilder.add_subject(summary);
50 eventBuilder.add_messageId(summary);
51 eventBuilder.add_mimeMessage(mimeMessage);
52 Sink::ApplicationDomain::Buffer::FinishMailBuffer(eventFbb, eventBuilder.Finish());
54 } 53 }
55 54
56 flatbuffers::FlatBufferBuilder entityFbb; 55 flatbuffers::FlatBufferBuilder entityFbb;
57 Sink::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize()); 56 Sink::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize());
58 bufferSize = entityFbb.GetSize(); 57 bufferSize = entityFbb.GetSize();
59 58
60 flatbuffers::FlatBufferBuilder fbb; 59 flatbuffers::FlatBufferBuilder fbb;
61 auto type = fbb.CreateString(Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Event>().toStdString().data()); 60 auto type = fbb.CreateString(Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Mail>().toStdString().data());
62 auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize()); 61 auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize());
63 Sink::Commands::CreateEntityBuilder builder(fbb); 62 Sink::Commands::CreateEntityBuilder builder(fbb);
64 builder.add_domainType(type); 63 builder.add_domainType(type);
@@ -263,10 +262,7 @@ private slots:
263 262
264 void runBenchmarks() 263 void runBenchmarks()
265 { 264 {
266 writeInProcess(1000, mTimeStamp);
267 writeInProcess(2000, mTimeStamp);
268 writeInProcess(5000, mTimeStamp); 265 writeInProcess(5000, mTimeStamp);
269 writeInProcess(20000, mTimeStamp);
270 } 266 }
271 267
272 void ensureUsedMemoryRemainsStable() 268 void ensureUsedMemoryRemainsStable()
diff --git a/tests/querytest.cpp b/tests/querytest.cpp
index 1584c48..ec6438d 100644
--- a/tests/querytest.cpp
+++ b/tests/querytest.cpp
@@ -14,6 +14,8 @@
14#include "testutils.h" 14#include "testutils.h"
15#include "applicationdomaintype.h" 15#include "applicationdomaintype.h"
16 16
17#include <KMime/Message>
18
17using namespace Sink; 19using namespace Sink;
18using namespace Sink::ApplicationDomain; 20using namespace Sink::ApplicationDomain;
19 21
@@ -1254,6 +1256,94 @@ private slots:
1254 VERIFYEXEC(Sink::Store::removeDataFromDisk(QByteArray("sink.dummy.instance1"))); 1256 VERIFYEXEC(Sink::Store::removeDataFromDisk(QByteArray("sink.dummy.instance1")));
1255 } 1257 }
1256 1258
1259 void testMailFulltextSubject()
1260 {
1261 // Setup
1262 {
1263 auto msg = KMime::Message::Ptr::create();
1264 msg->subject()->from7BitString("Subject To Search");
1265 msg->setBody("This is the searchable body.");
1266 msg->assemble();
1267 {
1268 Mail mail("sink.dummy.instance1");
1269 mail.setExtractedMessageId("test1");
1270 mail.setExtractedSubject("Subject To Search");
1271 mail.setFolder("folder1");
1272 mail.setMimeMessage(msg->encodedContent());
1273 VERIFYEXEC(Sink::Store::create<Mail>(mail));
1274 }
1275 {
1276 Mail mail("sink.dummy.instance1");
1277 mail.setExtractedMessageId("test2");
1278 mail.setFolder("folder2");
1279 mail.setExtractedSubject("Stuff");
1280 VERIFYEXEC(Sink::Store::create<Mail>(mail));
1281 }
1282 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1"));
1283 }
1284
1285 // Test
1286 {
1287 Sink::Query query;
1288 query.resourceFilter("sink.dummy.instance1");
1289 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject To Search"), QueryBase::Comparator::Fulltext));
1290 auto model = Sink::Store::loadModel<Mail>(query);
1291 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1292 QCOMPARE(model->rowCount(), 1);
1293 }
1294 {
1295 Sink::Query query;
1296 query.resourceFilter("sink.dummy.instance1");
1297 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject"), QueryBase::Comparator::Fulltext));
1298 auto result = Sink::Store::read<Mail>(query);
1299 QCOMPARE(result.size(), 1);
1300 }
1301 {
1302 Sink::Query query;
1303 query.resourceFilter("sink.dummy.instance1");
1304 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Search"), QueryBase::Comparator::Fulltext));
1305 auto result = Sink::Store::read<Mail>(query);
1306 QCOMPARE(result.size(), 1);
1307 }
1308 {
1309 Sink::Query query;
1310 query.resourceFilter("sink.dummy.instance1");
1311 query.filter<Mail::Subject>(QueryBase::Comparator(QString("search"), QueryBase::Comparator::Fulltext));
1312 auto result = Sink::Store::read<Mail>(query);
1313 QCOMPARE(result.size(), 1);
1314 }
1315 {
1316 Sink::Query query;
1317 query.resourceFilter("sink.dummy.instance1");
1318 query.filter<Mail::Subject>(QueryBase::Comparator(QString("sear*"), QueryBase::Comparator::Fulltext));
1319 auto result = Sink::Store::read<Mail>(query);
1320 QCOMPARE(result.size(), 1);
1321 }
1322 {
1323 Sink::Query query;
1324 query.resourceFilter("sink.dummy.instance1");
1325 query.filter<Mail::MimeMessage>(QueryBase::Comparator(QString("searchable"), QueryBase::Comparator::Fulltext));
1326 auto result = Sink::Store::read<Mail>(query);
1327 QCOMPARE(result.size(), 1);
1328 }
1329 {
1330 Sink::Query query;
1331 query.resourceFilter("sink.dummy.instance1");
1332 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject"), QueryBase::Comparator::Fulltext));
1333 query.filter<Mail::Folder>("folder1");
1334 auto result = Sink::Store::read<Mail>(query);
1335 QCOMPARE(result.size(), 1);
1336 }
1337 {
1338 Sink::Query query;
1339 query.resourceFilter("sink.dummy.instance1");
1340 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject"), QueryBase::Comparator::Fulltext));
1341 query.filter<Mail::Folder>("folder2");
1342 auto result = Sink::Store::read<Mail>(query);
1343 QCOMPARE(result.size(), 0);
1344 }
1345 }
1346
1257}; 1347};
1258 1348
1259QTEST_MAIN(QueryTest) 1349QTEST_MAIN(QueryTest)