diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-10-15 13:56:45 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-10-15 13:56:45 +0200 |
commit | 35b28af1f449edb1bac0b0bda606c3c06b2fe102 (patch) | |
tree | 6237f0421afc8dc7adeb37dc823f7b34c30846a1 /common/entitystorage.cpp | |
parent | 2eb3810b66de3130f3e650627380c28a96acded7 (diff) | |
download | sink-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.cpp | 118 |
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 | ||
22 | static 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 | |||
47 | void 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 | |||
70 | static 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 | |||
90 | ResultSet EntityStorageBase::filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, bool initialQuery) | 22 | ResultSet 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 | ||
115 | ResultSet 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 | ||
129 | ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision) | 48 | ResultSet 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)) { |