diff options
-rw-r--r-- | common/resultset.h | 60 | ||||
-rw-r--r-- | examples/dummyresource/facade.cpp | 48 | ||||
-rw-r--r-- | tests/dummyresourcetest.cpp | 51 |
3 files changed, 142 insertions, 17 deletions
diff --git a/common/resultset.h b/common/resultset.h index f9b6cea..33f3c68 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 <functional> | ||
23 | #include "domain/applicationdomaintype.h" | ||
22 | 24 | ||
23 | /* | 25 | /* |
24 | * An iterator to a result set. | 26 | * An iterator to a result set. |
@@ -27,6 +29,22 @@ | |||
27 | */ | 29 | */ |
28 | class ResultSet { | 30 | class ResultSet { |
29 | public: | 31 | public: |
32 | |||
33 | |||
34 | ResultSet(const std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)>)> &generator) | ||
35 | : mValueGenerator(generator), | ||
36 | mIt(nullptr) | ||
37 | { | ||
38 | |||
39 | } | ||
40 | |||
41 | ResultSet(const std::function<QByteArray()> &generator) | ||
42 | : mGenerator(generator), | ||
43 | mIt(nullptr) | ||
44 | { | ||
45 | |||
46 | } | ||
47 | |||
30 | ResultSet(const QVector<QByteArray> &resultSet) | 48 | ResultSet(const QVector<QByteArray> &resultSet) |
31 | : mResultSet(resultSet), | 49 | : mResultSet(resultSet), |
32 | mIt(nullptr) | 50 | mIt(nullptr) |
@@ -36,17 +54,46 @@ class ResultSet { | |||
36 | 54 | ||
37 | bool next() | 55 | bool next() |
38 | { | 56 | { |
39 | if (!mIt) { | 57 | if (mGenerator) { |
40 | mIt = mResultSet.constBegin(); | 58 | mCurrentValue = mGenerator(); |
41 | } else { | 59 | } else { |
42 | mIt++; | 60 | if (!mIt) { |
61 | mIt = mResultSet.constBegin(); | ||
62 | } else { | ||
63 | mIt++; | ||
64 | } | ||
65 | return mIt != mResultSet.constEnd(); | ||
43 | } | 66 | } |
44 | return mIt != mResultSet.constEnd(); | 67 | } |
68 | |||
69 | bool next(std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value)> callback) | ||
70 | { | ||
71 | Q_ASSERT(mValueGenerator); | ||
72 | return mValueGenerator(callback); | ||
73 | } | ||
74 | |||
75 | bool next(std::function<void(const QByteArray &key)> callback) | ||
76 | { | ||
77 | if (mGenerator) { | ||
78 | mCurrentValue = mGenerator(); | ||
79 | } else { | ||
80 | if (!mIt) { | ||
81 | mIt = mResultSet.constBegin(); | ||
82 | } else { | ||
83 | mIt++; | ||
84 | } | ||
85 | return mIt != mResultSet.constEnd(); | ||
86 | } | ||
87 | |||
45 | } | 88 | } |
46 | 89 | ||
47 | QByteArray id() | 90 | QByteArray id() |
48 | { | 91 | { |
49 | return *mIt; | 92 | if (mIt) { |
93 | return *mIt; | ||
94 | } else { | ||
95 | return mCurrentValue; | ||
96 | } | ||
50 | } | 97 | } |
51 | 98 | ||
52 | bool isEmpty() | 99 | bool isEmpty() |
@@ -57,5 +104,8 @@ class ResultSet { | |||
57 | private: | 104 | private: |
58 | QVector<QByteArray> mResultSet; | 105 | QVector<QByteArray> mResultSet; |
59 | QVector<QByteArray>::ConstIterator mIt; | 106 | QVector<QByteArray>::ConstIterator mIt; |
107 | QByteArray mCurrentValue; | ||
108 | std::function<QByteArray()> mGenerator; | ||
109 | std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)>)> mValueGenerator; | ||
60 | }; | 110 | }; |
61 | 111 | ||
diff --git a/examples/dummyresource/facade.cpp b/examples/dummyresource/facade.cpp index 9d4f64b..002b836 100644 --- a/examples/dummyresource/facade.cpp +++ b/examples/dummyresource/facade.cpp | |||
@@ -74,7 +74,7 @@ static void scan(const QSharedPointer<Akonadi2::Storage> &storage, const QByteAr | |||
74 | }); | 74 | }); |
75 | } | 75 | } |
76 | 76 | ||
77 | void DummyResourceFacade::readValue(const QSharedPointer<Akonadi2::Storage> &storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback) | 77 | static void readValue(const QSharedPointer<Akonadi2::Storage> &storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback, const QSharedPointer<DomainTypeAdaptorFactoryInterface<Akonadi2::ApplicationDomain::Event> > &adaptorFactory) |
78 | { | 78 | { |
79 | scan(storage, key, [=](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { | 79 | scan(storage, key, [=](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { |
80 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; | 80 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; |
@@ -82,19 +82,21 @@ void DummyResourceFacade::readValue(const QSharedPointer<Akonadi2::Storage> &sto | |||
82 | //Not i.e. for tags that are stored as flags in each entity of an imap store. | 82 | //Not i.e. for tags that are stored as flags in each entity of an imap store. |
83 | //additional properties that don't have a 1:1 mapping (such as separately stored tags), | 83 | //additional properties that don't have a 1:1 mapping (such as separately stored tags), |
84 | //could be added to the adaptor | 84 | //could be added to the adaptor |
85 | auto event = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("org.kde.dummy.instance1", key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)); | 85 | auto event = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("org.kde.dummy.instance1", key, revision, adaptorFactory->createAdaptor(entity)); |
86 | resultCallback(event); | 86 | resultCallback(event); |
87 | return true; | 87 | return true; |
88 | }); | 88 | }); |
89 | } | 89 | } |
90 | 90 | ||
91 | static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::Storage> &storage) | 91 | static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::Storage> &storage, const QSharedPointer<DomainTypeAdaptorFactoryInterface<Akonadi2::ApplicationDomain::Event> > &adaptorFactory) |
92 | { | 92 | { |
93 | QSet<QByteArray> appliedFilters; | 93 | QSet<QByteArray> appliedFilters; |
94 | ResultSet resultSet = Akonadi2::ApplicationDomain::TypeImplementation<Akonadi2::ApplicationDomain::Event>::queryIndexes(query, "org.kde.dummy.instance1", appliedFilters); | 94 | ResultSet resultSet = Akonadi2::ApplicationDomain::TypeImplementation<Akonadi2::ApplicationDomain::Event>::queryIndexes(query, "org.kde.dummy.instance1", appliedFilters); |
95 | const auto remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; | 95 | const auto remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; |
96 | 96 | ||
97 | if (resultSet.isEmpty()) { | 97 | //We do a full scan if there were no indexes available to create the initial set. |
98 | //TODO use a result set with an iterator, to read values on demand | ||
99 | if (appliedFilters.isEmpty()) { | ||
98 | QVector<QByteArray> keys; | 100 | QVector<QByteArray> keys; |
99 | scan(storage, QByteArray(), [=, &keys](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { | 101 | scan(storage, QByteArray(), [=, &keys](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { |
100 | keys << key; | 102 | keys << key; |
@@ -103,9 +105,34 @@ static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer | |||
103 | resultSet = ResultSet(keys); | 105 | resultSet = ResultSet(keys); |
104 | } | 106 | } |
105 | 107 | ||
106 | return resultSet; | 108 | auto filter = [remainingFilters, query](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &event) -> bool { |
109 | for (const auto &filterProperty : remainingFilters) { | ||
110 | //TODO implement other comparison operators than equality | ||
111 | if (event->getProperty(filterProperty) != query.propertyFilter.value(filterProperty)) { | ||
112 | return false; | ||
113 | } | ||
114 | } | ||
115 | return true; | ||
116 | }; | ||
117 | |||
118 | auto resultSetPtr = QSharedPointer<ResultSet>::create(resultSet); | ||
119 | |||
120 | //Read through the source values and return whatever matches the filter | ||
121 | std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)>)> generator = [resultSetPtr, storage, adaptorFactory, filter](std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)> callback) -> bool { | ||
122 | while (resultSetPtr->next()) { | ||
123 | Akonadi2::ApplicationDomain::Event::Ptr event; | ||
124 | readValue(storage, resultSetPtr->id(), [filter, callback](const Akonadi2::ApplicationDomain::Event::Ptr &event) { | ||
125 | if (filter(event)) { | ||
126 | callback(event); | ||
127 | } | ||
128 | }, adaptorFactory); | ||
129 | } | ||
130 | return false; | ||
131 | }; | ||
132 | return ResultSet(generator); | ||
107 | } | 133 | } |
108 | 134 | ||
135 | //TODO generalize | ||
109 | KAsync::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &resultProvider, qint64 oldRevision, qint64 newRevision) | 136 | KAsync::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &resultProvider, qint64 oldRevision, qint64 newRevision) |
110 | { | 137 | { |
111 | return KAsync::start<qint64>([=]() { | 138 | return KAsync::start<qint64>([=]() { |
@@ -118,17 +145,14 @@ KAsync::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, cons | |||
118 | //TODO start transaction on indexes as well | 145 | //TODO start transaction on indexes as well |
119 | const qint64 revision = storage->maxRevision(); | 146 | const qint64 revision = storage->maxRevision(); |
120 | 147 | ||
121 | auto resultSet = getResultSet(query, storage); | 148 | auto resultSet = getResultSet(query, storage, mDomainTypeAdaptorFactory); |
122 | 149 | ||
123 | // TODO only emit changes and don't replace everything | 150 | // TODO only emit changes and don't replace everything |
124 | resultProvider->clear(); | 151 | resultProvider->clear(); |
125 | auto resultCallback = std::bind(&Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr>::add, resultProvider, std::placeholders::_1); | 152 | auto resultCallback = std::bind(&Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr>::add, resultProvider, std::placeholders::_1); |
126 | while (resultSet.next()) { | 153 | while(resultSet.next([resultCallback](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value) -> bool { |
127 | readValue(storage, resultSet.id(), [resultCallback](const Akonadi2::ApplicationDomain::Event::Ptr &event) { | 154 | resultCallback(Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation<Akonadi2::ApplicationDomain::Event>(value)); |
128 | //We create an in-memory copy because the result provider will store the value, and the result we get back is only valid during the callback | 155 | })){}; |
129 | resultCallback(Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation<Akonadi2::ApplicationDomain::Event>(event)); | ||
130 | }); | ||
131 | } | ||
132 | storage->abortTransaction(); | 156 | storage->abortTransaction(); |
133 | return revision; | 157 | return revision; |
134 | }); | 158 | }); |
diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp index fe04d99..4c27b10 100644 --- a/tests/dummyresourcetest.cpp +++ b/tests/dummyresourcetest.cpp | |||
@@ -124,6 +124,57 @@ private Q_SLOTS: | |||
124 | QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid")); | 124 | QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid")); |
125 | } | 125 | } |
126 | 126 | ||
127 | void testWriteToFacadeAndQueryByUid2() | ||
128 | { | ||
129 | Akonadi2::ApplicationDomain::Event event; | ||
130 | event.setProperty("summary", "summaryValue"); | ||
131 | |||
132 | event.setProperty("uid", "testuid"); | ||
133 | Akonadi2::Store::create<Akonadi2::ApplicationDomain::Event>(event, "org.kde.dummy.instance1"); | ||
134 | |||
135 | event.setProperty("uid", "testuid2"); | ||
136 | Akonadi2::Store::create<Akonadi2::ApplicationDomain::Event>(event, "org.kde.dummy.instance1"); | ||
137 | |||
138 | Akonadi2::Query query; | ||
139 | query.resources << "org.kde.dummy.instance1"; | ||
140 | query.syncOnDemand = false; | ||
141 | query.processAll = true; | ||
142 | |||
143 | query.propertyFilter.insert("uid", "testuid"); | ||
144 | async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Event>(query)); | ||
145 | result.exec(); | ||
146 | QCOMPARE(result.size(), 1); | ||
147 | auto value = result.first(); | ||
148 | qDebug() << value->getProperty("uid").toByteArray(); | ||
149 | QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid")); | ||
150 | } | ||
151 | |||
152 | void testWriteToFacadeAndQueryBySummary() | ||
153 | { | ||
154 | Akonadi2::ApplicationDomain::Event event; | ||
155 | |||
156 | event.setProperty("uid", "testuid"); | ||
157 | event.setProperty("summary", "summaryValue1"); | ||
158 | Akonadi2::Store::create<Akonadi2::ApplicationDomain::Event>(event, "org.kde.dummy.instance1"); | ||
159 | |||
160 | event.setProperty("uid", "testuid2"); | ||
161 | event.setProperty("summary", "summaryValue2"); | ||
162 | Akonadi2::Store::create<Akonadi2::ApplicationDomain::Event>(event, "org.kde.dummy.instance1"); | ||
163 | |||
164 | Akonadi2::Query query; | ||
165 | query.resources << "org.kde.dummy.instance1"; | ||
166 | query.syncOnDemand = false; | ||
167 | query.processAll = true; | ||
168 | |||
169 | query.propertyFilter.insert("summary", "summaryValue2"); | ||
170 | async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Event>(query)); | ||
171 | result.exec(); | ||
172 | QCOMPARE(result.size(), 1); | ||
173 | auto value = result.first(); | ||
174 | qDebug() << value->getProperty("uid").toByteArray(); | ||
175 | QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid2")); | ||
176 | } | ||
177 | |||
127 | void testResourceSync() | 178 | void testResourceSync() |
128 | { | 179 | { |
129 | Akonadi2::Pipeline pipeline("org.kde.dummy.instance1"); | 180 | Akonadi2::Pipeline pipeline("org.kde.dummy.instance1"); |