summaryrefslogtreecommitdiffstats
path: root/common/typeindex.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/typeindex.cpp')
-rw-r--r--common/typeindex.cpp122
1 files changed, 110 insertions, 12 deletions
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 @@
21#include "log.h" 21#include "log.h"
22#include "index.h" 22#include "index.h"
23#include "fulltextindex.h" 23#include "fulltextindex.h"
24
24#include <QDateTime> 25#include <QDateTime>
25#include <QDataStream> 26#include <QDataStream>
26 27
28#include <cmath>
29
27using namespace Sink; 30using namespace Sink;
28 31
29static QByteArray getByteArray(const QVariant &value) 32static QByteArray getByteArray(const QVariant &value)
@@ -50,15 +53,34 @@ static QByteArray getByteArray(const QVariant &value)
50 return "toplevel"; 53 return "toplevel";
51} 54}
52 55
53static QByteArray toSortableByteArray(const QDateTime &date) 56
57static QByteArray toSortableByteArrayImpl(const QDateTime &date)
54{ 58{
55 // Sort invalid last 59 // Sort invalid last
56 if (!date.isValid()) { 60 if (!date.isValid()) {
57 return QByteArray::number(std::numeric_limits<unsigned int>::max()); 61 return QByteArray::number(std::numeric_limits<unsigned int>::max());
58 } 62 }
59 return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()); 63 static unsigned int uint_num_digits = std::log10(std::numeric_limits<unsigned int>::max()) + 1;
64 return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()).rightJustified(uint_num_digits, '0');
60} 65}
61 66
67static QByteArray toSortableByteArray(const QVariant &value)
68{
69 if (!value.isValid()) {
70 // FIXME: we don't know the type, so we don't know what to return
71 // This mean we're fixing every sorted index keys to use unsigned int
72 return QByteArray::number(std::numeric_limits<unsigned int>::max());
73 }
74
75 switch (value.type()) {
76 case QMetaType::QDateTime:
77 return toSortableByteArrayImpl(value.toDateTime());
78 default:
79 SinkWarning() << "Not knowing how to convert a" << value.typeName()
80 << "to a sortable key, falling back to default conversion";
81 return getByteArray(value);
82 }
83}
62 84
63TypeIndex::TypeIndex(const QByteArray &type, const Sink::Log::Context &ctx) : mLogCtx(ctx), mType(type) 85TypeIndex::TypeIndex(const QByteArray &type, const Sink::Log::Context &ctx) : mLogCtx(ctx), mType(type)
64{ 86{
@@ -72,6 +94,11 @@ QByteArray TypeIndex::indexName(const QByteArray &property, const QByteArray &so
72 return mType + ".index." + property + ".sort." + sortProperty; 94 return mType + ".index." + property + ".sort." + sortProperty;
73} 95}
74 96
97QByteArray TypeIndex::sortedIndexName(const QByteArray &property) const
98{
99 return mType + ".index." + property + ".sorted";
100}
101
75template <> 102template <>
76void TypeIndex::addProperty<QByteArray>(const QByteArray &property) 103void TypeIndex::addProperty<QByteArray>(const QByteArray &property)
77{ 104{
@@ -138,6 +165,22 @@ void TypeIndex::addProperty<ApplicationDomain::Reference>(const QByteArray &prop
138} 165}
139 166
140template <> 167template <>
168void TypeIndex::addSortedProperty<QDateTime>(const QByteArray &property)
169{
170 auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value,
171 Sink::Storage::DataStore::Transaction &transaction) {
172 const auto sortableDate = toSortableByteArray(value);
173 if (add) {
174 Index(sortedIndexName(property), transaction).add(sortableDate, identifier);
175 } else {
176 Index(sortedIndexName(property), transaction).remove(sortableDate, identifier);
177 }
178 };
179 mSortIndexer.insert(property, indexer);
180 mSortedProperties << property;
181}
182
183template <>
141void TypeIndex::addPropertyWithSorting<QByteArray, QDateTime>(const QByteArray &property, const QByteArray &sortProperty) 184void TypeIndex::addPropertyWithSorting<QByteArray, QDateTime>(const QByteArray &property, const QByteArray &sortProperty)
142{ 185{
143 auto indexer = [=](bool add, const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction) { 186 auto indexer = [=](bool add, const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction) {
@@ -149,8 +192,8 @@ void TypeIndex::addPropertyWithSorting<QByteArray, QDateTime>(const QByteArray &
149 Index(indexName(property, sortProperty), transaction).remove(propertyValue + toSortableByteArray(date), identifier); 192 Index(indexName(property, sortProperty), transaction).remove(propertyValue + toSortableByteArray(date), identifier);
150 } 193 }
151 }; 194 };
152 mSortIndexer.insert(property + sortProperty, indexer); 195 mGroupedSortIndexer.insert(property + sortProperty, indexer);
153 mSortedProperties.insert(property, sortProperty); 196 mGroupedSortedProperties.insert(property, sortProperty);
154} 197}
155 198
156template <> 199template <>
@@ -166,10 +209,15 @@ void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::
166 auto indexer = mIndexer.value(property); 209 auto indexer = mIndexer.value(property);
167 indexer(add, identifier, value, transaction); 210 indexer(add, identifier, value, transaction);
168 } 211 }
169 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { 212 for (const auto &property : mSortedProperties) {
213 const auto value = entity.getProperty(property);
214 auto indexer = mSortIndexer.value(property);
215 indexer(add, identifier, value, transaction);
216 }
217 for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) {
170 const auto value = entity.getProperty(it.key()); 218 const auto value = entity.getProperty(it.key());
171 const auto sortValue = entity.getProperty(it.value()); 219 const auto sortValue = entity.getProperty(it.value());
172 auto indexer = mSortIndexer.value(it.key() + it.value()); 220 auto indexer = mGroupedSortIndexer.value(it.key() + it.value());
173 indexer(add, identifier, value, sortValue, transaction); 221 indexer(add, identifier, value, sortValue, transaction);
174 } 222 }
175 for (const auto &indexer : mCustomIndexer) { 223 for (const auto &indexer : mCustomIndexer) {
@@ -207,22 +255,61 @@ void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDoma
207 updateIndex(false, identifier, entity, transaction, resourceInstanceId); 255 updateIndex(false, identifier, entity, transaction, resourceInstanceId);
208} 256}
209 257
210static QVector<QByteArray> indexLookup(Index &index, QueryBase::Comparator filter) 258static QVector<QByteArray> indexLookup(Index &index, QueryBase::Comparator filter,
259 std::function<QByteArray(const QVariant &)> valueToKey = getByteArray)
211{ 260{
212 QVector<QByteArray> keys; 261 QVector<QByteArray> keys;
213 QByteArrayList lookupKeys; 262 QByteArrayList lookupKeys;
214 if (filter.comparator == Query::Comparator::Equals) { 263 if (filter.comparator == Query::Comparator::Equals) {
215 lookupKeys << getByteArray(filter.value); 264 lookupKeys << valueToKey(filter.value);
216 } else if (filter.comparator == Query::Comparator::In) { 265 } else if (filter.comparator == Query::Comparator::In) {
217 lookupKeys = filter.value.value<QByteArrayList>(); 266 for(const QVariant &value : filter.value.value<QVariantList>()) {
267 lookupKeys << valueToKey(value);
268 }
269 //lookupKeys = filter.value.value<QByteArrayList>();
218 } else { 270 } else {
219 Q_ASSERT(false); 271 Q_ASSERT(false);
220 } 272 }
221 273
222 for (const auto &lookupKey : lookupKeys) { 274 for (const auto &lookupKey : lookupKeys) {
223 index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; }, 275 index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; },
224 [lookupKey](const Index::Error &error) { SinkWarning() << "Lookup error in index: " << error.message << lookupKey; }, true); 276 [lookupKey](const Index::Error &error) {
277 SinkWarning() << "Lookup error in index: " << error.message << lookupKey;
278 },
279 true);
280 }
281 return keys;
282}
283
284static QVector<QByteArray> sortedIndexLookup(Index &index, QueryBase::Comparator filter)
285{
286 if (filter.comparator == Query::Comparator::In || filter.comparator == Query::Comparator::Contains) {
287 SinkWarning() << "In and Contains comparison not supported on sorted indexes";
288 }
289
290 if (filter.comparator != Query::Comparator::Within) {
291 return indexLookup(index, filter, toSortableByteArray);
292 }
293
294 QVector<QByteArray> keys;
295
296 QByteArray lowerBound, upperBound;
297 auto bounds = filter.value.value<QVariantList>();
298 if (bounds[0].canConvert<QDateTime>()) {
299 // Inverse the bounds because dates are stored newest first
300 upperBound = toSortableByteArray(bounds[0].toDateTime());
301 lowerBound = toSortableByteArray(bounds[1].toDateTime());
302 } else {
303 lowerBound = bounds[0].toByteArray();
304 upperBound = bounds[1].toByteArray();
225 } 305 }
306
307 index.rangeLookup(lowerBound, upperBound, [&](const QByteArray &value) { keys << value; },
308 [bounds](const Index::Error &error) {
309 SinkWarning() << "Lookup error in index:" << error.message
310 << "with bounds:" << bounds[0] << bounds[1];
311 });
312
226 return keys; 313 return keys;
227} 314}
228 315
@@ -239,16 +326,27 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr
239 } 326 }
240 } 327 }
241 328
242 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { 329 for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) {
243 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) { 330 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) {
244 Index index(indexName(it.key(), it.value()), transaction); 331 Index index(indexName(it.key(), it.value()), transaction);
245 const auto keys = indexLookup(index, query.getFilter(it.key())); 332 const auto keys = indexLookup(index, query.getFilter(it.key()));
246 appliedFilters << it.key(); 333 appliedFilters << it.key();
247 appliedSorting = it.value(); 334 appliedSorting = it.value();
248 SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; 335 SinkTraceCtx(mLogCtx) << "Grouped sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys.";
249 return keys; 336 return keys;
250 } 337 }
251 } 338 }
339
340 for (const auto &property : mSortedProperties) {
341 if (query.hasFilter(property)) {
342 Index index(sortedIndexName(property), transaction);
343 const auto keys = sortedIndexLookup(index, query.getFilter(property));
344 appliedFilters << property;
345 SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << property << " found " << keys.size() << " keys.";
346 return keys;
347 }
348 }
349
252 for (const auto &property : mProperties) { 350 for (const auto &property : mProperties) {
253 if (query.hasFilter(property)) { 351 if (query.hasFilter(property)) {
254 Index index(indexName(property), transaction); 352 Index index(indexName(property), transaction);