summaryrefslogtreecommitdiffstats
path: root/common/entitystorage.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2015-10-15 13:56:45 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2015-10-15 13:56:45 +0200
commit35b28af1f449edb1bac0b0bda606c3c06b2fe102 (patch)
tree6237f0421afc8dc7adeb37dc823f7b34c30846a1 /common/entitystorage.cpp
parent2eb3810b66de3130f3e650627380c28a96acded7 (diff)
downloadsink-35b28af1f449edb1bac0b0bda606c3c06b2fe102.tar.gz
sink-35b28af1f449edb1bac0b0bda606c3c06b2fe102.zip
Moved more entitystorage functionality back to facade.
To avoid unnecessary abstraction layers that don't solve a problem, and to allow facades to customize how entities are loaded.
Diffstat (limited to 'common/entitystorage.cpp')
-rw-r--r--common/entitystorage.cpp118
1 files changed, 9 insertions, 109 deletions
diff --git a/common/entitystorage.cpp b/common/entitystorage.cpp
index e5346f4..c77e408 100644
--- a/common/entitystorage.cpp
+++ b/common/entitystorage.cpp
@@ -19,74 +19,6 @@
19 19
20#include "entitystorage.h" 20#include "entitystorage.h"
21 21
22static void scan(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, std::function<bool(const QByteArray &key, const Akonadi2::Entity &entity)> callback, const QByteArray &bufferType)
23{
24
25 transaction.openDatabase(bufferType + ".main").findLatest(key, [=](const QByteArray &key, const QByteArray &value) -> bool {
26 //Extract buffers
27 Akonadi2::EntityBuffer buffer(value.data(), value.size());
28
29 //FIXME implement buffer.isValid()
30 // const auto resourceBuffer = Akonadi2::EntityBuffer::readBuffer<DummyEvent>(buffer.entity().resource());
31 // const auto localBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::ApplicationDomain::Buffer::Event>(buffer.entity().local());
32 // const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(buffer.entity().metadata());
33
34 // if ((!resourceBuffer && !localBuffer) || !metadataBuffer) {
35 // qWarning() << "invalid buffer " << QByteArray::fromRawData(static_cast<char*>(keyValue), keySize);
36 // return true;
37 // }
38 //
39 //We're cutting the revision off the key
40 return callback(Akonadi2::Storage::uidFromKey(key), buffer.entity());
41 },
42 [](const Akonadi2::Storage::Error &error) {
43 qWarning() << "Error during query: " << error.message;
44 });
45}
46
47void EntityStorageBase::readEntity(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> &resultCallback)
48{
49 //This only works for a 1:1 mapping of resource to domain types.
50 //Not i.e. for tags that are stored as flags in each entity of an imap store.
51 //additional properties that don't have a 1:1 mapping (such as separately stored tags),
52 //could be added to the adaptor.
53 //TODO: resource implementations should be able to customize the retrieval function for non 1:1 entity-buffer mapping cases
54 scan(transaction, key, [=](const QByteArray &key, const Akonadi2::Entity &entity) {
55 const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(entity.metadata());
56 Q_ASSERT(metadataBuffer);
57 qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1;
58 auto operation = metadataBuffer->operation();
59
60 auto domainObject = create(key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity));
61 if (operation == Akonadi2::Operation_Removal) {
62 resultCallback(create(key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)), operation);
63 } else {
64 resultCallback(create(key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)), operation);
65 }
66 return false;
67 }, mBufferType);
68}
69
70static ResultSet fullScan(const Akonadi2::Storage::Transaction &transaction, const QByteArray &bufferType)
71{
72 //TODO use a result set with an iterator, to read values on demand
73 QVector<QByteArray> keys;
74 transaction.openDatabase(bufferType + ".main").scan(QByteArray(), [&](const QByteArray &key, const QByteArray &value) -> bool {
75 //Skip internals
76 if (Akonadi2::Storage::isInternalKey(key)) {
77 return true;
78 }
79 keys << Akonadi2::Storage::uidFromKey(key);
80 return true;
81 },
82 [](const Akonadi2::Storage::Error &error) {
83 qWarning() << "Error during query: " << error.message;
84 });
85
86 Trace() << "Full scan found " << keys.size() << " results";
87 return ResultSet(keys);
88}
89
90ResultSet EntityStorageBase::filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, bool initialQuery) 22ResultSet EntityStorageBase::filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, bool initialQuery)
91{ 23{
92 auto resultSetPtr = QSharedPointer<ResultSet>::create(resultSet); 24 auto resultSetPtr = QSharedPointer<ResultSet>::create(resultSet);
@@ -112,23 +44,9 @@ ResultSet EntityStorageBase::filteredSet(const ResultSet &resultSet, const std::
112 return ResultSet(generator); 44 return ResultSet(generator);
113} 45}
114 46
115ResultSet EntityStorageBase::loadInitialResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters)
116{
117 QSet<QByteArray> appliedFilters;
118 auto resultSet = queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, transaction);
119 remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters;
120
121 //We do a full scan if there were no indexes available to create the initial set.
122 if (appliedFilters.isEmpty()) {
123 //TODO this should be replaced by an index lookup as well
124 return fullScan(transaction, mBufferType);
125 }
126 return resultSet;
127}
128 47
129ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision) 48ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision)
130{ 49{
131 const qint64 topRevision = Akonadi2::Storage::maxRevision(transaction);
132 QSet<QByteArray> remainingFilters = query.propertyFilter.keys().toSet(); 50 QSet<QByteArray> remainingFilters = query.propertyFilter.keys().toSet();
133 ResultSet resultSet; 51 ResultSet resultSet;
134 const bool initialQuery = (baseRevision == 1); 52 const bool initialQuery = (baseRevision == 1);
@@ -137,35 +55,17 @@ ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2
137 resultSet = loadInitialResultSet(query, transaction, remainingFilters); 55 resultSet = loadInitialResultSet(query, transaction, remainingFilters);
138 } else { 56 } else {
139 //TODO fallback in case the old revision is no longer available to clear + redo complete initial scan 57 //TODO fallback in case the old revision is no longer available to clear + redo complete initial scan
140 Trace() << "Incremental result set update" << baseRevision << topRevision; 58 Trace() << "Incremental result set update" << baseRevision;
141 auto revisionCounter = QSharedPointer<qint64>::create(baseRevision); 59 resultSet = loadIncrementalResultSet(baseRevision, query, transaction, remainingFilters);
142 resultSet = ResultSet([revisionCounter, topRevision, &transaction, this]() -> QByteArray {
143 //Spit out the revision keys one by one.
144 while (*revisionCounter <= topRevision) {
145 const auto uid = Akonadi2::Storage::getUidFromRevision(transaction, *revisionCounter);
146 const auto type = Akonadi2::Storage::getTypeFromRevision(transaction, *revisionCounter);
147 Trace() << "Revision" << *revisionCounter << type << uid;
148 if (type != mBufferType) {
149 //Skip revision
150 *revisionCounter += 1;
151 continue;
152 }
153 const auto key = Akonadi2::Storage::assembleKey(uid, *revisionCounter);
154 *revisionCounter += 1;
155 return key;
156 }
157 //We're done
158 return QByteArray();
159 });
160 } 60 }
161 61
162 auto filter = [remainingFilters, query, baseRevision, topRevision](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject) -> bool { 62 auto filter = [remainingFilters, query, baseRevision](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject) -> bool {
163 if (topRevision > 0) { 63 // if (topRevision > 0) {
164 Trace() << "filtering by revision " << domainObject->revision(); 64 // Trace() << "filtering by revision " << domainObject->revision();
165 if (domainObject->revision() < baseRevision || domainObject->revision() > topRevision) { 65 // if (domainObject->revision() < baseRevision) {
166 return false; 66 // return false;
167 } 67 // }
168 } 68 // }
169 for (const auto &filterProperty : remainingFilters) { 69 for (const auto &filterProperty : remainingFilters) {
170 //TODO implement other comparison operators than equality 70 //TODO implement other comparison operators than equality
171 if (domainObject->getProperty(filterProperty) != query.propertyFilter.value(filterProperty)) { 71 if (domainObject->getProperty(filterProperty) != query.propertyFilter.value(filterProperty)) {