diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-10-04 08:25:18 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-10-04 08:25:18 +0200 |
commit | d6a01b3f82d626856001356c0875aa738a0346ac (patch) | |
tree | d77a6000e9d53be7844d72697496efa093b5f8ad | |
parent | 48ba18c92eede967afc4cf8894a3b06fd6a3c179 (diff) | |
download | sink-d6a01b3f82d626856001356c0875aa738a0346ac.tar.gz sink-d6a01b3f82d626856001356c0875aa738a0346ac.zip |
Support for subqueries.
This allows us to match properties from a subquery.
Unfortunately this also means that DataStoreQuery needs access to all
type implementations to issue the subquery (for potentially another type).
-rw-r--r-- | common/datastorequery.cpp | 45 | ||||
-rw-r--r-- | common/datastorequery.h | 4 | ||||
-rw-r--r-- | common/domain/applicationdomaintype.h | 1 | ||||
-rw-r--r-- | common/query.cpp | 5 | ||||
-rw-r--r-- | common/query.h | 13 | ||||
-rw-r--r-- | common/queryrunner.cpp | 2 | ||||
-rw-r--r-- | common/queryrunner.h | 3 | ||||
-rw-r--r-- | common/typeindex.cpp | 34 | ||||
-rw-r--r-- | common/typeindex.h | 2 | ||||
-rw-r--r-- | tests/querytest.cpp | 36 |
10 files changed, 132 insertions, 13 deletions
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp index 3c4ae00..c4fbe13 100644 --- a/common/datastorequery.cpp +++ b/common/datastorequery.cpp | |||
@@ -21,6 +21,11 @@ | |||
21 | #include "log.h" | 21 | #include "log.h" |
22 | #include "entitybuffer.h" | 22 | #include "entitybuffer.h" |
23 | #include "entity_generated.h" | 23 | #include "entity_generated.h" |
24 | #include "applicationdomaintype.h" | ||
25 | |||
26 | #include "folder.h" | ||
27 | #include "mail.h" | ||
28 | #include "event.h" | ||
24 | 29 | ||
25 | using namespace Sink; | 30 | using namespace Sink; |
26 | 31 | ||
@@ -373,8 +378,44 @@ QVector<QByteArray> DataStoreQuery::indexLookup(const QByteArray &property, cons | |||
373 | /* } */ | 378 | /* } */ |
374 | /* } */ | 379 | /* } */ |
375 | 380 | ||
381 | template <typename ... Args> | ||
382 | QSharedPointer<DataStoreQuery> prepareQuery(const QByteArray &type, Args && ... args) | ||
383 | { | ||
384 | if (type == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) { | ||
385 | return ApplicationDomain::TypeImplementation<ApplicationDomain::Folder>::prepareQuery(std::forward<Args>(args)...); | ||
386 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) { | ||
387 | return ApplicationDomain::TypeImplementation<ApplicationDomain::Mail>::prepareQuery(std::forward<Args>(args)...); | ||
388 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Event>()) { | ||
389 | return ApplicationDomain::TypeImplementation<ApplicationDomain::Event>::prepareQuery(std::forward<Args>(args)...); | ||
390 | } | ||
391 | Q_ASSERT(false); | ||
392 | return QSharedPointer<DataStoreQuery>(); | ||
393 | } | ||
394 | |||
395 | QByteArrayList DataStoreQuery::executeSubquery(const Query &subquery) | ||
396 | { | ||
397 | Q_ASSERT(!subquery.type.isEmpty()); | ||
398 | auto sub = prepareQuery(subquery.type, subquery, mTransaction); | ||
399 | auto result = sub->execute(); | ||
400 | QByteArrayList ids; | ||
401 | while (result.next([&ids](const QByteArray &uid, const Sink::EntityBuffer &, Sink::Operation) { | ||
402 | ids << uid; | ||
403 | })) | ||
404 | {} | ||
405 | return ids; | ||
406 | } | ||
407 | |||
376 | void DataStoreQuery::setupQuery() | 408 | void DataStoreQuery::setupQuery() |
377 | { | 409 | { |
410 | for (const auto &k : mQuery.propertyFilter.keys()) { | ||
411 | const auto comparator = mQuery.propertyFilter.value(k); | ||
412 | if (comparator.value.canConvert<Query>()) { | ||
413 | SinkTrace() << "Executing subquery for property: " << k; | ||
414 | const auto result = executeSubquery(comparator.value.value<Query>()); | ||
415 | mQuery.propertyFilter.insert(k, Query::Comparator(QVariant::fromValue(result), Query::Comparator::In)); | ||
416 | } | ||
417 | } | ||
418 | |||
378 | FilterBase::Ptr baseSet; | 419 | FilterBase::Ptr baseSet; |
379 | QSet<QByteArray> remainingFilters = mQuery.getBaseFilters().keys().toSet(); | 420 | QSet<QByteArray> remainingFilters = mQuery.getBaseFilters().keys().toSet(); |
380 | QByteArray appliedSorting; | 421 | QByteArray appliedSorting; |
@@ -459,7 +500,7 @@ ResultSet DataStoreQuery::update(qint64 baseRevision) | |||
459 | SinkTrace() << "Changed: " << incrementalResultSet; | 500 | SinkTrace() << "Changed: " << incrementalResultSet; |
460 | mSource->add(incrementalResultSet); | 501 | mSource->add(incrementalResultSet); |
461 | ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool { | 502 | ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool { |
462 | if (mCollector->next([callback](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &buffer) { | 503 | if (mCollector->next([this, callback](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &buffer) { |
463 | SinkTrace() << "Got incremental result: " << uid << operation; | 504 | SinkTrace() << "Got incremental result: " << uid << operation; |
464 | callback(uid, buffer, operation); | 505 | callback(uid, buffer, operation); |
465 | })) | 506 | })) |
@@ -477,7 +518,7 @@ ResultSet DataStoreQuery::execute() | |||
477 | SinkTrace() << "Executing query"; | 518 | SinkTrace() << "Executing query"; |
478 | 519 | ||
479 | ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool { | 520 | ResultSet::ValueGenerator generator = [this](const ResultSet::Callback &callback) -> bool { |
480 | if (mCollector->next([callback](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &buffer) { | 521 | if (mCollector->next([this, callback](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &buffer) { |
481 | if (operation != Sink::Operation_Removal) { | 522 | if (operation != Sink::Operation_Removal) { |
482 | SinkTrace() << "Got initial result: " << uid << operation; | 523 | SinkTrace() << "Got initial result: " << uid << operation; |
483 | callback(uid, buffer, Sink::Operation_Creation); | 524 | callback(uid, buffer, Sink::Operation_Creation); |
diff --git a/common/datastorequery.h b/common/datastorequery.h index 6620bbe..03b4eac 100644 --- a/common/datastorequery.h +++ b/common/datastorequery.h | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "typeindex.h" | 24 | #include "typeindex.h" |
25 | #include "query.h" | 25 | #include "query.h" |
26 | #include "entitybuffer.h" | 26 | #include "entitybuffer.h" |
27 | #include "log.h" | ||
27 | 28 | ||
28 | 29 | ||
29 | class Source; | 30 | class Source; |
@@ -52,6 +53,7 @@ protected: | |||
52 | QVector<QByteArray> loadIncrementalResultSet(qint64 baseRevision); | 53 | QVector<QByteArray> loadIncrementalResultSet(qint64 baseRevision); |
53 | 54 | ||
54 | void setupQuery(); | 55 | void setupQuery(); |
56 | QByteArrayList executeSubquery(const Sink::Query &subquery); | ||
55 | 57 | ||
56 | Sink::Query mQuery; | 58 | Sink::Query mQuery; |
57 | Sink::Storage::Transaction &mTransaction; | 59 | Sink::Storage::Transaction &mTransaction; |
@@ -62,6 +64,8 @@ protected: | |||
62 | bool mInitialQuery; | 64 | bool mInitialQuery; |
63 | QSharedPointer<FilterBase> mCollector; | 65 | QSharedPointer<FilterBase> mCollector; |
64 | QSharedPointer<Source> mSource; | 66 | QSharedPointer<Source> mSource; |
67 | |||
68 | SINK_DEBUG_COMPONENT(mType) | ||
65 | }; | 69 | }; |
66 | 70 | ||
67 | 71 | ||
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index 8b96758..2c93639 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h | |||
@@ -74,6 +74,7 @@ | |||
74 | struct NAME { \ | 74 | struct NAME { \ |
75 | static constexpr const char *name = #LOWERCASENAME; \ | 75 | static constexpr const char *name = #LOWERCASENAME; \ |
76 | typedef QByteArray Type; \ | 76 | typedef QByteArray Type; \ |
77 | typedef ApplicationDomain::TYPE ReferenceType; \ | ||
77 | }; \ | 78 | }; \ |
78 | void set##NAME(const ApplicationDomain::TYPE &value) { setProperty(NAME::name, value); } \ | 79 | void set##NAME(const ApplicationDomain::TYPE &value) { setProperty(NAME::name, value); } \ |
79 | void set##NAME(const QByteArray &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ | 80 | void set##NAME(const QByteArray &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ |
diff --git a/common/query.cpp b/common/query.cpp index 3de80d8..a43ef6c 100644 --- a/common/query.cpp +++ b/common/query.cpp | |||
@@ -62,6 +62,11 @@ bool Query::Comparator::matches(const QVariant &v) const | |||
62 | return false; | 62 | return false; |
63 | } | 63 | } |
64 | return v.value<QByteArrayList>().contains(value.toByteArray()); | 64 | return v.value<QByteArrayList>().contains(value.toByteArray()); |
65 | case In: | ||
66 | if (!v.isValid()) { | ||
67 | return false; | ||
68 | } | ||
69 | return value.value<QByteArrayList>().contains(v.toByteArray()); | ||
65 | case Invalid: | 70 | case Invalid: |
66 | default: | 71 | default: |
67 | break; | 72 | break; |
diff --git a/common/query.h b/common/query.h index 3362ac7..403c5b5 100644 --- a/common/query.h +++ b/common/query.h | |||
@@ -46,7 +46,8 @@ public: | |||
46 | enum Comparators { | 46 | enum Comparators { |
47 | Invalid, | 47 | Invalid, |
48 | Equals, | 48 | Equals, |
49 | Contains | 49 | Contains, |
50 | In | ||
50 | }; | 51 | }; |
51 | 52 | ||
52 | Comparator(); | 53 | Comparator(); |
@@ -158,6 +159,7 @@ public: | |||
158 | QByteArrayList requestedProperties; | 159 | QByteArrayList requestedProperties; |
159 | QByteArray parentProperty; | 160 | QByteArray parentProperty; |
160 | QByteArray sortProperty; | 161 | QByteArray sortProperty; |
162 | QByteArray type; | ||
161 | int limit; | 163 | int limit; |
162 | bool liveQuery; | 164 | bool liveQuery; |
163 | bool synchronousQuery; | 165 | bool synchronousQuery; |
@@ -222,6 +224,14 @@ public: | |||
222 | return filter(T::name, QVariant::fromValue(value.identifier())); | 224 | return filter(T::name, QVariant::fromValue(value.identifier())); |
223 | } | 225 | } |
224 | 226 | ||
227 | template <typename T> | ||
228 | Query &filter(const Query &query) | ||
229 | { | ||
230 | auto q = query; | ||
231 | q.type = ApplicationDomain::getTypeName<typename T::ReferenceType>(); | ||
232 | return filter(T::name, QVariant::fromValue(q)); | ||
233 | } | ||
234 | |||
225 | Query &filter(const ApplicationDomain::SinkResource &resource) | 235 | Query &filter(const ApplicationDomain::SinkResource &resource) |
226 | { | 236 | { |
227 | resources << resource.identifier(); | 237 | resources << resource.identifier(); |
@@ -353,3 +363,4 @@ public: | |||
353 | QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c); | 363 | QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c); |
354 | 364 | ||
355 | Q_DECLARE_OPERATORS_FOR_FLAGS(Sink::Query::Flags) | 365 | Q_DECLARE_OPERATORS_FOR_FLAGS(Sink::Query::Flags) |
366 | Q_DECLARE_METATYPE(Sink::Query); | ||
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp index d3f8f66..1835e1f 100644 --- a/common/queryrunner.cpp +++ b/common/queryrunner.cpp | |||
@@ -66,7 +66,7 @@ private: | |||
66 | template <class DomainType> | 66 | template <class DomainType> |
67 | QueryRunner<DomainType>::QueryRunner(const Sink::Query &query, const Sink::ResourceAccessInterface::Ptr &resourceAccess, const QByteArray &instanceIdentifier, | 67 | QueryRunner<DomainType>::QueryRunner(const Sink::Query &query, const Sink::ResourceAccessInterface::Ptr &resourceAccess, const QByteArray &instanceIdentifier, |
68 | const DomainTypeAdaptorFactoryInterface::Ptr &factory, const QByteArray &bufferType) | 68 | const DomainTypeAdaptorFactoryInterface::Ptr &factory, const QByteArray &bufferType) |
69 | : QueryRunnerBase(), mResourceAccess(resourceAccess), mResultProvider(new ResultProvider<typename DomainType::Ptr>), mBatchSize(query.limit) | 69 | : QueryRunnerBase(), mResourceInstanceIdentifier(instanceIdentifier), mResourceAccess(resourceAccess), mResultProvider(new ResultProvider<typename DomainType::Ptr>), mBatchSize(query.limit) |
70 | { | 70 | { |
71 | SinkTrace() << "Starting query"; | 71 | SinkTrace() << "Starting query"; |
72 | if (query.limit && query.sortProperty.isEmpty()) { | 72 | if (query.limit && query.sortProperty.isEmpty()) { |
diff --git a/common/queryrunner.h b/common/queryrunner.h index 439a990..78aabf6 100644 --- a/common/queryrunner.h +++ b/common/queryrunner.h | |||
@@ -25,6 +25,7 @@ | |||
25 | #include "domaintypeadaptorfactoryinterface.h" | 25 | #include "domaintypeadaptorfactoryinterface.h" |
26 | #include "storage.h" | 26 | #include "storage.h" |
27 | #include "query.h" | 27 | #include "query.h" |
28 | #include "log.h" | ||
28 | 29 | ||
29 | /** | 30 | /** |
30 | * Base clase because you can't have the Q_OBJECT macro in template classes | 31 | * Base clase because you can't have the Q_OBJECT macro in template classes |
@@ -96,6 +97,8 @@ public: | |||
96 | typename Sink::ResultEmitter<typename DomainType::Ptr>::Ptr emitter(); | 97 | typename Sink::ResultEmitter<typename DomainType::Ptr>::Ptr emitter(); |
97 | 98 | ||
98 | private: | 99 | private: |
100 | QByteArray mResourceInstanceIdentifier; | ||
101 | SINK_DEBUG_COMPONENT(mResourceInstanceIdentifier) | ||
99 | QSharedPointer<Sink::ResourceAccessInterface> mResourceAccess; | 102 | QSharedPointer<Sink::ResourceAccessInterface> mResourceAccess; |
100 | QSharedPointer<Sink::ResultProvider<typename DomainType::Ptr>> mResultProvider; | 103 | QSharedPointer<Sink::ResultProvider<typename DomainType::Ptr>> mResultProvider; |
101 | ResultTransformation mResultTransformation; | 104 | ResultTransformation mResultTransformation; |
diff --git a/common/typeindex.cpp b/common/typeindex.cpp index 272237c..816e7ee 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp | |||
@@ -24,6 +24,8 @@ | |||
24 | 24 | ||
25 | SINK_DEBUG_AREA("typeindex") | 25 | SINK_DEBUG_AREA("typeindex") |
26 | 26 | ||
27 | using namespace Sink; | ||
28 | |||
27 | static QByteArray getByteArray(const QVariant &value) | 29 | static QByteArray getByteArray(const QVariant &value) |
28 | { | 30 | { |
29 | if (value.type() == QVariant::DateTime) { | 31 | if (value.type() == QVariant::DateTime) { |
@@ -138,16 +140,32 @@ void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDoma | |||
138 | } | 140 | } |
139 | } | 141 | } |
140 | 142 | ||
143 | static QVector<QByteArray> indexLookup(Index &index, Query::Comparator filter) | ||
144 | { | ||
145 | QVector<QByteArray> keys; | ||
146 | QByteArrayList lookupKeys; | ||
147 | if (filter.comparator == Query::Comparator::Equals) { | ||
148 | lookupKeys << getByteArray(filter.value); | ||
149 | } else if (filter.comparator == Query::Comparator::In) { | ||
150 | lookupKeys = filter.value.value<QByteArrayList>(); | ||
151 | } else { | ||
152 | Q_ASSERT(false); | ||
153 | } | ||
154 | |||
155 | for (const auto &lookupKey : lookupKeys) { | ||
156 | index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; }, | ||
157 | [lookupKey](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << lookupKey; }, true); | ||
158 | } | ||
159 | return keys; | ||
160 | } | ||
161 | |||
141 | QVector<QByteArray> TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction) | 162 | QVector<QByteArray> TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction) |
142 | { | 163 | { |
143 | QVector<QByteArray> keys; | 164 | QVector<QByteArray> keys; |
144 | for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { | 165 | for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { |
145 | if (query.hasFilter(it.key()) && query.sortProperty == it.value()) { | 166 | if (query.hasFilter(it.key()) && query.sortProperty == it.value()) { |
146 | Index index(indexName(it.key(), it.value()), transaction); | 167 | Index index(indexName(it.key(), it.value()), transaction); |
147 | const auto lookupKey = getByteArray(query.getFilter(it.key()).value); | 168 | keys << indexLookup(index, query.getFilter(it.key())); |
148 | SinkTrace() << "looking for " << lookupKey; | ||
149 | index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; }, | ||
150 | [it](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << it.key() << it.value(); }, true); | ||
151 | appliedFilters << it.key(); | 169 | appliedFilters << it.key(); |
152 | appliedSorting = it.value(); | 170 | appliedSorting = it.value(); |
153 | SinkTrace() << "Index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; | 171 | SinkTrace() << "Index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; |
@@ -157,9 +175,7 @@ QVector<QByteArray> TypeIndex::query(const Sink::Query &query, QSet<QByteArray> | |||
157 | for (const auto &property : mProperties) { | 175 | for (const auto &property : mProperties) { |
158 | if (query.hasFilter(property)) { | 176 | if (query.hasFilter(property)) { |
159 | Index index(indexName(property), transaction); | 177 | Index index(indexName(property), transaction); |
160 | const auto lookupKey = getByteArray(query.getFilter(property).value); | 178 | keys << indexLookup(index, query.getFilter(property)); |
161 | index.lookup( | ||
162 | lookupKey, [&](const QByteArray &value) { keys << value; }, [property](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << property; }); | ||
163 | appliedFilters << property; | 179 | appliedFilters << property; |
164 | SinkTrace() << "Index lookup on " << property << " found " << keys.size() << " keys."; | 180 | SinkTrace() << "Index lookup on " << property << " found " << keys.size() << " keys."; |
165 | return keys; | 181 | return keys; |
@@ -177,7 +193,7 @@ QVector<QByteArray> TypeIndex::lookup(const QByteArray &property, const QVariant | |||
177 | Index index(indexName(property), transaction); | 193 | Index index(indexName(property), transaction); |
178 | const auto lookupKey = getByteArray(value); | 194 | const auto lookupKey = getByteArray(value); |
179 | index.lookup( | 195 | index.lookup( |
180 | lookupKey, [&](const QByteArray &value) { keys << value; }, [property](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << property; }); | 196 | lookupKey, [&, this](const QByteArray &value) { keys << value; }, [property, this](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << property; }); |
181 | SinkTrace() << "Index lookup on " << property << " found " << keys.size() << " keys."; | 197 | SinkTrace() << "Index lookup on " << property << " found " << keys.size() << " keys."; |
182 | return keys; | 198 | return keys; |
183 | } else if (mSecondaryProperties.contains(property)) { | 199 | } else if (mSecondaryProperties.contains(property)) { |
@@ -189,7 +205,7 @@ QVector<QByteArray> TypeIndex::lookup(const QByteArray &property, const QVariant | |||
189 | Index index(indexName(property + resultProperty), transaction); | 205 | Index index(indexName(property + resultProperty), transaction); |
190 | const auto lookupKey = getByteArray(value); | 206 | const auto lookupKey = getByteArray(value); |
191 | index.lookup( | 207 | index.lookup( |
192 | lookupKey, [&](const QByteArray &value) { secondaryKeys << value; }, [property](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << property; }); | 208 | lookupKey, [&, this](const QByteArray &value) { secondaryKeys << value; }, [property, this](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << property; }); |
193 | SinkTrace() << "Looked up secondary keys: " << secondaryKeys; | 209 | SinkTrace() << "Looked up secondary keys: " << secondaryKeys; |
194 | for (const auto &secondary : secondaryKeys) { | 210 | for (const auto &secondary : secondaryKeys) { |
195 | keys += lookup(resultProperty, secondary, transaction); | 211 | keys += lookup(resultProperty, secondary, transaction); |
diff --git a/common/typeindex.h b/common/typeindex.h index 7266f02..4972e95 100644 --- a/common/typeindex.h +++ b/common/typeindex.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "bufferadaptor.h" | 22 | #include "bufferadaptor.h" |
23 | #include "storage.h" | 23 | #include "storage.h" |
24 | #include "query.h" | 24 | #include "query.h" |
25 | #include "log.h" | ||
25 | #include <QByteArray> | 26 | #include <QByteArray> |
26 | 27 | ||
27 | class TypeIndex | 28 | class TypeIndex |
@@ -79,6 +80,7 @@ public: | |||
79 | private: | 80 | private: |
80 | QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; | 81 | QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; |
81 | QByteArray mType; | 82 | QByteArray mType; |
83 | SINK_DEBUG_COMPONENT(mType) | ||
82 | QByteArrayList mProperties; | 84 | QByteArrayList mProperties; |
83 | QMap<QByteArray, QByteArray> mSortedProperties; | 85 | QMap<QByteArray, QByteArray> mSortedProperties; |
84 | //<Property, ResultProperty> | 86 | //<Property, ResultProperty> |
diff --git a/tests/querytest.cpp b/tests/querytest.cpp index afa8e33..6011a99 100644 --- a/tests/querytest.cpp +++ b/tests/querytest.cpp | |||
@@ -492,6 +492,42 @@ private slots: | |||
492 | QCOMPARE(folders.size(), 1); | 492 | QCOMPARE(folders.size(), 1); |
493 | } | 493 | } |
494 | 494 | ||
495 | void testSubquery() | ||
496 | { | ||
497 | // Setup | ||
498 | auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); | ||
499 | folder1.setSpecialPurpose(QByteArrayList() << "purpose1"); | ||
500 | VERIFYEXEC(Sink::Store::create<Folder>(folder1)); | ||
501 | |||
502 | auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); | ||
503 | folder2.setSpecialPurpose(QByteArrayList() << "purpose2"); | ||
504 | VERIFYEXEC(Sink::Store::create<Folder>(folder2)); | ||
505 | |||
506 | { | ||
507 | auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); | ||
508 | mail.setUid("mail1"); | ||
509 | mail.setFolder(folder1); | ||
510 | VERIFYEXEC(Sink::Store::create(mail)); | ||
511 | } | ||
512 | { | ||
513 | auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); | ||
514 | mail.setUid("mail2"); | ||
515 | mail.setFolder(folder2); | ||
516 | VERIFYEXEC(Sink::Store::create(mail)); | ||
517 | } | ||
518 | |||
519 | // Ensure all local data is processed | ||
520 | Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1").exec().waitForFinished(); | ||
521 | |||
522 | //Setup two folders with a mail each, ensure we only get the mail from the folder that matches the folder filter. | ||
523 | Query query; | ||
524 | query.filter<Mail::Folder>(Sink::Query().containsFilter<Folder::SpecialPurpose>("purpose1")); | ||
525 | query.request<Mail::Uid>(); | ||
526 | |||
527 | auto mails = Sink::Store::read<Mail>(query); | ||
528 | QCOMPARE(mails.size(), 1); | ||
529 | QCOMPARE(mails.first().getUid().toLatin1(), QByteArray("mail1")); | ||
530 | } | ||
495 | }; | 531 | }; |
496 | 532 | ||
497 | QTEST_MAIN(QueryTest) | 533 | QTEST_MAIN(QueryTest) |