diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-09-27 00:28:40 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-09-27 00:28:40 +0200 |
commit | fd532607ef29aac49b52c861e5aecda6dfa19e82 (patch) | |
tree | 3687ad63516a344f298c014c87a6f6209379a811 | |
parent | 47b9f2109f57c1121b760ea6d885ab08f12c46b3 (diff) | |
download | sink-fd532607ef29aac49b52c861e5aecda6dfa19e82.tar.gz sink-fd532607ef29aac49b52c861e5aecda6dfa19e82.zip |
New query api
-rw-r--r-- | common/datastorequery.cpp | 30 | ||||
-rw-r--r-- | common/domain/applicationdomaintype.cpp | 2 | ||||
-rw-r--r-- | common/query.h | 157 | ||||
-rw-r--r-- | examples/maildirresource/tests/maildirmailsynctest.cpp | 3 | ||||
-rw-r--r-- | examples/maildirresource/tests/maildirthreadtest.cpp | 2 | ||||
-rw-r--r-- | tests/mailthreadtest.cpp | 26 |
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"); | |||
29 | namespace Sink { | 29 | namespace Sink { |
30 | namespace ApplicationDomain { | 30 | namespace ApplicationDomain { |
31 | 31 | ||
32 | constexpr const char *Mail::ThreadId::name; | ||
33 | |||
32 | ApplicationDomainType::ApplicationDomainType() | 34 | ApplicationDomainType::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 | ||
244 | QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c); | 351 | QDebug 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() | |||
66 | void MailThreadTest::testListThreadLeader() | 66 | void 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 | }); |