summaryrefslogtreecommitdiffstats
path: root/common/facade.h
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/facade.h
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/facade.h')
-rw-r--r--common/facade.h85
1 files changed, 84 insertions, 1 deletions
diff --git a/common/facade.h b/common/facade.h
index 38388c7..8b37579 100644
--- a/common/facade.h
+++ b/common/facade.h
@@ -82,6 +82,27 @@ private:
82 qint64 mLatestRevision; 82 qint64 mLatestRevision;
83}; 83};
84 84
85static ResultSet fullScan(const Akonadi2::Storage::Transaction &transaction, const QByteArray &bufferType)
86{
87 //TODO use a result set with an iterator, to read values on demand
88 QVector<QByteArray> keys;
89 transaction.openDatabase(bufferType + ".main").scan(QByteArray(), [&](const QByteArray &key, const QByteArray &value) -> bool {
90 //Skip internals
91 if (Akonadi2::Storage::isInternalKey(key)) {
92 return true;
93 }
94 keys << Akonadi2::Storage::uidFromKey(key);
95 return true;
96 },
97 [](const Akonadi2::Storage::Error &error) {
98 qWarning() << "Error during query: " << error.message;
99 });
100
101 Trace() << "Full scan found " << keys.size() << " results";
102 return ResultSet(keys);
103}
104
105
85namespace Akonadi2 { 106namespace Akonadi2 {
86/** 107/**
87 * Default facade implementation for resources that are implemented in a separate process using the ResourceAccess class. 108 * Default facade implementation for resources that are implemented in a separate process using the ResourceAccess class.
@@ -107,13 +128,75 @@ public:
107 GenericFacade(const QByteArray &resourceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory = DomainTypeAdaptorFactoryInterface::Ptr(), const QSharedPointer<EntityStorage<DomainType> > storage = QSharedPointer<EntityStorage<DomainType> >(), const QSharedPointer<Akonadi2::ResourceAccessInterface> resourceAccess = QSharedPointer<Akonadi2::ResourceAccessInterface>()) 128 GenericFacade(const QByteArray &resourceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory = DomainTypeAdaptorFactoryInterface::Ptr(), const QSharedPointer<EntityStorage<DomainType> > storage = QSharedPointer<EntityStorage<DomainType> >(), const QSharedPointer<Akonadi2::ResourceAccessInterface> resourceAccess = QSharedPointer<Akonadi2::ResourceAccessInterface>())
108 : Akonadi2::StoreFacade<DomainType>(), 129 : Akonadi2::StoreFacade<DomainType>(),
109 mResourceAccess(resourceAccess), 130 mResourceAccess(resourceAccess),
110 mStorage(storage ? storage : QSharedPointer<EntityStorage<DomainType> >::create(resourceIdentifier, adaptorFactory, bufferTypeForDomainType())), 131 mStorage(storage),
111 mDomainTypeAdaptorFactory(adaptorFactory), 132 mDomainTypeAdaptorFactory(adaptorFactory),
112 mResourceInstanceIdentifier(resourceIdentifier) 133 mResourceInstanceIdentifier(resourceIdentifier)
113 { 134 {
114 if (!mResourceAccess) { 135 if (!mResourceAccess) {
115 mResourceAccess = QSharedPointer<Akonadi2::ResourceAccess>::create(resourceIdentifier); 136 mResourceAccess = QSharedPointer<Akonadi2::ResourceAccess>::create(resourceIdentifier);
116 } 137 }
138 if (!mStorage) {
139 mStorage = QSharedPointer<EntityStorage<DomainType> >::create(resourceIdentifier);
140 const auto bufferType = bufferTypeForDomainType();
141
142 mStorage->readEntity = [bufferType, this] (const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> &resultCallback)
143 {
144 //This only works for a 1:1 mapping of resource to domain types.
145 //Not i.e. for tags that are stored as flags in each entity of an imap store.
146 //additional properties that don't have a 1:1 mapping (such as separately stored tags),
147 //could be added to the adaptor.
148 transaction.openDatabase(bufferType + ".main").findLatest(key, [=](const QByteArray &key, const QByteArray &value) -> bool {
149 Akonadi2::EntityBuffer buffer(value.data(), value.size());
150 const Akonadi2::Entity &entity = buffer.entity();
151 const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(entity.metadata());
152 Q_ASSERT(metadataBuffer);
153 const qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1;
154 resultCallback(DomainType::Ptr::create(mResourceInstanceIdentifier, Akonadi2::Storage::uidFromKey(key), revision, mDomainTypeAdaptorFactory->createAdaptor(entity)), metadataBuffer->operation());
155 return false;
156 },
157 [](const Akonadi2::Storage::Error &error) {
158 qWarning() << "Error during query: " << error.message;
159 });
160 };
161
162 mStorage->loadInitialResultSet = [bufferType, this] (const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) -> ResultSet
163 {
164 QSet<QByteArray> appliedFilters;
165 auto resultSet = Akonadi2::ApplicationDomain::TypeImplementation<DomainType>::queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, transaction);
166 remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters;
167
168 //We do a full scan if there were no indexes available to create the initial set.
169 if (appliedFilters.isEmpty()) {
170 //TODO this should be replaced by an index lookup as well
171 return fullScan(transaction, bufferType);
172 }
173 return resultSet;
174 };
175
176 mStorage->loadIncrementalResultSet = [bufferType, this] (qint64 baseRevision, const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) -> ResultSet
177 {
178 auto revisionCounter = QSharedPointer<qint64>::create(baseRevision);
179 return ResultSet([bufferType, revisionCounter, &transaction, this]() -> QByteArray {
180 const qint64 topRevision = Akonadi2::Storage::maxRevision(transaction);
181 //Spit out the revision keys one by one.
182 while (*revisionCounter <= topRevision) {
183 const auto uid = Akonadi2::Storage::getUidFromRevision(transaction, *revisionCounter);
184 const auto type = Akonadi2::Storage::getTypeFromRevision(transaction, *revisionCounter);
185 Trace() << "Revision" << *revisionCounter << type << uid;
186 if (type != bufferType) {
187 //Skip revision
188 *revisionCounter += 1;
189 continue;
190 }
191 const auto key = Akonadi2::Storage::assembleKey(uid, *revisionCounter);
192 *revisionCounter += 1;
193 return key;
194 }
195 //We're done
196 return QByteArray();
197 });
198 };
199 }
117 } 200 }
118 201
119 ~GenericFacade() 202 ~GenericFacade()