diff options
-rw-r--r-- | common/entitystorage.cpp | 118 | ||||
-rw-r--r-- | common/entitystorage.h | 63 | ||||
-rw-r--r-- | common/facade.h | 85 | ||||
-rw-r--r-- | tests/genericfacadebenchmark.cpp | 2 | ||||
-rw-r--r-- | tests/genericfacadetest.cpp | 8 |
5 files changed, 126 insertions, 150 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)) { |
diff --git a/common/entitystorage.h b/common/entitystorage.h index 6b09cad..8e73083 100644 --- a/common/entitystorage.h +++ b/common/entitystorage.h | |||
@@ -32,41 +32,44 @@ | |||
32 | /** | 32 | /** |
33 | * Wraps storage, entity adaptor factory and indexes into one. | 33 | * Wraps storage, entity adaptor factory and indexes into one. |
34 | * | 34 | * |
35 | * TODO: customize with readEntity instead of adaptor factory | ||
36 | */ | 35 | */ |
37 | class EntityStorageBase | 36 | class EntityStorageBase |
38 | { | 37 | { |
38 | public: | ||
39 | typedef std::function<ResultSet (const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters)> InitialResultLoader; | ||
40 | typedef std::function<ResultSet (qint64 baseRevision, const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters)> IncrementalResultLoader; | ||
41 | typedef std::function<void(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> &resultCallback)> EntityReader; | ||
42 | |||
43 | /** | ||
44 | * Returns the initial result set that still needs to be filtered. | ||
45 | * | ||
46 | * To make this efficient indexes should be chosen that are as selective as possible. | ||
47 | */ | ||
48 | InitialResultLoader loadInitialResultSet; | ||
49 | /** | ||
50 | * Returns the incremental result set that still needs to be filtered. | ||
51 | */ | ||
52 | IncrementalResultLoader loadIncrementalResultSet; | ||
53 | |||
54 | /** | ||
55 | * Loads a single entity by uid from storage. | ||
56 | */ | ||
57 | EntityReader readEntity; | ||
58 | |||
39 | protected: | 59 | protected: |
40 | EntityStorageBase(const QByteArray &instanceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory) | 60 | EntityStorageBase(const QByteArray &instanceIdentifier) |
41 | : mResourceInstanceIdentifier(instanceIdentifier), | 61 | : mResourceInstanceIdentifier(instanceIdentifier) |
42 | mDomainTypeAdaptorFactory(adaptorFactory) | ||
43 | { | 62 | { |
44 | 63 | ||
45 | } | 64 | } |
46 | 65 | ||
47 | virtual Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr create(const QByteArray &key, qint64 revision, const QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor> &adaptor) = 0; | ||
48 | virtual Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr copy(const Akonadi2::ApplicationDomain::ApplicationDomainType &) = 0; | 66 | virtual Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr copy(const Akonadi2::ApplicationDomain::ApplicationDomainType &) = 0; |
49 | virtual ResultSet queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Akonadi2::Storage::Transaction &transaction) = 0; | ||
50 | 67 | ||
51 | /** | ||
52 | * Loads a single entity by uid from storage. | ||
53 | * | ||
54 | * TODO: Resources should be able to customize this for cases where an entity is not the same as a single buffer. | ||
55 | */ | ||
56 | void readEntity(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> &resultCallback); | ||
57 | ResultSet getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision); | 68 | ResultSet getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision); |
58 | 69 | ||
59 | protected: | ||
60 | QByteArray mResourceInstanceIdentifier; | 70 | QByteArray mResourceInstanceIdentifier; |
61 | QByteArray mBufferType; | 71 | |
62 | DomainTypeAdaptorFactoryInterface::Ptr mDomainTypeAdaptorFactory; | ||
63 | private: | 72 | private: |
64 | /** | ||
65 | * Returns the initial result set that still needs to be filtered. | ||
66 | * | ||
67 | * To make this efficient indexes should be chosen that are as selective as possible. | ||
68 | */ | ||
69 | ResultSet loadInitialResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters); | ||
70 | ResultSet filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, bool isInitialQuery); | 73 | ResultSet filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, bool isInitialQuery); |
71 | }; | 74 | }; |
72 | 75 | ||
@@ -75,28 +78,18 @@ class EntityStorage : public EntityStorageBase | |||
75 | { | 78 | { |
76 | 79 | ||
77 | public: | 80 | public: |
78 | EntityStorage(const QByteArray &instanceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory, const QByteArray &bufferType) | ||
79 | : EntityStorageBase(instanceIdentifier, adaptorFactory) | ||
80 | { | ||
81 | mBufferType = bufferType; | ||
82 | } | ||
83 | 81 | ||
84 | protected: | 82 | EntityStorage(const QByteArray &instanceIdentifier) |
85 | Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr create(const QByteArray &key, qint64 revision, const QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor> &adaptor) Q_DECL_OVERRIDE | 83 | : EntityStorageBase(instanceIdentifier) |
86 | { | 84 | { |
87 | return DomainType::Ptr::create(mResourceInstanceIdentifier, key, revision, adaptor); | ||
88 | } | 85 | } |
89 | 86 | ||
87 | protected: | ||
90 | Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr copy(const Akonadi2::ApplicationDomain::ApplicationDomainType &object) Q_DECL_OVERRIDE | 88 | Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr copy(const Akonadi2::ApplicationDomain::ApplicationDomainType &object) Q_DECL_OVERRIDE |
91 | { | 89 | { |
92 | return Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation<DomainType>(object); | 90 | return Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation<DomainType>(object); |
93 | } | 91 | } |
94 | 92 | ||
95 | ResultSet queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Akonadi2::Storage::Transaction &transaction) Q_DECL_OVERRIDE | ||
96 | { | ||
97 | return Akonadi2::ApplicationDomain::TypeImplementation<DomainType>::queryIndexes(query, resourceInstanceIdentifier, appliedFilters, transaction); | ||
98 | } | ||
99 | |||
100 | public: | 93 | public: |
101 | 94 | ||
102 | virtual qint64 read(const Akonadi2::Query &query, qint64 baseRevision, const QSharedPointer<Akonadi2::ResultProvider<typename DomainType::Ptr> > &resultProvider) | 95 | virtual qint64 read(const Akonadi2::Query &query, qint64 baseRevision, const QSharedPointer<Akonadi2::ResultProvider<typename DomainType::Ptr> > &resultProvider) |
@@ -108,7 +101,7 @@ public: | |||
108 | 101 | ||
109 | auto transaction = storage.createTransaction(Akonadi2::Storage::ReadOnly); | 102 | auto transaction = storage.createTransaction(Akonadi2::Storage::ReadOnly); |
110 | 103 | ||
111 | Log() << "Querying" << baseRevision; | 104 | Log() << "Querying" << baseRevision << Akonadi2::Storage::maxRevision(transaction); |
112 | auto resultSet = getResultSet(query, transaction, baseRevision); | 105 | auto resultSet = getResultSet(query, transaction, baseRevision); |
113 | while(resultSet.next([this, resultProvider](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value, Akonadi2::Operation operation) -> bool { | 106 | while(resultSet.next([this, resultProvider](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value, Akonadi2::Operation operation) -> bool { |
114 | switch (operation) { | 107 | switch (operation) { |
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 | ||
85 | static 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 | |||
85 | namespace Akonadi2 { | 106 | namespace 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() |
diff --git a/tests/genericfacadebenchmark.cpp b/tests/genericfacadebenchmark.cpp index 3e98fce..10aabd4 100644 --- a/tests/genericfacadebenchmark.cpp +++ b/tests/genericfacadebenchmark.cpp | |||
@@ -53,7 +53,7 @@ private Q_SLOTS: | |||
53 | QBENCHMARK { | 53 | QBENCHMARK { |
54 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); | 54 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); |
55 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); | 55 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); |
56 | auto storage = QSharedPointer<EntityStorage<Akonadi2::ApplicationDomain::Event> >::create("identifier", domainTypeAdaptorFactory, "bufferType"); | 56 | auto storage = QSharedPointer<EntityStorage<Akonadi2::ApplicationDomain::Event> >::create("identifier"); |
57 | TestResourceFacade facade(identifier, storage, resourceAccess); | 57 | TestResourceFacade facade(identifier, storage, resourceAccess); |
58 | 58 | ||
59 | async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(resultSet->emitter()); | 59 | async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(resultSet->emitter()); |
diff --git a/tests/genericfacadetest.cpp b/tests/genericfacadetest.cpp index ae6a685..183a62a 100644 --- a/tests/genericfacadetest.cpp +++ b/tests/genericfacadetest.cpp | |||
@@ -29,7 +29,7 @@ private Q_SLOTS: | |||
29 | query.liveQuery = false; | 29 | query.liveQuery = false; |
30 | 30 | ||
31 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); | 31 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); |
32 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create(), "bufferType"); | 32 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier"); |
33 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); | 33 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); |
34 | storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create(); | 34 | storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create(); |
35 | TestResourceFacade facade("identifier", storage, resourceAccess); | 35 | TestResourceFacade facade("identifier", storage, resourceAccess); |
@@ -51,7 +51,7 @@ private Q_SLOTS: | |||
51 | query.liveQuery = true; | 51 | query.liveQuery = true; |
52 | 52 | ||
53 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); | 53 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); |
54 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create(), "bufferType"); | 54 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier"); |
55 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); | 55 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); |
56 | storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create(); | 56 | storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create(); |
57 | TestResourceFacade facade("identifier", storage, resourceAccess); | 57 | TestResourceFacade facade("identifier", storage, resourceAccess); |
@@ -83,7 +83,7 @@ private Q_SLOTS: | |||
83 | query.liveQuery = true; | 83 | query.liveQuery = true; |
84 | 84 | ||
85 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); | 85 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); |
86 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create(), "bufferType"); | 86 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier"); |
87 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); | 87 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); |
88 | auto entity = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("resource", "id2", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create()); | 88 | auto entity = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("resource", "id2", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create()); |
89 | entity->setProperty("test", "test1"); | 89 | entity->setProperty("test", "test1"); |
@@ -120,7 +120,7 @@ private Q_SLOTS: | |||
120 | query.liveQuery = true; | 120 | query.liveQuery = true; |
121 | 121 | ||
122 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); | 122 | auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); |
123 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create(), "bufferType"); | 123 | auto storage = QSharedPointer<TestEntityStorage>::create("identifier"); |
124 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); | 124 | auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); |
125 | auto entity = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("resource", "id2", 0, QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor>()); | 125 | auto entity = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("resource", "id2", 0, QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor>()); |
126 | storage->mResults << entity; | 126 | storage->mResults << entity; |