summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/datastorequery.cpp65
-rw-r--r--common/datastorequery.h2
-rw-r--r--common/entityreader.cpp14
-rw-r--r--common/entityreader.h2
-rw-r--r--common/query.h33
-rw-r--r--common/queryrunner.cpp9
-rw-r--r--common/resultset.cpp2
-rw-r--r--common/resultset.h10
-rw-r--r--common/standardqueries.h2
-rw-r--r--tests/mailthreadtest.cpp4
10 files changed, 96 insertions, 47 deletions
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp
index 7341675..8b14951 100644
--- a/common/datastorequery.cpp
+++ b/common/datastorequery.cpp
@@ -62,13 +62,13 @@ class Source : public FilterBase {
62 mIt = mIds.constBegin(); 62 mIt = mIds.constBegin();
63 } 63 }
64 64
65 bool next(const std::function<void(Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) Q_DECL_OVERRIDE 65 bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE
66 { 66 {
67 if (mIt == mIds.constEnd()) { 67 if (mIt == mIds.constEnd()) {
68 return false; 68 return false;
69 } 69 }
70 readEntity(*mIt, [callback](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { 70 readEntity(*mIt, [callback](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) {
71 callback(entityBuffer.operation(), uid, entityBuffer); 71 callback({uid, entityBuffer, entityBuffer.operation()});
72 }); 72 });
73 mIt++; 73 mIt++;
74 return mIt != mIds.constEnd(); 74 return mIt != mIds.constEnd();
@@ -86,7 +86,7 @@ public:
86 } 86 }
87 virtual ~Collector(){} 87 virtual ~Collector(){}
88 88
89 bool next(const std::function<void(Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) Q_DECL_OVERRIDE 89 bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE
90 { 90 {
91 return mSource->next(callback); 91 return mSource->next(callback);
92 } 92 }
@@ -106,26 +106,26 @@ public:
106 106
107 virtual ~Filter(){} 107 virtual ~Filter(){}
108 108
109 bool next(const std::function<void(Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) Q_DECL_OVERRIDE { 109 bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE {
110 bool foundValue = false; 110 bool foundValue = false;
111 while(!foundValue && mSource->next([this, callback, &foundValue](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { 111 while(!foundValue && mSource->next([this, callback, &foundValue](const ResultSet::Result &result) {
112 SinkTrace() << "Filter: " << uid << operation; 112 SinkTrace() << "Filter: " << result.uid << result.operation;
113 113
114 //Always accept removals. They can't match the filter since the data is gone. 114 //Always accept removals. They can't match the filter since the data is gone.
115 if (operation == Sink::Operation_Removal) { 115 if (result.operation == Sink::Operation_Removal) {
116 SinkTrace() << "Removal: " << uid << operation; 116 SinkTrace() << "Removal: " << result.uid << result.operation;
117 callback(operation, uid, entityBuffer); 117 callback(result);
118 foundValue = true; 118 foundValue = true;
119 } else if (matchesFilter(uid, entityBuffer)) { 119 } else if (matchesFilter(result.uid, result.buffer)) {
120 SinkTrace() << "Accepted: " << uid << operation; 120 SinkTrace() << "Accepted: " << result.uid << result.operation;
121 callback(operation, uid, entityBuffer); 121 callback(result);
122 foundValue = true; 122 foundValue = true;
123 //TODO if something did not match the filter so far but does now, turn into an add operation. 123 //TODO if something did not match the filter so far but does now, turn into an add operation.
124 } else { 124 } else {
125 SinkTrace() << "Rejected: " << uid << operation; 125 SinkTrace() << "Rejected: " << result.uid << result.operation;
126 //TODO emit a removal if we had the uid in the result set and this is a modification. 126 //TODO emit a removal if we had the uid in the result set and this is a modification.
127 //We don't know if this results in a removal from the dataset, so we emit a removal notification anyways 127 //We don't know if this results in a removal from the dataset, so we emit a removal notification anyways
128 callback(Sink::Operation_Removal, uid, entityBuffer); 128 callback({result.uid, result.buffer, Sink::Operation_Removal, result.aggregateValues});
129 } 129 }
130 return false; 130 return false;
131 })) 131 }))
@@ -186,10 +186,10 @@ public:
186 return false; 186 return false;
187 } 187 }
188 188
189 bool next(const std::function<void(Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) Q_DECL_OVERRIDE { 189 bool next(const std::function<void(const ResultSet::Result &)> &callback) Q_DECL_OVERRIDE {
190 bool foundValue = false; 190 bool foundValue = false;
191 while(!foundValue && mSource->next([this, callback, &foundValue](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { 191 while(!foundValue && mSource->next([this, callback, &foundValue](const ResultSet::Result &result) {
192 auto reductionValue = getProperty(entityBuffer.entity(), mReductionProperty); 192 auto reductionValue = getProperty(result.buffer.entity(), mReductionProperty);
193 if (!mReducedValues.contains(getByteArray(reductionValue))) { 193 if (!mReducedValues.contains(getByteArray(reductionValue))) {
194 //Only reduce every value once. 194 //Only reduce every value once.
195 mReducedValues.insert(getByteArray(reductionValue)); 195 mReducedValues.insert(getByteArray(reductionValue));
@@ -205,8 +205,11 @@ public:
205 } 205 }
206 }); 206 });
207 } 207 }
208 int count = results.size();
208 readEntity(selectionResult, [&, this](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { 209 readEntity(selectionResult, [&, this](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) {
209 callback(Sink::Operation_Creation, uid, entityBuffer); 210 QMap<QByteArray, QVariant> aggregateValues;
211 aggregateValues.insert("count", count);
212 callback({uid, entityBuffer, Sink::Operation_Creation, aggregateValues});
210 foundValue = true; 213 foundValue = true;
211 }); 214 });
212 } 215 }
@@ -232,14 +235,14 @@ public:
232 235
233 virtual ~Bloom(){} 236 virtual ~Bloom(){}
234 237
235 bool next(const std::function<void(Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) Q_DECL_OVERRIDE { 238 bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE {
236 bool foundValue = false; 239 bool foundValue = false;
237 while(!foundValue && mSource->next([this, callback, &foundValue](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { 240 while(!foundValue && mSource->next([this, callback, &foundValue](const ResultSet::Result &result) {
238 auto bloomValue = getProperty(entityBuffer.entity(), mBloomProperty); 241 auto bloomValue = getProperty(result.buffer.entity(), mBloomProperty);
239 auto results = indexLookup(mBloomProperty, bloomValue); 242 auto results = indexLookup(mBloomProperty, bloomValue);
240 for (const auto r : results) { 243 for (const auto r : results) {
241 readEntity(r, [&, this](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { 244 readEntity(r, [&, this](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) {
242 callback(Sink::Operation_Creation, uid, entityBuffer); 245 callback({uid, entityBuffer, Sink::Operation_Creation});
243 foundValue = true; 246 foundValue = true;
244 }); 247 });
245 } 248 }
@@ -398,8 +401,8 @@ QByteArrayList DataStoreQuery::executeSubquery(const Query &subquery)
398 auto sub = prepareQuery(subquery.type, subquery, mTransaction); 401 auto sub = prepareQuery(subquery.type, subquery, mTransaction);
399 auto result = sub->execute(); 402 auto result = sub->execute();
400 QByteArrayList ids; 403 QByteArrayList ids;
401 while (result.next([&ids](const QByteArray &uid, const Sink::EntityBuffer &, Sink::Operation) { 404 while (result.next([&ids](const ResultSet::Result &result) {
402 ids << uid; 405 ids << result.uid;
403 })) 406 }))
404 {} 407 {}
405 return ids; 408 return ids;
@@ -502,9 +505,9 @@ ResultSet DataStoreQuery::update(qint64 baseRevision)
502 SinkTrace() << "Changed: " << incrementalResultSet; 505 SinkTrace() << "Changed: " << incrementalResultSet;
503 mSource->add(incrementalResultSet); 506 mSource->add(incrementalResultSet);
504 ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool { 507 ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool {
505 if (mCollector->next([this, callback](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &buffer) { 508 if (mCollector->next([this, callback](const ResultSet::Result &result) {
506 SinkTrace() << "Got incremental result: " << uid << operation; 509 SinkTrace() << "Got incremental result: " << result.uid << result.operation;
507 callback(uid, buffer, operation); 510 callback(result);
508 })) 511 }))
509 { 512 {
510 return true; 513 return true;
@@ -520,10 +523,10 @@ ResultSet DataStoreQuery::execute()
520 SinkTrace() << "Executing query"; 523 SinkTrace() << "Executing query";
521 524
522 ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool { 525 ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool {
523 if (mCollector->next([this, callback](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &buffer) { 526 if (mCollector->next([this, callback](const ResultSet::Result &result) {
524 if (operation != Sink::Operation_Removal) { 527 if (result.operation != Sink::Operation_Removal) {
525 SinkTrace() << "Got initial result: " << uid << operation; 528 SinkTrace() << "Got initial result: " << result.uid << result.operation;
526 callback(uid, buffer, Sink::Operation_Creation); 529 callback(ResultSet::Result{result.uid, result.buffer, Sink::Operation_Creation, result.aggregateValues});
527 } 530 }
528 })) 531 }))
529 { 532 {
diff --git a/common/datastorequery.h b/common/datastorequery.h
index 03b4eac..164d721 100644
--- a/common/datastorequery.h
+++ b/common/datastorequery.h
@@ -107,7 +107,7 @@ public:
107 virtual void skip() { mSource->skip(); }; 107 virtual void skip() { mSource->skip(); };
108 108
109 //Returns true for as long as a result is available 109 //Returns true for as long as a result is available
110 virtual bool next(const std::function<void(Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) = 0; 110 virtual bool next(const std::function<void(const ResultSet::Result &)> &callback) = 0;
111 111
112 QSharedPointer<FilterBase> mSource; 112 QSharedPointer<FilterBase> mSource;
113 DataStoreQuery *mDatastore; 113 DataStoreQuery *mDatastore;
diff --git a/common/entityreader.cpp b/common/entityreader.cpp
index bd973d0..cca1511 100644
--- a/common/entityreader.cpp
+++ b/common/entityreader.cpp
@@ -145,14 +145,14 @@ template <class DomainType>
145void EntityReader<DomainType>::query(const Sink::Query &query, const std::function<bool(const DomainType &)> &callback) 145void EntityReader<DomainType>::query(const Sink::Query &query, const std::function<bool(const DomainType &)> &callback)
146{ 146{
147 executeInitialQuery(query, 0, 0, 147 executeInitialQuery(query, 0, 0,
148 [&callback](const typename DomainType::Ptr &value, Sink::Operation operation) -> bool { 148 [&callback](const typename DomainType::Ptr &value, Sink::Operation operation, const QMap<QByteArray, QVariant> &) -> bool {
149 Q_ASSERT(operation == Sink::Operation_Creation); 149 Q_ASSERT(operation == Sink::Operation_Creation);
150 return callback(*value); 150 return callback(*value);
151 }); 151 });
152} 152}
153 153
154template <class DomainType> 154template <class DomainType>
155QPair<qint64, qint64> EntityReader<DomainType>::executeInitialQuery(const Sink::Query &query, int offset, int batchsize, const std::function<bool(const typename DomainType::Ptr &value, Sink::Operation operation)> &callback) 155QPair<qint64, qint64> EntityReader<DomainType>::executeInitialQuery(const Sink::Query &query, int offset, int batchsize, const ResultCallback &callback)
156{ 156{
157 QTime time; 157 QTime time;
158 time.start(); 158 time.start();
@@ -168,7 +168,7 @@ QPair<qint64, qint64> EntityReader<DomainType>::executeInitialQuery(const Sink::
168} 168}
169 169
170template <class DomainType> 170template <class DomainType>
171QPair<qint64, qint64> EntityReader<DomainType>::executeIncrementalQuery(const Sink::Query &query, qint64 lastRevision, const std::function<bool(const typename DomainType::Ptr &value, Sink::Operation operation)> &callback) 171QPair<qint64, qint64> EntityReader<DomainType>::executeIncrementalQuery(const Sink::Query &query, qint64 lastRevision, const ResultCallback &callback)
172{ 172{
173 QTime time; 173 QTime time;
174 time.start(); 174 time.start();
@@ -185,18 +185,18 @@ QPair<qint64, qint64> EntityReader<DomainType>::executeIncrementalQuery(const Si
185} 185}
186 186
187template <class DomainType> 187template <class DomainType>
188qint64 EntityReader<DomainType>::replaySet(ResultSet &resultSet, int offset, int batchSize, const std::function<bool(const typename DomainType::Ptr &value, Sink::Operation operation)> &callback) 188qint64 EntityReader<DomainType>::replaySet(ResultSet &resultSet, int offset, int batchSize, const ResultCallback &callback)
189{ 189{
190 SinkTrace() << "Skipping over " << offset << " results"; 190 SinkTrace() << "Skipping over " << offset << " results";
191 resultSet.skip(offset); 191 resultSet.skip(offset);
192 int counter = 0; 192 int counter = 0;
193 while (!batchSize || (counter < batchSize)) { 193 while (!batchSize || (counter < batchSize)) {
194 const bool ret = 194 const bool ret =
195 resultSet.next([this, &counter, callback](const QByteArray &uid, const Sink::EntityBuffer &value, Sink::Operation operation) -> bool { 195 resultSet.next([this, &counter, callback](const ResultSet::Result &result) -> bool {
196 counter++; 196 counter++;
197 auto adaptor = mDomainTypeAdaptorFactory.createAdaptor(value.entity()); 197 auto adaptor = mDomainTypeAdaptorFactory.createAdaptor(result.buffer.entity());
198 Q_ASSERT(adaptor); 198 Q_ASSERT(adaptor);
199 return callback(QSharedPointer<DomainType>::create(mResourceInstanceIdentifier, uid, value.revision(), adaptor), operation); 199 return callback(QSharedPointer<DomainType>::create(mResourceInstanceIdentifier, result.uid, result.buffer.revision(), adaptor), result.operation, result.aggregateValues);
200 }); 200 });
201 if (!ret) { 201 if (!ret) {
202 break; 202 break;
diff --git a/common/entityreader.h b/common/entityreader.h
index f216453..1e7b086 100644
--- a/common/entityreader.h
+++ b/common/entityreader.h
@@ -48,7 +48,7 @@ namespace EntityReaderUtils {
48template<typename DomainType> 48template<typename DomainType>
49class SINK_EXPORT EntityReader 49class SINK_EXPORT EntityReader
50{ 50{
51 typedef std::function<bool(const typename DomainType::Ptr &domainObject, Sink::Operation operation)> ResultCallback; 51 typedef std::function<bool(const typename DomainType::Ptr &domainObject, Sink::Operation operation, const QMap<QByteArray, QVariant> &aggregateValues)> ResultCallback;
52 52
53public: 53public:
54 EntityReader(const QByteArray &resourceType, const QByteArray &mResourceInstanceIdentifier, Sink::Storage::Transaction &transaction); 54 EntityReader(const QByteArray &resourceType, const QByteArray &mResourceInstanceIdentifier, Sink::Storage::Transaction &transaction);
diff --git a/common/query.h b/common/query.h
index 00ae086..3ab5acf 100644
--- a/common/query.h
+++ b/common/query.h
@@ -255,15 +255,48 @@ public:
255 Comparator comparator; 255 Comparator comparator;
256 }; 256 };
257 257
258 class Aggregator {
259 public:
260 enum Operation {
261 Count,
262 Collect
263 };
264
265 Aggregator(const QByteArray &p, Operation o, const QByteArray &c = QByteArray())
266 : resultProperty(p),
267 operation(o),
268 propertyToCollect(c)
269 {
270 }
271
272 QByteArray resultProperty;
273 Operation operation;
274 QByteArray propertyToCollect;
275 };
276
258 Reduce(const QByteArray &p, const Selector &s) 277 Reduce(const QByteArray &p, const Selector &s)
259 : property(p), 278 : property(p),
260 selector(s) 279 selector(s)
261 { 280 {
262 } 281 }
263 282
283 Reduce &count(const QByteArray &propertyName = "count")
284 {
285 aggregators << Aggregator(propertyName, Aggregator::Count);
286 return *this;
287 }
288
289 template <typename T>
290 Reduce &collect(const QByteArray &propertyName)
291 {
292 aggregators << Aggregator(propertyName, Aggregator::Collect, T::name);
293 return *this;
294 }
295
264 //Reduce on property 296 //Reduce on property
265 QByteArray property; 297 QByteArray property;
266 Selector selector; 298 Selector selector;
299 QList<Aggregator> aggregators;
267 300
268 //TODO add aggregate functions like: 301 //TODO add aggregate functions like:
269 //.count() 302 //.count()
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp
index 1835e1f..f037cfc 100644
--- a/common/queryrunner.cpp
+++ b/common/queryrunner.cpp
@@ -55,7 +55,7 @@ public:
55 55
56private: 56private:
57 Storage::Transaction getTransaction(); 57 Storage::Transaction getTransaction();
58 std::function<bool(const typename DomainType::Ptr &, Sink::Operation)> resultProviderCallback(const Sink::Query &query, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider); 58 std::function<bool(const typename DomainType::Ptr &, Sink::Operation, const QMap<QByteArray, QVariant> &)> resultProviderCallback(const Sink::Query &query, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider);
59 59
60 QueryRunnerBase::ResultTransformation mResultTransformation; 60 QueryRunnerBase::ResultTransformation mResultTransformation;
61 DomainTypeAdaptorFactoryInterface::Ptr mDomainTypeAdaptorFactory; 61 DomainTypeAdaptorFactoryInterface::Ptr mDomainTypeAdaptorFactory;
@@ -174,10 +174,13 @@ QueryWorker<DomainType>::~QueryWorker()
174} 174}
175 175
176template <class DomainType> 176template <class DomainType>
177std::function<bool(const typename DomainType::Ptr &, Sink::Operation)> QueryWorker<DomainType>::resultProviderCallback(const Sink::Query &query, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider) 177std::function<bool(const typename DomainType::Ptr &, Sink::Operation, const QMap<QByteArray, QVariant> &)> QueryWorker<DomainType>::resultProviderCallback(const Sink::Query &query, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider)
178{ 178{
179 return [this, &query, &resultProvider](const typename DomainType::Ptr &domainObject, Sink::Operation operation) -> bool { 179 return [this, &query, &resultProvider](const typename DomainType::Ptr &domainObject, Sink::Operation operation, const QMap<QByteArray, QVariant> &aggregateValues) -> bool {
180 auto valueCopy = Sink::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation<DomainType>(*domainObject, query.requestedProperties).template staticCast<DomainType>(); 180 auto valueCopy = Sink::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation<DomainType>(*domainObject, query.requestedProperties).template staticCast<DomainType>();
181 for (auto it = aggregateValues.constBegin(); it != aggregateValues.constEnd(); it++) {
182 valueCopy->setProperty(it.key(), it.value());
183 }
181 if (mResultTransformation) { 184 if (mResultTransformation) {
182 mResultTransformation(*valueCopy); 185 mResultTransformation(*valueCopy);
183 } 186 }
diff --git a/common/resultset.cpp b/common/resultset.cpp
index c3ed5f6..13b5f16 100644
--- a/common/resultset.cpp
+++ b/common/resultset.cpp
@@ -79,7 +79,7 @@ bool ResultSet::next()
79 return true; 79 return true;
80 } 80 }
81 } else { 81 } else {
82 next([](const QByteArray &, const Sink::EntityBuffer &, Sink::Operation) { return false; }); 82 next([](const Result &) { return false; });
83 } 83 }
84 return false; 84 return false;
85} 85}
diff --git a/common/resultset.h b/common/resultset.h
index 4e934fc..7b77417 100644
--- a/common/resultset.h
+++ b/common/resultset.h
@@ -19,6 +19,8 @@
19#pragma once 19#pragma once
20 20
21#include <QVector> 21#include <QVector>
22#include <QMap>
23#include <QVariant>
22#include <functional> 24#include <functional>
23#include "metadata_generated.h" 25#include "metadata_generated.h"
24#include "entitybuffer.h" 26#include "entitybuffer.h"
@@ -31,7 +33,13 @@
31class ResultSet 33class ResultSet
32{ 34{
33public: 35public:
34 typedef std::function<void(const QByteArray &uid, const Sink::EntityBuffer &, Sink::Operation)> Callback; 36 struct Result {
37 QByteArray uid;
38 Sink::EntityBuffer buffer;
39 Sink::Operation operation;
40 QMap<QByteArray, QVariant> aggregateValues;
41 };
42 typedef std::function<void(const Result &)> Callback;
35 typedef std::function<bool(Callback)> ValueGenerator; 43 typedef std::function<bool(Callback)> ValueGenerator;
36 typedef std::function<QByteArray()> IdGenerator; 44 typedef std::function<QByteArray()> IdGenerator;
37 typedef std::function<void()> SkipValue; 45 typedef std::function<void()> SkipValue;
diff --git a/common/standardqueries.h b/common/standardqueries.h
index 06ce396..07ce637 100644
--- a/common/standardqueries.h
+++ b/common/standardqueries.h
@@ -50,7 +50,7 @@ namespace StandardQueries {
50 } 50 }
51 query.filter<ApplicationDomain::Mail::Folder>(folder); 51 query.filter<ApplicationDomain::Mail::Folder>(folder);
52 query.sort<ApplicationDomain::Mail::Date>(); 52 query.sort<ApplicationDomain::Mail::Date>();
53 query.reduce<ApplicationDomain::Mail::ThreadId>(Query::Reduce::Selector::max<ApplicationDomain::Mail::Date>()); 53 query.reduce<ApplicationDomain::Mail::ThreadId>(Query::Reduce::Selector::max<ApplicationDomain::Mail::Date>()).count("count");
54 return query; 54 return query;
55 } 55 }
56 56
diff --git a/tests/mailthreadtest.cpp b/tests/mailthreadtest.cpp
index 9e5e911..917e3e2 100644
--- a/tests/mailthreadtest.cpp
+++ b/tests/mailthreadtest.cpp
@@ -70,7 +70,7 @@ void MailThreadTest::testListThreadLeader()
70 query.resourceFilter(mResourceInstanceIdentifier); 70 query.resourceFilter(mResourceInstanceIdentifier);
71 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>(); 71 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>();
72 query.sort<Mail::Date>(); 72 query.sort<Mail::Date>();
73 query.reduce<Mail::ThreadId>(Query::Reduce::Selector::max<Mail::Date>()); 73 query.reduce<Mail::ThreadId>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Sender>("senders");
74 74
75 // Ensure all local data is processed 75 // Ensure all local data is processed
76 VERIFYEXEC(Store::synchronize(query)); 76 VERIFYEXEC(Store::synchronize(query));
@@ -79,6 +79,8 @@ void MailThreadTest::testListThreadLeader()
79 auto mails = Store::read<Mail>(query); 79 auto mails = Store::read<Mail>(query);
80 QCOMPARE(mails.size(), 1); 80 QCOMPARE(mails.size(), 1);
81 QVERIFY(mails.first().getSubject().startsWith(QString("ThreadLeader"))); 81 QVERIFY(mails.first().getSubject().startsWith(QString("ThreadLeader")));
82 auto threadSize = mails.first().getProperty("count").toInt();
83 QCOMPARE(threadSize, 2);
82} 84}
83 85
84/* 86/*