From 00717f6c8b8a9c6dbd56a80d685c5082fc03f6a5 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 25 May 2018 11:28:22 +0200 Subject: Implement range queries --- common/typeindex.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 12 deletions(-) (limited to 'common/typeindex.cpp') diff --git a/common/typeindex.cpp b/common/typeindex.cpp index a897ad0..5564144 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp @@ -21,9 +21,12 @@ #include "log.h" #include "index.h" #include "fulltextindex.h" + #include #include +#include + using namespace Sink; static QByteArray getByteArray(const QVariant &value) @@ -50,15 +53,34 @@ static QByteArray getByteArray(const QVariant &value) return "toplevel"; } -static QByteArray toSortableByteArray(const QDateTime &date) + +static QByteArray toSortableByteArrayImpl(const QDateTime &date) { // Sort invalid last if (!date.isValid()) { return QByteArray::number(std::numeric_limits::max()); } - return QByteArray::number(std::numeric_limits::max() - date.toTime_t()); + static unsigned int uint_num_digits = std::log10(std::numeric_limits::max()) + 1; + return QByteArray::number(std::numeric_limits::max() - date.toTime_t()).rightJustified(uint_num_digits, '0'); } +static QByteArray toSortableByteArray(const QVariant &value) +{ + if (!value.isValid()) { + // FIXME: we don't know the type, so we don't know what to return + // This mean we're fixing every sorted index keys to use unsigned int + return QByteArray::number(std::numeric_limits::max()); + } + + switch (value.type()) { + case QMetaType::QDateTime: + return toSortableByteArrayImpl(value.toDateTime()); + default: + SinkWarning() << "Not knowing how to convert a" << value.typeName() + << "to a sortable key, falling back to default conversion"; + return getByteArray(value); + } +} TypeIndex::TypeIndex(const QByteArray &type, const Sink::Log::Context &ctx) : mLogCtx(ctx), mType(type) { @@ -72,6 +94,11 @@ QByteArray TypeIndex::indexName(const QByteArray &property, const QByteArray &so return mType + ".index." + property + ".sort." + sortProperty; } +QByteArray TypeIndex::sortedIndexName(const QByteArray &property) const +{ + return mType + ".index." + property + ".sorted"; +} + template <> void TypeIndex::addProperty(const QByteArray &property) { @@ -137,6 +164,22 @@ void TypeIndex::addProperty(const QByteArray &prop addProperty(property); } +template <> +void TypeIndex::addSortedProperty(const QByteArray &property) +{ + auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value, + Sink::Storage::DataStore::Transaction &transaction) { + const auto sortableDate = toSortableByteArray(value); + if (add) { + Index(sortedIndexName(property), transaction).add(sortableDate, identifier); + } else { + Index(sortedIndexName(property), transaction).remove(sortableDate, identifier); + } + }; + mSortIndexer.insert(property, indexer); + mSortedProperties << property; +} + template <> void TypeIndex::addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty) { @@ -149,8 +192,8 @@ void TypeIndex::addPropertyWithSorting(const QByteArray & Index(indexName(property, sortProperty), transaction).remove(propertyValue + toSortableByteArray(date), identifier); } }; - mSortIndexer.insert(property + sortProperty, indexer); - mSortedProperties.insert(property, sortProperty); + mGroupedSortIndexer.insert(property + sortProperty, indexer); + mGroupedSortedProperties.insert(property, sortProperty); } template <> @@ -166,10 +209,15 @@ void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink:: auto indexer = mIndexer.value(property); indexer(add, identifier, value, transaction); } - for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { + for (const auto &property : mSortedProperties) { + const auto value = entity.getProperty(property); + auto indexer = mSortIndexer.value(property); + indexer(add, identifier, value, transaction); + } + for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) { const auto value = entity.getProperty(it.key()); const auto sortValue = entity.getProperty(it.value()); - auto indexer = mSortIndexer.value(it.key() + it.value()); + auto indexer = mGroupedSortIndexer.value(it.key() + it.value()); indexer(add, identifier, value, sortValue, transaction); } for (const auto &indexer : mCustomIndexer) { @@ -207,22 +255,61 @@ void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDoma updateIndex(false, identifier, entity, transaction, resourceInstanceId); } -static QVector indexLookup(Index &index, QueryBase::Comparator filter) +static QVector indexLookup(Index &index, QueryBase::Comparator filter, + std::function valueToKey = getByteArray) { QVector keys; QByteArrayList lookupKeys; if (filter.comparator == Query::Comparator::Equals) { - lookupKeys << getByteArray(filter.value); + lookupKeys << valueToKey(filter.value); } else if (filter.comparator == Query::Comparator::In) { - lookupKeys = filter.value.value(); + for(const QVariant &value : filter.value.value()) { + lookupKeys << valueToKey(value); + } + //lookupKeys = filter.value.value(); } else { Q_ASSERT(false); } for (const auto &lookupKey : lookupKeys) { index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; }, - [lookupKey](const Index::Error &error) { SinkWarning() << "Lookup error in index: " << error.message << lookupKey; }, true); + [lookupKey](const Index::Error &error) { + SinkWarning() << "Lookup error in index: " << error.message << lookupKey; + }, + true); + } + return keys; +} + +static QVector sortedIndexLookup(Index &index, QueryBase::Comparator filter) +{ + if (filter.comparator == Query::Comparator::In || filter.comparator == Query::Comparator::Contains) { + SinkWarning() << "In and Contains comparison not supported on sorted indexes"; + } + + if (filter.comparator != Query::Comparator::Within) { + return indexLookup(index, filter, toSortableByteArray); + } + + QVector keys; + + QByteArray lowerBound, upperBound; + auto bounds = filter.value.value(); + if (bounds[0].canConvert()) { + // Inverse the bounds because dates are stored newest first + upperBound = toSortableByteArray(bounds[0].toDateTime()); + lowerBound = toSortableByteArray(bounds[1].toDateTime()); + } else { + lowerBound = bounds[0].toByteArray(); + upperBound = bounds[1].toByteArray(); } + + index.rangeLookup(lowerBound, upperBound, [&](const QByteArray &value) { keys << value; }, + [bounds](const Index::Error &error) { + SinkWarning() << "Lookup error in index:" << error.message + << "with bounds:" << bounds[0] << bounds[1]; + }); + return keys; } @@ -239,16 +326,27 @@ QVector TypeIndex::query(const Sink::QueryBase &query, QSet