From 529dd17eec62a9702b8837f8f1976dfbf28fdd82 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 16 Feb 2016 17:43:22 +0100 Subject: Prepared sort indexes --- common/domain/event.cpp | 4 +- common/domain/event.h | 2 +- common/domain/folder.cpp | 4 +- common/domain/folder.h | 2 +- common/domain/mail.cpp | 4 +- common/domain/mail.h | 2 +- common/domainadaptor.h | 2 +- common/index.cpp | 6 +-- common/index.h | 2 +- common/modelresult.cpp | 20 ++++++---- common/pipeline.cpp | 1 + common/queryrunner.cpp | 32 ++++++++++------ common/typeindex.cpp | 95 +++++++++++++++++++++++++++++++++++++----------- common/typeindex.h | 7 +++- 14 files changed, 128 insertions(+), 55 deletions(-) (limited to 'common') diff --git a/common/domain/event.cpp b/common/domain/event.cpp index 4210125..96e2d44 100644 --- a/common/domain/event.cpp +++ b/common/domain/event.cpp @@ -50,9 +50,9 @@ static TypeIndex &getIndex() return *index; } -ResultSet TypeImplementation::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, Sink::Storage::Transaction &transaction) +ResultSet TypeImplementation::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction) { - return getIndex().query(query, appliedFilters, transaction); + return getIndex().query(query, appliedFilters, appliedSorting, transaction); } void TypeImplementation::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) diff --git a/common/domain/event.h b/common/domain/event.h index 479969d..5315566 100644 --- a/common/domain/event.h +++ b/common/domain/event.h @@ -55,7 +55,7 @@ public: * * An empty result set indicates that a full scan is required. */ - static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, Sink::Storage::Transaction &transaction); + static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction); static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); static QSharedPointer > initializeReadPropertyMapper(); diff --git a/common/domain/folder.cpp b/common/domain/folder.cpp index 16b2ec9..1cb9217 100644 --- a/common/domain/folder.cpp +++ b/common/domain/folder.cpp @@ -51,9 +51,9 @@ static TypeIndex &getIndex() return *index; } -ResultSet TypeImplementation::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, Sink::Storage::Transaction &transaction) +ResultSet TypeImplementation::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction) { - return getIndex().query(query, appliedFilters, transaction); + return getIndex().query(query, appliedFilters, appliedSorting, transaction); } void TypeImplementation::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) diff --git a/common/domain/folder.h b/common/domain/folder.h index 40c799c..6e066e1 100644 --- a/common/domain/folder.h +++ b/common/domain/folder.h @@ -45,7 +45,7 @@ public: typedef Sink::ApplicationDomain::Buffer::Folder Buffer; typedef Sink::ApplicationDomain::Buffer::FolderBuilder BufferBuilder; static QSet indexedProperties(); - static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, Sink::Storage::Transaction &transaction); + static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction); static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); static QSharedPointer > initializeReadPropertyMapper(); diff --git a/common/domain/mail.cpp b/common/domain/mail.cpp index 518331f..69c351c 100644 --- a/common/domain/mail.cpp +++ b/common/domain/mail.cpp @@ -55,9 +55,9 @@ static TypeIndex &getIndex() return *index; } -ResultSet TypeImplementation::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, Sink::Storage::Transaction &transaction) +ResultSet TypeImplementation::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction) { - return getIndex().query(query, appliedFilters, transaction); + return getIndex().query(query, appliedFilters, appliedSorting, transaction); } void TypeImplementation::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) diff --git a/common/domain/mail.h b/common/domain/mail.h index e45d64d..ff169dd 100644 --- a/common/domain/mail.h +++ b/common/domain/mail.h @@ -50,7 +50,7 @@ public: * * An empty result set indicates that a full scan is required. */ - static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, Sink::Storage::Transaction &transaction); + static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction); static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); static QSharedPointer > initializeReadPropertyMapper(); diff --git a/common/domainadaptor.h b/common/domainadaptor.h index 893c8d0..9ce5a8a 100644 --- a/common/domainadaptor.h +++ b/common/domainadaptor.h @@ -101,7 +101,7 @@ public: } else if (mLocalBuffer && mLocalMapper->hasMapping(key)) { return mLocalMapper->getProperty(key, mLocalBuffer); } - Warning() << "No mapping available for key " << key; + Warning() << "No mapping available for key " << key << mLocalBuffer << mResourceBuffer; return QVariant(); } diff --git a/common/index.cpp b/common/index.cpp index ae56da1..e35b838 100644 --- a/common/index.cpp +++ b/common/index.cpp @@ -31,7 +31,7 @@ void Index::remove(const QByteArray &key, const QByteArray &value) } void Index::lookup(const QByteArray &key, const std::function &resultHandler, - const std::function &errorHandler) + const std::function &errorHandler, bool matchSubStringKeys) { mDb.scan(key, [this, resultHandler](const QByteArray &key, const QByteArray &value) -> bool { resultHandler(value); @@ -40,8 +40,8 @@ void Index::lookup(const QByteArray &key, const std::function &resultHandler, - const std::function &errorHandler); + const std::function &errorHandler, bool matchSubStringKeys = false); QByteArray lookup(const QByteArray &key); private: diff --git a/common/modelresult.cpp b/common/modelresult.cpp index f28c665..5b5a473 100644 --- a/common/modelresult.cpp +++ b/common/modelresult.cpp @@ -27,9 +27,9 @@ #undef DEBUG_AREA #define DEBUG_AREA "client.modelresult" -static uint qHash(const Sink::ApplicationDomain::ApplicationDomainType &type) +static uint qHash(const Sink::ApplicationDomain::ApplicationDomainType &type) { - Q_ASSERT(!type.resourceInstanceIdentifier().isEmpty()); + // Q_ASSERT(!type.resourceInstanceIdentifier().isEmpty()); Q_ASSERT(!type.identifier().isEmpty()); return qHash(type.resourceInstanceIdentifier() + type.identifier()); } @@ -88,18 +88,18 @@ QVariant ModelResult::headerData(int section, Qt::Orientation orientatio template QVariant ModelResult::data(const QModelIndex &index, int role) const { - if (role == DomainObjectRole) { + if (role == DomainObjectRole && index.isValid()) { Q_ASSERT(mEntities.contains(index.internalId())); return QVariant::fromValue(mEntities.value(index.internalId())); } - if (role == DomainObjectBaseRole) { + if (role == DomainObjectBaseRole && index.isValid()) { Q_ASSERT(mEntities.contains(index.internalId())); return QVariant::fromValue(mEntities.value(index.internalId()). template staticCast()); } if (role == ChildrenFetchedRole) { return childrenFetched(index); } - if (role == Qt::DisplayRole) { + if (role == Qt::DisplayRole && index.isValid()) { if (index.column() < mPropertyColumns.size()) { Q_ASSERT(mEntities.contains(index.internalId())); auto entity = mEntities.value(index.internalId()); @@ -115,8 +115,13 @@ template QModelIndex ModelResult::index(int row, int column, const QModelIndex &parent) const { const auto id = getIdentifier(parent); - const auto childId = mTree.value(id).at(row); - return createIndex(row, column, childId); + const auto list = mTree.value(id); + if (list.size() > row) { + const auto childId = list.at(row); + return createIndex(row, column, childId); + } + Warning() << "Index not available " << row << column << parent; + return QModelIndex(); } template @@ -156,6 +161,7 @@ bool ModelResult::canFetchMore(const QModelIndex &parent) const template void ModelResult::fetchMore(const QModelIndex &parent) { + Trace() << "Fetching more: " << parent; fetchEntities(parent); } diff --git a/common/pipeline.cpp b/common/pipeline.cpp index 93d8236..35e582b 100644 --- a/common/pipeline.cpp +++ b/common/pipeline.cpp @@ -209,6 +209,7 @@ KAsync::Job Pipeline::newEntity(void const *command, size_t size) Log() << "Pipeline: wrote entity: " << key << newRevision << bufferType; Storage::mainDatabase(d->transaction, bufferType).scan(Storage::assembleKey(key, newRevision), [this, bufferType, newRevision, adaptorFactory, key](const QByteArray &, const QByteArray &value) -> bool { auto entity = GetEntity(value); + Q_ASSERT(entity->resource() || entity->local()); auto adaptor = adaptorFactory->createAdaptor(*entity); for (auto processor : d->processors[bufferType]) { processor->newEntity(key, newRevision, *adaptor, d->transaction); diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp index 2d188ae..299b987 100644 --- a/common/queryrunner.cpp +++ b/common/queryrunner.cpp @@ -54,12 +54,12 @@ private: void readEntity(const Sink::Storage::NamedDatabase &db, const QByteArray &key, const std::function &resultCallback); - ResultSet loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet &remainingFilters); + ResultSet loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet &remainingFilters, QByteArray &remainingSorting); ResultSet loadIncrementalResultSet(qint64 baseRevision, const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet &remainingFilters); ResultSet filterAndSortSet(ResultSet &resultSet, const std::function &filter, const Sink::Storage::NamedDatabase &db, bool initialQuery, const QByteArray &sortProperty); std::function getFilter(const QSet remainingFilters, const Sink::Query &query); - qint64 load(const Sink::Query &query, const std::function &)> &baseSetRetriever, Sink::ResultProviderInterface &resultProvider, bool initialQuery, int offset, int batchSize); + qint64 load(const Sink::Query &query, const std::function &, QByteArray &)> &baseSetRetriever, Sink::ResultProviderInterface &resultProvider, bool initialQuery, int offset, int batchSize); private: QueryRunnerBase::ResultTransformation mResultTransformation; @@ -154,6 +154,7 @@ static inline ResultSet fullScan(const Sink::Storage::Transaction &transaction, Warning() << "Error during query: " << error.message; }); + Trace() << "Full scan retrieved " << keys.size() << " results."; return ResultSet(keys); } @@ -207,7 +208,7 @@ void QueryWorker::replaySet(ResultSet &resultSet, Sink::ResultProvid } return true; })){}; - Trace() << "Replayed " << counter << " results"; + Trace() << "Replayed " << counter << " results." << "Limit " << batchSize; } template @@ -234,14 +235,18 @@ void QueryWorker::readEntity(const Sink::Storage::NamedDatabase &db, } template -ResultSet QueryWorker::loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet &remainingFilters) +ResultSet QueryWorker::loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet &remainingFilters, QByteArray &remainingSorting) { if (!query.ids.isEmpty()) { return ResultSet(query.ids.toVector()); } QSet appliedFilters; - auto resultSet = Sink::ApplicationDomain::TypeImplementation::queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, transaction); + QByteArray appliedSorting; + auto resultSet = Sink::ApplicationDomain::TypeImplementation::queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, appliedSorting, transaction); remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; + if (appliedSorting.isEmpty()) { + remainingSorting = query.sortProperty; + } //We do a full scan if there were no indexes available to create the initial set. if (appliedFilters.isEmpty()) { @@ -284,6 +289,7 @@ ResultSet QueryWorker::filterAndSortSet(ResultSet &resultSet, const { const bool sortingRequired = !sortProperty.isEmpty(); if (initialQuery && sortingRequired) { + Trace() << "Sorting the resultset in memory according to property: " << sortProperty; //Sort the complete set by reading the sort property and filling into a sorted map auto sortedMap = QSharedPointer>::create(); while (resultSet.next()) { @@ -293,7 +299,7 @@ ResultSet QueryWorker::filterAndSortSet(ResultSet &resultSet, const if ((operation != Sink::Operation_Removal) && filter(domainObject)) { if (!sortProperty.isEmpty()) { const auto sortValue = domainObject->getProperty(sortProperty); - if (sortValue.canConvert()) { + if (sortValue.type() == QVariant::DateTime) { sortedMap->insert(QByteArray::number(std::numeric_limits::max() - sortValue.toDateTime().toTime_t()), domainObject->identifier()); } else { sortedMap->insert(sortValue.toString().toLatin1(), domainObject->identifier()); @@ -305,6 +311,7 @@ ResultSet QueryWorker::filterAndSortSet(ResultSet &resultSet, const }); } + Trace() << "Sorted " << sortedMap->size() << " values."; auto iterator = QSharedPointer >::create(*sortedMap); ResultSet::ValueGenerator generator = [this, iterator, sortedMap, &db, filter, initialQuery](std::function callback) -> bool { if (iterator->hasNext()) { @@ -377,7 +384,7 @@ std::function -qint64 QueryWorker::load(const Sink::Query &query, const std::function &)> &baseSetRetriever, Sink::ResultProviderInterface &resultProvider, bool initialQuery, int offset, int batchSize) +qint64 QueryWorker::load(const Sink::Query &query, const std::function &, QByteArray &)> &baseSetRetriever, Sink::ResultProviderInterface &resultProvider, bool initialQuery, int offset, int batchSize) { QTime time; time.start(); @@ -390,9 +397,10 @@ qint64 QueryWorker::load(const Sink::Query &query, const std::functi auto db = Storage::mainDatabase(transaction, mBufferType); QSet remainingFilters; - auto resultSet = baseSetRetriever(transaction, remainingFilters); + QByteArray remainingSorting; + auto resultSet = baseSetRetriever(transaction, remainingFilters, remainingSorting); Trace() << "Base set retrieved. " << time.elapsed(); - auto filteredSet = filterAndSortSet(resultSet, getFilter(remainingFilters, query), db, initialQuery, query.sortProperty); + auto filteredSet = filterAndSortSet(resultSet, getFilter(remainingFilters, query), db, initialQuery, remainingSorting); Trace() << "Filtered set retrieved. " << time.elapsed(); replaySet(filteredSet, resultProvider, query.requestedProperties, offset, batchSize); Trace() << "Filtered set replayed. " << time.elapsed(); @@ -408,7 +416,7 @@ qint64 QueryWorker::executeIncrementalQuery(const Sink::Query &query const qint64 baseRevision = resultProvider.revision() + 1; Trace() << "Running incremental query " << baseRevision; - auto revision = load(query, [&](Sink::Storage::Transaction &transaction, QSet &remainingFilters) -> ResultSet { + auto revision = load(query, [&](Sink::Storage::Transaction &transaction, QSet &remainingFilters, QByteArray &remainingSorting) -> ResultSet { return loadIncrementalResultSet(baseRevision, query, transaction, remainingFilters); }, resultProvider, false, 0, 0); Trace() << "Incremental query took: " << time.elapsed() << " ms"; @@ -431,8 +439,8 @@ qint64 QueryWorker::executeInitialQuery(const Sink::Query &query, co modifiedQuery.propertyFilter.insert(query.parentProperty, QVariant()); } } - auto revision = load(modifiedQuery, [&](Sink::Storage::Transaction &transaction, QSet &remainingFilters) -> ResultSet { - return loadInitialResultSet(modifiedQuery, transaction, remainingFilters); + auto revision = load(modifiedQuery, [&](Sink::Storage::Transaction &transaction, QSet &remainingFilters, QByteArray &remainingSorting) -> ResultSet { + return loadInitialResultSet(modifiedQuery, transaction, remainingFilters, remainingSorting); }, resultProvider, true, offset, batchsize); Trace() << "Initial query took: " << time.elapsed() << " ms"; resultProvider.initialResultSetComplete(parent); diff --git a/common/typeindex.cpp b/common/typeindex.cpp index 37c0517..e945dcb 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp @@ -25,22 +25,41 @@ #undef DEBUG_AREA #define DEBUG_AREA "common.typeindex" +static QByteArray getByteArray(const QVariant &value) +{ + if (value.isValid() && !value.toByteArray().isEmpty()) { + return value.toByteArray(); + } + //LMDB can't handle empty keys, so use something different + return "toplevel"; +} + +static QByteArray toSortableByteArray(const QDateTime &date) +{ + return QByteArray::number(std::numeric_limits::max() - date.toTime_t()); +} + + TypeIndex::TypeIndex(const QByteArray &type) : mType(type) { } +QByteArray TypeIndex::indexName(const QByteArray &property, const QByteArray &sortProperty) const +{ + if (sortProperty.isEmpty()) { + return mType + ".index." + property; + } + return mType + ".index." + property + ".sort." + sortProperty; +} + template<> void TypeIndex::addProperty(const QByteArray &property) { auto indexer = [this, property](const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction) { // Trace() << "Indexing " << mType + ".index." + property << value.toByteArray(); - if (value.isValid() && !value.toByteArray().isEmpty()) { - Index(mType + ".index." + property, transaction).add(value.toByteArray(), identifier); - } else { - Index(mType + ".index." + property, transaction).add("toplevel", identifier); - } + Index(indexName(property), transaction).add(getByteArray(value), identifier); }; mIndexer.insert(property, indexer); mProperties << property; @@ -51,11 +70,7 @@ void TypeIndex::addProperty(const QByteArray &property) { auto indexer = [this, property](const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction) { // Trace() << "Indexing " << mType + ".index." + property << value.toByteArray(); - if (value.isValid() && !value.toByteArray().isEmpty()) { - Index(mType + ".index." + property, transaction).add(value.toByteArray(), identifier); - } else { - Index(mType + ".index." + property, transaction).add("toplevel", identifier); - } + Index(indexName(property), transaction).add(getByteArray(value), identifier); }; mIndexer.insert(property, indexer); mProperties << property; @@ -68,13 +83,25 @@ void TypeIndex::addProperty(const QByteArray &property) const auto date = value.toDateTime(); // Trace() << "Indexing " << mType + ".index." + property << date.toString(); if (date.isValid()) { - Index(mType + ".index." + property, transaction).add(date.toString().toLatin1(), identifier); + Index(indexName(property), transaction).add(date.toString().toLatin1(), identifier); } }; mIndexer.insert(property, indexer); mProperties << property; } +template<> +void TypeIndex::addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty) +{ + auto indexer = [=](const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::Transaction &transaction) { + const auto date = sortValue.toDateTime(); + const auto propertyValue = getByteArray(value); + Index(indexName(property, sortProperty), transaction).add(propertyValue + toSortableByteArray(date), identifier); + }; + mSortIndexer.insert(property+sortProperty, indexer); + mSortedProperties.insert(property, sortProperty); +} + void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) { for (const auto &property : mProperties) { @@ -82,31 +109,57 @@ void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain: auto indexer = mIndexer.value(property); indexer(identifier, value, transaction); } + for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { + const auto value = bufferAdaptor.getProperty(it.key()); + const auto sortValue = bufferAdaptor.getProperty(it.value()); + auto indexer = mSortIndexer.value(it.key()+it.value()); + indexer(identifier, value, sortValue, transaction); + } } void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) { for (const auto &property : mProperties) { const auto value = bufferAdaptor.getProperty(property); - if (value.isValid()) { - //FIXME don't always convert to byte array - Index(mType + ".index." + property, transaction).remove(value.toByteArray(), identifier); + //FIXME don't always convert to byte array + Index(indexName(property), transaction).remove(getByteArray(value), identifier); + } + for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { + const auto propertyValue = bufferAdaptor.getProperty(it.key()); + const auto sortValue = bufferAdaptor.getProperty(it.value()); + if (sortValue.type() == QVariant::DateTime) { + Index(indexName(it.key(), it.value()), transaction).remove(propertyValue.toByteArray() + toSortableByteArray(sortValue.toDateTime()), identifier); } else { - Index(mType + ".index." + property, transaction).remove("toplevel", identifier); + Index(indexName(it.key(), it.value()), transaction).remove(propertyValue.toByteArray() + sortValue.toByteArray(), identifier); } } } -ResultSet TypeIndex::query(const Sink::Query &query, QSet &appliedFilters, Sink::Storage::Transaction &transaction) +ResultSet TypeIndex::query(const Sink::Query &query, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction) { QVector keys; + for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { + if (query.propertyFilter.contains(it.key()) && query.sortProperty == it.value()) { + Index index(indexName(it.key(), it.value()), transaction); + const auto lookupKey = getByteArray(query.propertyFilter.value(it.key())); + Trace() << "looking for " << lookupKey; + index.lookup(lookupKey, [&](const QByteArray &value) { + keys << value; + }, + [it](const Index::Error &error) { + Warning() << "Error in index: " << error.message << it.key() << it.value(); + }, true); + appliedFilters << it.key(); + appliedSorting = it.value(); + Trace() << "Index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; + return ResultSet(keys); + + } + } for (const auto &property : mProperties) { if (query.propertyFilter.contains(property)) { - Index index(mType + ".index." + property, transaction); - auto lookupKey = query.propertyFilter.value(property).toByteArray(); - if (lookupKey.isEmpty()) { - lookupKey = "toplevel"; - } + Index index(indexName(property), transaction); + const auto lookupKey = getByteArray(query.propertyFilter.value(property)); index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; }, diff --git a/common/typeindex.h b/common/typeindex.h index b8a6e39..c19780c 100644 --- a/common/typeindex.h +++ b/common/typeindex.h @@ -31,15 +31,20 @@ public: template void addProperty(const QByteArray &property); + template + void addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty); void add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); void remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); - ResultSet query(const Sink::Query &query, QSet &appliedFilters, Sink::Storage::Transaction &transaction); + ResultSet query(const Sink::Query &query, QSet &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction); private: + QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; QByteArray mType; QByteArrayList mProperties; + QMap mSortedProperties; QHash > mIndexer; + QHash > mSortIndexer; }; -- cgit v1.2.3