diff options
-rw-r--r-- | common/clientapi.h | 10 | ||||
-rw-r--r-- | examples/dummyresource/facade.cpp | 96 | ||||
-rw-r--r-- | examples/dummyresource/facade.h | 2 |
3 files changed, 66 insertions, 42 deletions
diff --git a/common/clientapi.h b/common/clientapi.h index 05c4675..ee4ef3f 100644 --- a/common/clientapi.h +++ b/common/clientapi.h | |||
@@ -112,6 +112,14 @@ public: | |||
112 | { | 112 | { |
113 | } | 113 | } |
114 | 114 | ||
115 | template <typename DomainType> | ||
116 | static typename DomainType::Ptr getInMemoryRepresentation(const ApplicationDomainType::Ptr &domainType) | ||
117 | { | ||
118 | //TODO only copy requested properties | ||
119 | auto memoryAdaptor = QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create(*(domainType->mAdaptor)); | ||
120 | return QSharedPointer<DomainType>::create(domainType->mResourceName, domainType->mIdentifier, domainType->mRevision, memoryAdaptor); | ||
121 | } | ||
122 | |||
115 | virtual ~ApplicationDomainType() {} | 123 | virtual ~ApplicationDomainType() {} |
116 | 124 | ||
117 | virtual QVariant getProperty(const QByteArray &key) const { return mAdaptor->getProperty(key); } | 125 | virtual QVariant getProperty(const QByteArray &key) const { return mAdaptor->getProperty(key); } |
@@ -125,7 +133,7 @@ private: | |||
125 | /* | 133 | /* |
126 | * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. | 134 | * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. |
127 | */ | 135 | */ |
128 | QString mResourceName; | 136 | QByteArray mResourceName; |
129 | QByteArray mIdentifier; | 137 | QByteArray mIdentifier; |
130 | qint64 mRevision; | 138 | qint64 mRevision; |
131 | }; | 139 | }; |
diff --git a/examples/dummyresource/facade.cpp b/examples/dummyresource/facade.cpp index c0143c0..5e0bada 100644 --- a/examples/dummyresource/facade.cpp +++ b/examples/dummyresource/facade.cpp | |||
@@ -86,10 +86,9 @@ static std::function<bool(const std::string &key, DummyEvent const *buffer, Akon | |||
86 | return preparedQuery; | 86 | return preparedQuery; |
87 | } | 87 | } |
88 | 88 | ||
89 | void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)> preparedQuery) | 89 | static void scan(const QSharedPointer<Akonadi2::Storage> &storage, const QByteArray &key, std::function<bool(const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadata)> callback) |
90 | { | 90 | { |
91 | storage->scan(key, [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | 91 | storage->scan(key, [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { |
92 | |||
93 | //Skip internals | 92 | //Skip internals |
94 | if (Akonadi2::Storage::isInternalKey(keyValue, keySize)) { | 93 | if (Akonadi2::Storage::isInternalKey(keyValue, keySize)) { |
95 | return true; | 94 | return true; |
@@ -106,65 +105,82 @@ void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, c | |||
106 | qWarning() << "invalid buffer " << QByteArray::fromRawData(static_cast<char*>(keyValue), keySize); | 105 | qWarning() << "invalid buffer " << QByteArray::fromRawData(static_cast<char*>(keyValue), keySize); |
107 | return true; | 106 | return true; |
108 | } | 107 | } |
109 | 108 | return callback(QByteArray::fromRawData(static_cast<char*>(keyValue), keySize), buffer.entity(), resourceBuffer, localBuffer, metadataBuffer); | |
110 | //We probably only want to create all buffers after the scan | ||
111 | //TODO use adapter for query and scan? | ||
112 | if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer, localBuffer)) { | ||
113 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; | ||
114 | //This only works for a 1:1 mapping of resource to domain types. | ||
115 | //Not i.e. for tags that are stored as flags in each entity of an imap store. | ||
116 | auto adaptor = mDomainTypeAdaptorFactory->createAdaptor(buffer.entity()); | ||
117 | //TODO only copy requested properties | ||
118 | auto memoryAdaptor = QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create(*adaptor); | ||
119 | // here we could copy additional properties that don't have a 1:1 mapping, such as separately stored tags. | ||
120 | auto event = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("org.kde.dummy", QByteArray::fromRawData(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor); | ||
121 | resultCallback(event); | ||
122 | } | ||
123 | return true; | ||
124 | }, | 109 | }, |
125 | [](const Akonadi2::Storage::Error &error) { | 110 | [](const Akonadi2::Storage::Error &error) { |
126 | qWarning() << "Error during query: " << error.message; | 111 | qWarning() << "Error during query: " << error.message; |
127 | }); | 112 | }); |
128 | } | 113 | } |
129 | 114 | ||
115 | void DummyResourceFacade::readValue(const QSharedPointer<Akonadi2::Storage> &storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback) | ||
116 | { | ||
117 | scan(storage, key, [=](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { | ||
118 | qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; | ||
119 | //This only works for a 1:1 mapping of resource to domain types. | ||
120 | //Not i.e. for tags that are stored as flags in each entity of an imap store. | ||
121 | //additional properties that don't have a 1:1 mapping (such as separately stored tags), | ||
122 | //could be added to the adaptor | ||
123 | auto event = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("org.kde.dummy", key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)); | ||
124 | resultCallback(event); | ||
125 | return true; | ||
126 | }); | ||
127 | } | ||
128 | |||
129 | //TODO this should return an iterator to the result set so we can lazy load | ||
130 | static QVector<QByteArray> getResultSet(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::Storage> &storage) | ||
131 | { | ||
132 | //Now that the sync is complete we can execute the query | ||
133 | const auto preparedQuery = prepareQuery(query); | ||
134 | |||
135 | //Index lookups | ||
136 | //TODO query standard indexes | ||
137 | QVector<QByteArray> keys; | ||
138 | if (query.propertyFilter.contains("uid")) { | ||
139 | static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly); | ||
140 | uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) { | ||
141 | keys << value; | ||
142 | }, | ||
143 | [](const Index::Error &error) { | ||
144 | Warning() << "Error in index: " << error.message; | ||
145 | }); | ||
146 | } | ||
147 | |||
148 | //Scan for where we don't have an index | ||
149 | if (keys.isEmpty()) { | ||
150 | scan(storage, QByteArray(), [preparedQuery, &keys](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { | ||
151 | //TODO use adapter for query and scan? | ||
152 | if (preparedQuery && preparedQuery(std::string(key.constData(), key.size()), buffer, local)) { | ||
153 | keys << key; | ||
154 | } | ||
155 | return true; | ||
156 | }); | ||
157 | } | ||
158 | |||
159 | return keys; | ||
160 | } | ||
161 | |||
130 | KAsync::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &resultProvider, qint64 oldRevision, qint64 newRevision) | 162 | KAsync::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &resultProvider, qint64 oldRevision, qint64 newRevision) |
131 | { | 163 | { |
132 | return KAsync::start<qint64>([=]() { | 164 | return KAsync::start<qint64>([=]() { |
133 | //Now that the sync is complete we can execute the query | ||
134 | const auto preparedQuery = prepareQuery(query); | ||
135 | |||
136 | auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); | 165 | auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); |
137 | storage->setDefaultErrorHandler([](const Akonadi2::Storage::Error &error) { | 166 | storage->setDefaultErrorHandler([](const Akonadi2::Storage::Error &error) { |
138 | Warning() << "Error during query: " << error.store << error.message; | 167 | Warning() << "Error during query: " << error.store << error.message; |
139 | }); | 168 | }); |
140 | 169 | ||
141 | storage->startTransaction(Akonadi2::Storage::ReadOnly); | 170 | storage->startTransaction(Akonadi2::Storage::ReadOnly); |
171 | //TODO start transaction on indexes as well | ||
142 | const qint64 revision = storage->maxRevision(); | 172 | const qint64 revision = storage->maxRevision(); |
143 | 173 | ||
144 | //Index lookups | 174 | auto resultSet = getResultSet(query, storage); |
145 | QVector<QByteArray> keys; | ||
146 | if (query.propertyFilter.contains("uid")) { | ||
147 | static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly); | ||
148 | uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) { | ||
149 | keys << value; | ||
150 | }, | ||
151 | [](const Index::Error &error) { | ||
152 | Warning() << "Error in index: " << error.message; | ||
153 | }); | ||
154 | } | ||
155 | 175 | ||
156 | // TODO only emit changes and don't replace everything | 176 | // TODO only emit changes and don't replace everything |
157 | resultProvider->clear(); | 177 | resultProvider->clear(); |
158 | // rerun query | ||
159 | auto resultCallback = std::bind(&Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr>::add, resultProvider, std::placeholders::_1); | 178 | auto resultCallback = std::bind(&Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr>::add, resultProvider, std::placeholders::_1); |
160 | 179 | for (const auto &key : resultSet) { | |
161 | if (keys.isEmpty()) { | 180 | readValue(storage, key, [resultCallback](const Akonadi2::ApplicationDomain::Event::Ptr &event) { |
162 | Log() << "Executing a full scan"; | 181 | //We create an in-memory copy because the result provider will store the value, and the result we get back is only valid during the callback |
163 | readValue(storage, QByteArray(), resultCallback, preparedQuery); | 182 | resultCallback(Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation<Akonadi2::ApplicationDomain::Event>(event)); |
164 | } else { | 183 | }); |
165 | for (const auto &key : keys) { | ||
166 | readValue(storage, key, resultCallback, preparedQuery); | ||
167 | } | ||
168 | } | 184 | } |
169 | storage->abortTransaction(); | 185 | storage->abortTransaction(); |
170 | return revision; | 186 | return revision; |
diff --git a/examples/dummyresource/facade.h b/examples/dummyresource/facade.h index 41fb3f3..7c894f1 100644 --- a/examples/dummyresource/facade.h +++ b/examples/dummyresource/facade.h | |||
@@ -37,5 +37,5 @@ public: | |||
37 | KAsync::Job<qint64> load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &resultProvider, qint64 oldRevision, qint64 newRevision) Q_DECL_OVERRIDE; | 37 | KAsync::Job<qint64> load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &resultProvider, qint64 oldRevision, qint64 newRevision) Q_DECL_OVERRIDE; |
38 | 38 | ||
39 | private: | 39 | private: |
40 | void readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyCalendar::DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)>); | 40 | void readValue(const QSharedPointer<Akonadi2::Storage> &storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback); |
41 | }; | 41 | }; |