From 4eb94786232aee936cd6371824764705c9359538 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 17 Jun 2015 00:57:24 +0200 Subject: An almost generic query implementation. With equality filter on arbitrary properties as a bonus. --- common/resultset.h | 60 +++++++++++++++++++++++++++++++++++---- examples/dummyresource/facade.cpp | 48 +++++++++++++++++++++++-------- tests/dummyresourcetest.cpp | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 17 deletions(-) diff --git a/common/resultset.h b/common/resultset.h index f9b6cea..33f3c68 100644 --- a/common/resultset.h +++ b/common/resultset.h @@ -19,6 +19,8 @@ #pragma once #include +#include +#include "domain/applicationdomaintype.h" /* * An iterator to a result set. @@ -27,6 +29,22 @@ */ class ResultSet { public: + + + ResultSet(const std::function)> &generator) + : mValueGenerator(generator), + mIt(nullptr) + { + + } + + ResultSet(const std::function &generator) + : mGenerator(generator), + mIt(nullptr) + { + + } + ResultSet(const QVector &resultSet) : mResultSet(resultSet), mIt(nullptr) @@ -36,17 +54,46 @@ class ResultSet { bool next() { - if (!mIt) { - mIt = mResultSet.constBegin(); + if (mGenerator) { + mCurrentValue = mGenerator(); } else { - mIt++; + if (!mIt) { + mIt = mResultSet.constBegin(); + } else { + mIt++; + } + return mIt != mResultSet.constEnd(); } - return mIt != mResultSet.constEnd(); + } + + bool next(std::function callback) + { + Q_ASSERT(mValueGenerator); + return mValueGenerator(callback); + } + + bool next(std::function callback) + { + if (mGenerator) { + mCurrentValue = mGenerator(); + } else { + if (!mIt) { + mIt = mResultSet.constBegin(); + } else { + mIt++; + } + return mIt != mResultSet.constEnd(); + } + } QByteArray id() { - return *mIt; + if (mIt) { + return *mIt; + } else { + return mCurrentValue; + } } bool isEmpty() @@ -57,5 +104,8 @@ class ResultSet { private: QVector mResultSet; QVector::ConstIterator mIt; + QByteArray mCurrentValue; + std::function mGenerator; + std::function)> mValueGenerator; }; diff --git a/examples/dummyresource/facade.cpp b/examples/dummyresource/facade.cpp index 9d4f64b..002b836 100644 --- a/examples/dummyresource/facade.cpp +++ b/examples/dummyresource/facade.cpp @@ -74,7 +74,7 @@ static void scan(const QSharedPointer &storage, const QByteAr }); } -void DummyResourceFacade::readValue(const QSharedPointer &storage, const QByteArray &key, const std::function &resultCallback) +static void readValue(const QSharedPointer &storage, const QByteArray &key, const std::function &resultCallback, const QSharedPointer > &adaptorFactory) { scan(storage, key, [=](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; @@ -82,19 +82,21 @@ void DummyResourceFacade::readValue(const QSharedPointer &sto //Not i.e. for tags that are stored as flags in each entity of an imap store. //additional properties that don't have a 1:1 mapping (such as separately stored tags), //could be added to the adaptor - auto event = QSharedPointer::create("org.kde.dummy.instance1", key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)); + auto event = QSharedPointer::create("org.kde.dummy.instance1", key, revision, adaptorFactory->createAdaptor(entity)); resultCallback(event); return true; }); } -static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer &storage) +static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer &storage, const QSharedPointer > &adaptorFactory) { QSet appliedFilters; ResultSet resultSet = Akonadi2::ApplicationDomain::TypeImplementation::queryIndexes(query, "org.kde.dummy.instance1", appliedFilters); const auto remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; - if (resultSet.isEmpty()) { + //We do a full scan if there were no indexes available to create the initial set. + //TODO use a result set with an iterator, to read values on demand + if (appliedFilters.isEmpty()) { QVector keys; scan(storage, QByteArray(), [=, &keys](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { keys << key; @@ -103,9 +105,34 @@ static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer resultSet = ResultSet(keys); } - return resultSet; + auto filter = [remainingFilters, query](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &event) -> bool { + for (const auto &filterProperty : remainingFilters) { + //TODO implement other comparison operators than equality + if (event->getProperty(filterProperty) != query.propertyFilter.value(filterProperty)) { + return false; + } + } + return true; + }; + + auto resultSetPtr = QSharedPointer::create(resultSet); + + //Read through the source values and return whatever matches the filter + std::function)> generator = [resultSetPtr, storage, adaptorFactory, filter](std::function callback) -> bool { + while (resultSetPtr->next()) { + Akonadi2::ApplicationDomain::Event::Ptr event; + readValue(storage, resultSetPtr->id(), [filter, callback](const Akonadi2::ApplicationDomain::Event::Ptr &event) { + if (filter(event)) { + callback(event); + } + }, adaptorFactory); + } + return false; + }; + return ResultSet(generator); } +//TODO generalize KAsync::Job DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer > &resultProvider, qint64 oldRevision, qint64 newRevision) { return KAsync::start([=]() { @@ -118,17 +145,14 @@ KAsync::Job DummyResourceFacade::load(const Akonadi2::Query &query, cons //TODO start transaction on indexes as well const qint64 revision = storage->maxRevision(); - auto resultSet = getResultSet(query, storage); + auto resultSet = getResultSet(query, storage, mDomainTypeAdaptorFactory); // TODO only emit changes and don't replace everything resultProvider->clear(); auto resultCallback = std::bind(&Akonadi2::ResultProvider::add, resultProvider, std::placeholders::_1); - while (resultSet.next()) { - readValue(storage, resultSet.id(), [resultCallback](const Akonadi2::ApplicationDomain::Event::Ptr &event) { - //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 - resultCallback(Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation(event)); - }); - } + while(resultSet.next([resultCallback](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value) -> bool { + resultCallback(Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation(value)); + })){}; storage->abortTransaction(); return revision; }); diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp index fe04d99..4c27b10 100644 --- a/tests/dummyresourcetest.cpp +++ b/tests/dummyresourcetest.cpp @@ -124,6 +124,57 @@ private Q_SLOTS: QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid")); } + void testWriteToFacadeAndQueryByUid2() + { + Akonadi2::ApplicationDomain::Event event; + event.setProperty("summary", "summaryValue"); + + event.setProperty("uid", "testuid"); + Akonadi2::Store::create(event, "org.kde.dummy.instance1"); + + event.setProperty("uid", "testuid2"); + Akonadi2::Store::create(event, "org.kde.dummy.instance1"); + + Akonadi2::Query query; + query.resources << "org.kde.dummy.instance1"; + query.syncOnDemand = false; + query.processAll = true; + + query.propertyFilter.insert("uid", "testuid"); + async::SyncListResult result(Akonadi2::Store::load(query)); + result.exec(); + QCOMPARE(result.size(), 1); + auto value = result.first(); + qDebug() << value->getProperty("uid").toByteArray(); + QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid")); + } + + void testWriteToFacadeAndQueryBySummary() + { + Akonadi2::ApplicationDomain::Event event; + + event.setProperty("uid", "testuid"); + event.setProperty("summary", "summaryValue1"); + Akonadi2::Store::create(event, "org.kde.dummy.instance1"); + + event.setProperty("uid", "testuid2"); + event.setProperty("summary", "summaryValue2"); + Akonadi2::Store::create(event, "org.kde.dummy.instance1"); + + Akonadi2::Query query; + query.resources << "org.kde.dummy.instance1"; + query.syncOnDemand = false; + query.processAll = true; + + query.propertyFilter.insert("summary", "summaryValue2"); + async::SyncListResult result(Akonadi2::Store::load(query)); + result.exec(); + QCOMPARE(result.size(), 1); + auto value = result.first(); + qDebug() << value->getProperty("uid").toByteArray(); + QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid2")); + } + void testResourceSync() { Akonadi2::Pipeline pipeline("org.kde.dummy.instance1"); -- cgit v1.2.3