summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/datastorequery.cpp45
-rw-r--r--common/datastorequery.h4
-rw-r--r--common/domain/applicationdomaintype.h1
-rw-r--r--common/query.cpp5
-rw-r--r--common/query.h13
-rw-r--r--common/queryrunner.cpp2
-rw-r--r--common/queryrunner.h3
-rw-r--r--common/typeindex.cpp34
-rw-r--r--common/typeindex.h2
-rw-r--r--tests/querytest.cpp36
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
25using namespace Sink; 30using namespace Sink;
26 31
@@ -373,8 +378,44 @@ QVector<QByteArray> DataStoreQuery::indexLookup(const QByteArray &property, cons
373/* } */ 378/* } */
374/* } */ 379/* } */
375 380
381template <typename ... Args>
382QSharedPointer<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
395QByteArrayList 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
376void DataStoreQuery::setupQuery() 408void 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
29class Source; 30class 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:
353QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c); 363QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c);
354 364
355Q_DECLARE_OPERATORS_FOR_FLAGS(Sink::Query::Flags) 365Q_DECLARE_OPERATORS_FOR_FLAGS(Sink::Query::Flags)
366Q_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:
66template <class DomainType> 66template <class DomainType>
67QueryRunner<DomainType>::QueryRunner(const Sink::Query &query, const Sink::ResourceAccessInterface::Ptr &resourceAccess, const QByteArray &instanceIdentifier, 67QueryRunner<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
98private: 99private:
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
25SINK_DEBUG_AREA("typeindex") 25SINK_DEBUG_AREA("typeindex")
26 26
27using namespace Sink;
28
27static QByteArray getByteArray(const QVariant &value) 29static 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
143static 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
141QVector<QByteArray> TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction) 162QVector<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
27class TypeIndex 28class TypeIndex
@@ -79,6 +80,7 @@ public:
79private: 80private:
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
497QTEST_MAIN(QueryTest) 533QTEST_MAIN(QueryTest)