diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-10-06 17:52:52 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-10-06 17:52:52 +0200 |
commit | 87695f52d5ac627cdd710f37c275fccdf920af0b (patch) | |
tree | 733a7e66fafd3a0ae747b050427f2d7762bde793 | |
parent | f1e496f7c12ebc787ed47a4c048015f2098e65d9 (diff) | |
download | sink-87695f52d5ac627cdd710f37c275fccdf920af0b.tar.gz sink-87695f52d5ac627cdd710f37c275fccdf920af0b.zip |
count as a first aggregation function
-rw-r--r-- | common/datastorequery.cpp | 65 | ||||
-rw-r--r-- | common/datastorequery.h | 2 | ||||
-rw-r--r-- | common/entityreader.cpp | 14 | ||||
-rw-r--r-- | common/entityreader.h | 2 | ||||
-rw-r--r-- | common/query.h | 33 | ||||
-rw-r--r-- | common/queryrunner.cpp | 9 | ||||
-rw-r--r-- | common/resultset.cpp | 2 | ||||
-rw-r--r-- | common/resultset.h | 10 | ||||
-rw-r--r-- | common/standardqueries.h | 2 | ||||
-rw-r--r-- | tests/mailthreadtest.cpp | 4 |
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> | |||
145 | void EntityReader<DomainType>::query(const Sink::Query &query, const std::function<bool(const DomainType &)> &callback) | 145 | void 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 | ||
154 | template <class DomainType> | 154 | template <class DomainType> |
155 | QPair<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) | 155 | QPair<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 | ||
170 | template <class DomainType> | 170 | template <class DomainType> |
171 | QPair<qint64, qint64> EntityReader<DomainType>::executeIncrementalQuery(const Sink::Query &query, qint64 lastRevision, const std::function<bool(const typename DomainType::Ptr &value, Sink::Operation operation)> &callback) | 171 | QPair<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 | ||
187 | template <class DomainType> | 187 | template <class DomainType> |
188 | qint64 EntityReader<DomainType>::replaySet(ResultSet &resultSet, int offset, int batchSize, const std::function<bool(const typename DomainType::Ptr &value, Sink::Operation operation)> &callback) | 188 | qint64 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 { | |||
48 | template<typename DomainType> | 48 | template<typename DomainType> |
49 | class SINK_EXPORT EntityReader | 49 | class 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 | ||
53 | public: | 53 | public: |
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 | ||
56 | private: | 56 | private: |
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 | ||
176 | template <class DomainType> | 176 | template <class DomainType> |
177 | std::function<bool(const typename DomainType::Ptr &, Sink::Operation)> QueryWorker<DomainType>::resultProviderCallback(const Sink::Query &query, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider) | 177 | std::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 @@ | |||
31 | class ResultSet | 33 | class ResultSet |
32 | { | 34 | { |
33 | public: | 35 | public: |
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 | /* |