summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/datastorequery.cpp30
-rw-r--r--common/domain/applicationdomaintype.cpp2
-rw-r--r--common/query.h157
-rw-r--r--examples/maildirresource/tests/maildirmailsynctest.cpp3
-rw-r--r--examples/maildirresource/tests/maildirthreadtest.cpp2
-rw-r--r--tests/mailthreadtest.cpp26
6 files changed, 167 insertions, 53 deletions
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp
index f352b74..0fc9234 100644
--- a/common/datastorequery.cpp
+++ b/common/datastorequery.cpp
@@ -148,14 +148,9 @@ public:
148 QHash<QByteArray, QVariant> mAggregateValues; 148 QHash<QByteArray, QVariant> mAggregateValues;
149 QByteArray mReductionProperty; 149 QByteArray mReductionProperty;
150 QByteArray mSelectionProperty; 150 QByteArray mSelectionProperty;
151 enum SelectionComparator { 151 Query::Reduce::Selector::Comparator mSelectionComparator;
152 Max
153 /* Min, */
154 /* First */
155 };
156 SelectionComparator mSelectionComparator;
157 152
158 Reduce(const QByteArray &reductionProperty, const QByteArray &selectionProperty, SelectionComparator comparator, FilterBase::Ptr source, DataStoreQuery *store) 153 Reduce(const QByteArray &reductionProperty, const QByteArray &selectionProperty, Query::Reduce::Selector::Comparator comparator, FilterBase::Ptr source, DataStoreQuery *store)
159 : FilterBase(source, store), 154 : FilterBase(source, store),
160 mReductionProperty(reductionProperty), 155 mReductionProperty(reductionProperty),
161 mSelectionProperty(selectionProperty), 156 mSelectionProperty(selectionProperty),
@@ -177,9 +172,9 @@ public:
177 return QByteArray(); 172 return QByteArray();
178 } 173 }
179 174
180 static bool compare(const QVariant &left, const QVariant &right, SelectionComparator comparator) 175 static bool compare(const QVariant &left, const QVariant &right, Query::Reduce::Selector::Comparator comparator)
181 { 176 {
182 if (comparator == Max) { 177 if (comparator == Query::Reduce::Selector::Max) {
183 return left > right; 178 return left > right;
184 } 179 }
185 return false; 180 return false;
@@ -412,13 +407,16 @@ void DataStoreQuery::setupQuery()
412 /* baseSet = Sort::Ptr::create(baseSet, mQuery.sortProperty); */ 407 /* baseSet = Sort::Ptr::create(baseSet, mQuery.sortProperty); */
413 /* } */ 408 /* } */
414 409
415 if (mQuery.threadLeaderOnly) { 410 for (const auto &stage : mQuery.filterStages) {
416 auto reduce = Reduce::Ptr::create("threadId", "date", Reduce::Max, baseSet, this); 411 if (auto filter = stage.dynamicCast<Query::Filter>()) {
417 baseSet = reduce; 412
418 } 413 } else if (auto filter = stage.dynamicCast<Query::Reduce>()) {
419 if (mQuery.bloomThread) { 414 auto reduce = Reduce::Ptr::create(filter->property, filter->selector.property, filter->selector.comparator, baseSet, this);
420 auto reduce = Bloom::Ptr::create("threadId", baseSet, this); 415 baseSet = reduce;
421 baseSet = reduce; 416 } else if (auto filter = stage.dynamicCast<Query::Bloom>()) {
417 auto reduce = Bloom::Ptr::create(filter->property, baseSet, this);
418 baseSet = reduce;
419 }
422 } 420 }
423 421
424 mCollector = Collector::Ptr::create(baseSet, this); 422 mCollector = Collector::Ptr::create(baseSet, this);
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp
index e6117aa..2f7c32b 100644
--- a/common/domain/applicationdomaintype.cpp
+++ b/common/domain/applicationdomaintype.cpp
@@ -29,6 +29,8 @@ SINK_DEBUG_AREA("applicationdomaintype");
29namespace Sink { 29namespace Sink {
30namespace ApplicationDomain { 30namespace ApplicationDomain {
31 31
32constexpr const char *Mail::ThreadId::name;
33
32ApplicationDomainType::ApplicationDomainType() 34ApplicationDomainType::ApplicationDomainType()
33 :mAdaptor(new MemoryBufferAdaptor()) 35 :mAdaptor(new MemoryBufferAdaptor())
34{ 36{
diff --git a/common/query.h b/common/query.h
index 1b909e5..3021fe2 100644
--- a/common/query.h
+++ b/common/query.h
@@ -174,34 +174,13 @@ public:
174 return *this; 174 return *this;
175 } 175 }
176 176
177 template <typename T> 177 Query(const ApplicationDomain::Entity &value) : limit(0), liveQuery(false), synchronousQuery(false)
178 Query &filter(const QVariant &value)
179 {
180 propertyFilter.insert(T::name, value);
181 return *this;
182 }
183
184 template <typename T>
185 Query &filter(const Comparator &comparator)
186 {
187 propertyFilter.insert(T::name, comparator);
188 return *this;
189 }
190
191 template <typename T>
192 Query &filter(const ApplicationDomain::Entity &value)
193 {
194 propertyFilter.insert(T::name, QVariant::fromValue(value.identifier()));
195 return *this;
196 }
197
198 Query(const ApplicationDomain::Entity &value) : limit(0), liveQuery(false), synchronousQuery(false), threadLeaderOnly(false), bloomThread(false)
199 { 178 {
200 ids << value.identifier(); 179 ids << value.identifier();
201 resources << value.resourceInstanceIdentifier(); 180 resources << value.resourceInstanceIdentifier();
202 } 181 }
203 182
204 Query(Flags flags = Flags()) : limit(0), liveQuery(false), synchronousQuery(false), threadLeaderOnly(false), bloomThread(false) 183 Query(Flags flags = Flags()) : limit(0), liveQuery(false), synchronousQuery(false)
205 { 184 {
206 } 185 }
207 186
@@ -236,9 +215,137 @@ public:
236 int limit; 215 int limit;
237 bool liveQuery; 216 bool liveQuery;
238 bool synchronousQuery; 217 bool synchronousQuery;
239 bool threadLeaderOnly; 218
240 bool bloomThread; 219 class FilterStage {
220 public:
221 virtual ~FilterStage(){};
222 };
223
224 QList<QSharedPointer<FilterStage>> filterStages;
225
226 /*
227 * Filters
228 */
229 class Filter : public FilterStage {
230 QByteArrayList ids;
231 QHash<QByteArray, Comparator> propertyFilter;
232 QByteArray sortProperty;
233 };
234
235 template <typename T>
236 Query &filter(const QVariant &value)
237 {
238 propertyFilter.insert(T::name, value);
239 return *this;
240 }
241
242 template <typename T>
243 Query &filter(const Comparator &comparator)
244 {
245 propertyFilter.insert(T::name, comparator);
246 return *this;
247 }
248
249 template <typename T>
250 Query &filter(const ApplicationDomain::Entity &value)
251 {
252 propertyFilter.insert(T::name, QVariant::fromValue(value.identifier()));
253 return *this;
254 }
255
256 Query &filter(const ApplicationDomain::SinkResource &resource)
257 {
258 resources << resource.identifier();
259 return *this;
260 }
261
262 Query &filter(const ApplicationDomain::SinkAccount &account)
263 {
264 accounts << account.identifier();
265 return *this;
266 }
267
268 class Reduce : public FilterStage {
269 public:
270
271 class Selector {
272 public:
273 enum Comparator {
274 Min, //get the minimum value
275 Max, //get the maximum value
276 First //Get the first result we get
277 };
278
279 template <typename SelectionProperty>
280 static Selector max()
281 {
282 return Selector(SelectionProperty::name, Max);
283 }
284
285 Selector(const QByteArray &p, Comparator c)
286 : property(p),
287 comparator(c)
288 {
289 }
290
291 QByteArray property;
292 Comparator comparator;
293 };
294
295 Reduce(const QByteArray &p, const Selector &s)
296 : property(p),
297 selector(s)
298 {
299 }
300
301 //Reduce on property
302 QByteArray property;
303 Selector selector;
304
305 //TODO add aggregate functions like:
306 //.count()
307 //.collect<Mail::sender>();
308 //...
309 //
310 //Potentially pass-in an identifier under which the result will be available in the result set.
311 };
312
313 template <typename T>
314 Reduce &reduce(const Reduce::Selector &s)
315 {
316 auto reduction = QSharedPointer<Reduce>::create(T::name, s);
317 filterStages << reduction;
318 return *reduction;
319 }
320
321 /**
322 * "Bloom" on a property.
323 *
324 * For every encountered value of a property,
325 * a result set is generated containing all entries with the same value.
326 *
327 * Example:
328 * For an input result set of one mail; return all emails with the same threadId.
329 */
330 class Bloom : public FilterStage {
331 public:
332 //Property to bloom on
333 QByteArray property;
334 Bloom(const QByteArray &p)
335 : property(p)
336 {
337 }
338 };
339
340 template <typename T>
341 void bloom()
342 {
343 auto bloom = QSharedPointer<Bloom>::create(T::name);
344 filterStages << bloom;
345 }
346
241}; 347};
348
242} 349}
243 350
244QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c); 351QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c);
diff --git a/examples/maildirresource/tests/maildirmailsynctest.cpp b/examples/maildirresource/tests/maildirmailsynctest.cpp
index bafd5a8..2269362 100644
--- a/examples/maildirresource/tests/maildirmailsynctest.cpp
+++ b/examples/maildirresource/tests/maildirmailsynctest.cpp
@@ -22,8 +22,7 @@
22#include "../maildirresource.h" 22#include "../maildirresource.h"
23#include "../libmaildir/maildir.h" 23#include "../libmaildir/maildir.h"
24 24
25#include "common/test.h" 25#include "test.h"
26#include "common/domain/applicationdomaintype.h"
27 26
28#include "utils.h" 27#include "utils.h"
29 28
diff --git a/examples/maildirresource/tests/maildirthreadtest.cpp b/examples/maildirresource/tests/maildirthreadtest.cpp
index 69d51a6..5a23ccf 100644
--- a/examples/maildirresource/tests/maildirthreadtest.cpp
+++ b/examples/maildirresource/tests/maildirthreadtest.cpp
@@ -23,7 +23,7 @@
23#include "../libmaildir/maildir.h" 23#include "../libmaildir/maildir.h"
24 24
25#include "common/test.h" 25#include "common/test.h"
26#include "common/domain/applicationdomaintype.h" 26#include "applicationdomaintype.h"
27 27
28#include "utils.h" 28#include "utils.h"
29 29
diff --git a/tests/mailthreadtest.cpp b/tests/mailthreadtest.cpp
index 89e5a85..e9fe499 100644
--- a/tests/mailthreadtest.cpp
+++ b/tests/mailthreadtest.cpp
@@ -66,10 +66,10 @@ void MailThreadTest::init()
66void MailThreadTest::testListThreadLeader() 66void MailThreadTest::testListThreadLeader()
67{ 67{
68 Sink::Query query; 68 Sink::Query query;
69 query.resources << mResourceInstanceIdentifier; 69 query.filter(SinkResource(mResourceInstanceIdentifier));
70 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>(); 70 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>();
71 query.threadLeaderOnly = true;
72 query.sort<Mail::Date>(); 71 query.sort<Mail::Date>();
72 query.reduce<Mail::ThreadId>(Query::Reduce::Selector::max<Mail::Date>());
73 73
74 // Ensure all local data is processed 74 // Ensure all local data is processed
75 VERIFYEXEC(Store::synchronize(query)); 75 VERIFYEXEC(Store::synchronize(query));
@@ -127,11 +127,11 @@ void MailThreadTest::testIndexInMixedOrder()
127 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); 127 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
128 128
129 Sink::Query query; 129 Sink::Query query;
130 query.resources << mResourceInstanceIdentifier; 130 query.filter(SinkResource(mResourceInstanceIdentifier));
131 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>(); 131 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>();
132 query.threadLeaderOnly = true;
133 query.sort<Mail::Date>();
134 query.filter<Mail::Folder>(folder); 132 query.filter<Mail::Folder>(folder);
133 query.sort<Mail::Date>();
134 query.reduce<Mail::ThreadId>(Query::Reduce::Selector::max<Mail::Date>());
135 135
136 Mail threadLeader; 136 Mail threadLeader;
137 137
@@ -147,6 +147,14 @@ void MailThreadTest::testIndexInMixedOrder()
147 VERIFYEXEC(job); 147 VERIFYEXEC(job);
148 } 148 }
149 149
150 {
151 auto mail = Mail::create(mResourceInstanceIdentifier);
152 mail.setMimeMessage(message2->encodedContent());
153 mail.setFolder(folder);
154 VERIFYEXEC(Store::create(mail));
155 }
156 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
157
150 //Ensure we find the thread leader still 158 //Ensure we find the thread leader still
151 { 159 {
152 auto job = Store::fetchAll<Mail>(query) 160 auto job = Store::fetchAll<Mail>(query)
@@ -169,15 +177,15 @@ void MailThreadTest::testIndexInMixedOrder()
169 //Ensure the thread is complete 177 //Ensure the thread is complete
170 { 178 {
171 Sink::Query query; 179 Sink::Query query;
172 query.resources << mResourceInstanceIdentifier; 180 query.filter(SinkResource(mResourceInstanceIdentifier));
181 query.ids << threadLeader.identifier();
173 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>(); 182 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>();
174 query.bloomThread = true;
175 query.sort<Mail::Date>(); 183 query.sort<Mail::Date>();
176 query.ids << threadLeader.identifier(); 184 query.bloom<Mail::ThreadId>();
177 185
178 auto job = Store::fetchAll<Mail>(query) 186 auto job = Store::fetchAll<Mail>(query)
179 .syncThen<void, QList<Mail::Ptr>>([=](const QList<Mail::Ptr> &mails) { 187 .syncThen<void, QList<Mail::Ptr>>([=](const QList<Mail::Ptr> &mails) {
180 QCOMPARE(mails.size(), 2); 188 QCOMPARE(mails.size(), 3);
181 auto mail = *mails.first(); 189 auto mail = *mails.first();
182 QCOMPARE(mail.getSubject(), QString::fromLatin1("Re: Re: 1")); 190 QCOMPARE(mail.getSubject(), QString::fromLatin1("Re: Re: 1"));
183 }); 191 });