diff options
Diffstat (limited to 'dummyresource/facade.cpp')
-rw-r--r-- | dummyresource/facade.cpp | 145 |
1 files changed, 94 insertions, 51 deletions
diff --git a/dummyresource/facade.cpp b/dummyresource/facade.cpp index cd930f6..1aaec68 100644 --- a/dummyresource/facade.cpp +++ b/dummyresource/facade.cpp | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "createentity_generated.h" | 31 | #include "createentity_generated.h" |
32 | #include "domainadaptor.h" | 32 | #include "domainadaptor.h" |
33 | #include <common/entitybuffer.h> | 33 | #include <common/entitybuffer.h> |
34 | #include <common/index.h> | ||
34 | 35 | ||
35 | using namespace DummyCalendar; | 36 | using namespace DummyCalendar; |
36 | using namespace flatbuffers; | 37 | using namespace flatbuffers; |
@@ -73,11 +74,11 @@ Async::Job<void> DummyResourceFacade::remove(const Akonadi2::Domain::Event &doma | |||
73 | return Async::null<void>(); | 74 | return Async::null<void>(); |
74 | } | 75 | } |
75 | 76 | ||
76 | static std::function<bool(const std::string &key, DummyEvent const *buffer)> prepareQuery(const Akonadi2::Query &query) | 77 | static std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> prepareQuery(const Akonadi2::Query &query) |
77 | { | 78 | { |
78 | //Compose some functions to make query matching fast. | 79 | //Compose some functions to make query matching fast. |
79 | //This way we can process the query once, and convert all values into something that can be compared quickly | 80 | //This way we can process the query once, and convert all values into something that can be compared quickly |
80 | std::function<bool(const std::string &key, DummyEvent const *buffer)> preparedQuery; | 81 | std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> preparedQuery; |
81 | if (!query.ids.isEmpty()) { | 82 | if (!query.ids.isEmpty()) { |
82 | //Match by id | 83 | //Match by id |
83 | //TODO: for id's a direct lookup would be way faster | 84 | //TODO: for id's a direct lookup would be way faster |
@@ -88,15 +89,26 @@ static std::function<bool(const std::string &key, DummyEvent const *buffer)> pre | |||
88 | for (const auto &id : query.ids) { | 89 | for (const auto &id : query.ids) { |
89 | ids << id.toStdString(); | 90 | ids << id.toStdString(); |
90 | } | 91 | } |
91 | preparedQuery = [ids](const std::string &key, DummyEvent const *buffer) { | 92 | preparedQuery = [ids](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) { |
92 | if (ids.contains(key)) { | 93 | if (ids.contains(key)) { |
93 | return true; | 94 | return true; |
94 | } | 95 | } |
95 | return false; | 96 | return false; |
96 | }; | 97 | }; |
98 | } else if (!query.propertyFilter.isEmpty()) { | ||
99 | if (query.propertyFilter.contains("uid")) { | ||
100 | const QByteArray uid = query.propertyFilter.value("uid").toByteArray(); | ||
101 | preparedQuery = [uid](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) { | ||
102 | if (local && local->uid() && (QByteArray::fromRawData(local->uid()->c_str(), local->uid()->size()) == uid)) { | ||
103 | qDebug() << "uid match"; | ||
104 | return true; | ||
105 | } | ||
106 | return false; | ||
107 | }; | ||
108 | } | ||
97 | } else { | 109 | } else { |
98 | //Match everything | 110 | //Match everything |
99 | preparedQuery = [](const std::string &key, DummyEvent const *buffer) { | 111 | preparedQuery = [](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) { |
100 | return true; | 112 | return true; |
101 | }; | 113 | }; |
102 | } | 114 | } |
@@ -121,65 +133,96 @@ Async::Job<void> DummyResourceFacade::synchronizeResource(bool sync) | |||
121 | return Async::null<void>(); | 133 | return Async::null<void>(); |
122 | } | 134 | } |
123 | 135 | ||
124 | Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) | 136 | void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> preparedQuery) |
125 | { | 137 | { |
126 | qDebug() << "load called"; | 138 | storage->scan(key.data(), key.size(), [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { |
127 | 139 | ||
128 | return synchronizeResource(query.syncOnDemand).then<void>([=](Async::Future<void> &future) { | 140 | //Skip internals |
129 | qDebug() << "sync complete"; | 141 | if (QByteArray::fromRawData(static_cast<char*>(keyValue), keySize).startsWith("__internal")) { |
130 | //Now that the sync is complete we can execute the query | 142 | return true; |
131 | const auto preparedQuery = prepareQuery(query); | 143 | } |
132 | |||
133 | auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); | ||
134 | 144 | ||
135 | qDebug() << "executing query"; | 145 | //Extract buffers |
136 | //We start a transaction explicitly that we'll leave open so the values can be read. | 146 | Akonadi2::EntityBuffer buffer(dataValue, dataSize); |
137 | //The transaction will be closed automatically once the storage object is destroyed. | ||
138 | storage->startTransaction(Akonadi2::Storage::ReadOnly); | ||
139 | //Because we have no indexes yet, we always do a full scan | ||
140 | storage->scan("", [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | ||
141 | 147 | ||
142 | //Skip internals | 148 | DummyEvent const *resourceBuffer = 0; |
143 | if (QByteArray::fromRawData(static_cast<char*>(keyValue), keySize).startsWith("__internal")) { | 149 | if (auto resourceData = buffer.entity().resource()) { |
144 | return true; | 150 | flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size()); |
151 | if (VerifyDummyEventBuffer(verifyer)) { | ||
152 | resourceBuffer = GetDummyEvent(resourceData->Data()); | ||
145 | } | 153 | } |
154 | } | ||
146 | 155 | ||
147 | //Extract buffers | 156 | Akonadi2::Domain::Buffer::Event const *localBuffer = 0; |
148 | Akonadi2::EntityBuffer buffer(dataValue, dataSize); | 157 | if (auto localData = buffer.entity().local()) { |
149 | 158 | flatbuffers::Verifier verifyer(localData->Data(), localData->size()); | |
150 | DummyEvent const *resourceBuffer = 0; | 159 | if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) { |
151 | if (auto resourceData = buffer.entity().resource()) { | 160 | localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData->Data()); |
152 | flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size()); | ||
153 | if (VerifyDummyEventBuffer(verifyer)) { | ||
154 | resourceBuffer = GetDummyEvent(resourceData); | ||
155 | } | ||
156 | } | 161 | } |
162 | } | ||
157 | 163 | ||
158 | Akonadi2::Metadata const *metadataBuffer = 0; | 164 | Akonadi2::Metadata const *metadataBuffer = 0; |
159 | if (auto metadataData = buffer.entity().metadata()) { | 165 | if (auto metadataData = buffer.entity().metadata()) { |
160 | flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size()); | 166 | flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size()); |
161 | if (Akonadi2::VerifyMetadataBuffer(verifyer)) { | 167 | if (Akonadi2::VerifyMetadataBuffer(verifyer)) { |
162 | metadataBuffer = Akonadi2::GetMetadata(metadataData); | 168 | metadataBuffer = Akonadi2::GetMetadata(metadataData->Data()); |
163 | } | ||
164 | } | 169 | } |
170 | } | ||
165 | 171 | ||
166 | if (!resourceBuffer || !metadataBuffer) { | 172 | if (!resourceBuffer || !metadataBuffer) { |
167 | qWarning() << "invalid buffer " << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize)); | 173 | qWarning() << "invalid buffer " << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize)); |
168 | return true; | 174 | return true; |
169 | } | 175 | } |
176 | |||
177 | //We probably only want to create all buffers after the scan | ||
178 | //TODO use adapter for query and scan? | ||
179 | if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer, localBuffer)) { | ||
180 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; | ||
181 | //This only works for a 1:1 mapping of resource to domain types. | ||
182 | //Not i.e. for tags that are stored as flags in each entity of an imap store. | ||
183 | auto adaptor = mFactory->createAdaptor(buffer.entity()); | ||
184 | //TODO only copy requested properties | ||
185 | auto memoryAdaptor = QSharedPointer<Akonadi2::Domain::MemoryBufferAdaptor>::create(*adaptor); | ||
186 | auto event = QSharedPointer<Akonadi2::Domain::Event>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor); | ||
187 | resultCallback(event); | ||
188 | } | ||
189 | return true; | ||
190 | }, | ||
191 | [](const Akonadi2::Storage::Error &error) { | ||
192 | qDebug() << QString::fromStdString(error.message); | ||
193 | }); | ||
194 | } | ||
195 | |||
196 | Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) | ||
197 | { | ||
198 | return synchronizeResource(query.syncOnDemand).then<void>([=](Async::Future<void> &future) { | ||
199 | //Now that the sync is complete we can execute the query | ||
200 | const auto preparedQuery = prepareQuery(query); | ||
201 | |||
202 | auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); | ||
170 | 203 | ||
171 | //We probably only want to create all buffers after the scan | 204 | QVector<QByteArray> keys; |
172 | //TODO use adapter for query and scan? | 205 | if (query.propertyFilter.contains("uid")) { |
173 | if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer)) { | 206 | static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly); |
174 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; | 207 | uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) { |
175 | auto adaptor = mFactory->createAdaptor(buffer.entity()); | 208 | keys << value; |
176 | //TODO only copy requested properties | 209 | }, |
177 | auto memoryAdaptor = QSharedPointer<Akonadi2::Domain::MemoryBufferAdaptor>::create(*adaptor); | 210 | [](const Index::Error &error) { |
178 | auto event = QSharedPointer<Akonadi2::Domain::Event>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor); | 211 | qWarning() << "Error in index: " << QString::fromStdString(error.message); |
179 | resultCallback(event); | 212 | }); |
213 | } | ||
214 | |||
215 | //We start a transaction explicitly that we'll leave open so the values can be read. | ||
216 | //The transaction will be closed automatically once the storage object is destroyed. | ||
217 | storage->startTransaction(Akonadi2::Storage::ReadOnly); | ||
218 | if (keys.isEmpty()) { | ||
219 | qDebug() << "full scan"; | ||
220 | readValue(storage, QByteArray(), resultCallback, preparedQuery); | ||
221 | } else { | ||
222 | for (const auto &key : keys) { | ||
223 | readValue(storage, key, resultCallback, preparedQuery); | ||
180 | } | 224 | } |
181 | return true; | 225 | } |
182 | }); | ||
183 | future.setFinished(); | 226 | future.setFinished(); |
184 | }); | 227 | }); |
185 | } | 228 | } |