diff options
author | Minijackson <minijackson@riseup.net> | 2018-06-11 11:08:12 +0200 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2018-06-11 11:08:12 +0200 |
commit | a520527c8983bf1cba50fd3d86d406e7e68ddb88 (patch) | |
tree | a2b4a104e00c6a14c11651a2112b6b45ce93babd /common/typeindex.cpp | |
parent | bdf8ada25760ca9f4054d9ac719c703ce8abc300 (diff) | |
download | sink-a520527c8983bf1cba50fd3d86d406e7e68ddb88.tar.gz sink-a520527c8983bf1cba50fd3d86d406e7e68ddb88.zip |
Add overlap query
Diffstat (limited to 'common/typeindex.cpp')
-rw-r--r-- | common/typeindex.cpp | 140 |
1 files changed, 134 insertions, 6 deletions
diff --git a/common/typeindex.cpp b/common/typeindex.cpp index a111134..e056b36 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp | |||
@@ -22,6 +22,8 @@ | |||
22 | #include "index.h" | 22 | #include "index.h" |
23 | #include "fulltextindex.h" | 23 | #include "fulltextindex.h" |
24 | 24 | ||
25 | #include "sample_generated.h" | ||
26 | |||
25 | #include <QDateTime> | 27 | #include <QDateTime> |
26 | #include <QDataStream> | 28 | #include <QDataStream> |
27 | 29 | ||
@@ -53,6 +55,12 @@ static QByteArray getByteArray(const QVariant &value) | |||
53 | return "toplevel"; | 55 | return "toplevel"; |
54 | } | 56 | } |
55 | 57 | ||
58 | template <typename T> | ||
59 | static QByteArray padNumber(T number) | ||
60 | { | ||
61 | static T uint_num_digits = (T)std::log10(std::numeric_limits<T>::max()) + 1; | ||
62 | return QByteArray::number(number).rightJustified(uint_num_digits, '0'); | ||
63 | } | ||
56 | 64 | ||
57 | static QByteArray toSortableByteArrayImpl(const QDateTime &date) | 65 | static QByteArray toSortableByteArrayImpl(const QDateTime &date) |
58 | { | 66 | { |
@@ -60,8 +68,7 @@ static QByteArray toSortableByteArrayImpl(const QDateTime &date) | |||
60 | if (!date.isValid()) { | 68 | if (!date.isValid()) { |
61 | return QByteArray::number(std::numeric_limits<unsigned int>::max()); | 69 | return QByteArray::number(std::numeric_limits<unsigned int>::max()); |
62 | } | 70 | } |
63 | static unsigned int uint_num_digits = (unsigned int)std::log10(std::numeric_limits<unsigned int>::max()) + 1; | 71 | return padNumber(std::numeric_limits<unsigned int>::max() - date.toTime_t()); |
64 | return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()).rightJustified(uint_num_digits, '0'); | ||
65 | } | 72 | } |
66 | 73 | ||
67 | static QByteArray toSortableByteArray(const QVariant &value) | 74 | static QByteArray toSortableByteArray(const QVariant &value) |
@@ -99,6 +106,22 @@ QByteArray TypeIndex::sortedIndexName(const QByteArray &property) const | |||
99 | return mType + ".index." + property + ".sorted"; | 106 | return mType + ".index." + property + ".sorted"; |
100 | } | 107 | } |
101 | 108 | ||
109 | QByteArray TypeIndex::sampledPeriodIndexName(const QByteArray &rangeBeginProperty, const QByteArray &rangeEndProperty) const | ||
110 | { | ||
111 | return mType + ".index." + rangeBeginProperty + ".range." + rangeEndProperty; | ||
112 | } | ||
113 | |||
114 | static unsigned int bucketOf(const QVariant &value) | ||
115 | { | ||
116 | switch (value.type()) { | ||
117 | case QMetaType::QDateTime: | ||
118 | return value.value<QDateTime>().date().toJulianDay() / 7; | ||
119 | default: | ||
120 | SinkError() << "Not knowing how to get the bucket of a" << value.typeName(); | ||
121 | return {}; | ||
122 | } | ||
123 | } | ||
124 | |||
102 | template <> | 125 | template <> |
103 | void TypeIndex::addProperty<QByteArray>(const QByteArray &property) | 126 | void TypeIndex::addProperty<QByteArray>(const QByteArray &property) |
104 | { | 127 | { |
@@ -202,6 +225,50 @@ void TypeIndex::addPropertyWithSorting<ApplicationDomain::Reference, QDateTime>( | |||
202 | addPropertyWithSorting<QByteArray, QDateTime>(property, sortProperty); | 225 | addPropertyWithSorting<QByteArray, QDateTime>(property, sortProperty); |
203 | } | 226 | } |
204 | 227 | ||
228 | template <> | ||
229 | void TypeIndex::addSampledPeriodIndex<QDateTime, QDateTime>( | ||
230 | const QByteArray &beginProperty, const QByteArray &endProperty) | ||
231 | { | ||
232 | auto indexer = [=](bool add, const QByteArray &identifier, const QVariant &begin, | ||
233 | const QVariant &end, Sink::Storage::DataStore::Transaction &transaction) { | ||
234 | SinkTraceCtx(mLogCtx) << "Adding entity to sampled period index"; | ||
235 | const auto beginDate = begin.toDateTime(); | ||
236 | const auto endDate = end.toDateTime(); | ||
237 | |||
238 | auto beginBucket = bucketOf(beginDate); | ||
239 | auto endBucket = bucketOf(endDate); | ||
240 | |||
241 | if (beginBucket > endBucket) { | ||
242 | SinkError() << "End bucket greater than begin bucket"; | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | auto beginBA = toSortableByteArray(beginDate); | ||
247 | auto endBA = toSortableByteArray(endDate); | ||
248 | |||
249 | flatbuffers::FlatBufferBuilder fbb; | ||
250 | auto sample = CreateSampleDirect(fbb, beginBA.constData(), endBA.constData(), identifier.constData()); | ||
251 | fbb.Finish(sample); | ||
252 | const auto buffer = QByteArray::fromRawData( | ||
253 | reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize()); | ||
254 | |||
255 | Index index(sampledPeriodIndexName(beginProperty, endProperty), transaction); | ||
256 | for (auto bucket = beginBucket; bucket <= endBucket; ++bucket) { | ||
257 | QByteArray bucketKey = padNumber(bucket); | ||
258 | if (add) { | ||
259 | SinkTraceCtx(mLogCtx) << "Adding entity to bucket:" << bucketKey; | ||
260 | index.add(bucketKey, buffer); | ||
261 | } else { | ||
262 | SinkTraceCtx(mLogCtx) << "Removing entity from bucket:" << bucketKey; | ||
263 | index.remove(bucketKey, buffer); | ||
264 | } | ||
265 | } | ||
266 | }; | ||
267 | |||
268 | mSampledPeriodProperties.insert({ beginProperty, endProperty }); | ||
269 | mSampledPeriodIndexer.insert({ beginProperty, endProperty }, indexer); | ||
270 | } | ||
271 | |||
205 | void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) | 272 | void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) |
206 | { | 273 | { |
207 | for (const auto &property : mProperties) { | 274 | for (const auto &property : mProperties) { |
@@ -209,6 +276,12 @@ void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink:: | |||
209 | auto indexer = mIndexer.value(property); | 276 | auto indexer = mIndexer.value(property); |
210 | indexer(add, identifier, value, transaction); | 277 | indexer(add, identifier, value, transaction); |
211 | } | 278 | } |
279 | for (const auto &properties : mSampledPeriodProperties) { | ||
280 | const auto beginValue = entity.getProperty(properties.first); | ||
281 | const auto endValue = entity.getProperty(properties.second); | ||
282 | auto indexer = mSampledPeriodIndexer.value(properties); | ||
283 | indexer(add, identifier, beginValue, endValue, transaction); | ||
284 | } | ||
212 | for (const auto &property : mSortedProperties) { | 285 | for (const auto &property : mSortedProperties) { |
213 | const auto value = entity.getProperty(property); | 286 | const auto value = entity.getProperty(property); |
214 | auto indexer = mSortIndexer.value(property); | 287 | auto indexer = mSortIndexer.value(property); |
@@ -312,7 +385,48 @@ static QVector<QByteArray> sortedIndexLookup(Index &index, QueryBase::Comparator | |||
312 | return keys; | 385 | return keys; |
313 | } | 386 | } |
314 | 387 | ||
315 | QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) | 388 | static QVector<QByteArray> sampledIndexLookup(Index &index, QueryBase::Comparator filter) |
389 | { | ||
390 | if (filter.comparator != Query::Comparator::Overlap) { | ||
391 | SinkWarning() << "Comparisons other than Overlap not supported on sampled period indexes"; | ||
392 | return {}; | ||
393 | } | ||
394 | |||
395 | QVector<QByteArray> keys; | ||
396 | |||
397 | auto bounds = filter.value.value<QVariantList>(); | ||
398 | |||
399 | QByteArray lowerBound = toSortableByteArray(bounds[0]); | ||
400 | QByteArray upperBound = toSortableByteArray(bounds[1]); | ||
401 | |||
402 | QByteArray lowerBucket = padNumber(bucketOf(bounds[0])); | ||
403 | QByteArray upperBucket = padNumber(bucketOf(bounds[1])); | ||
404 | |||
405 | SinkTrace() << "Looking up from bucket:" << lowerBucket << "to:" << upperBucket; | ||
406 | |||
407 | index.rangeLookup(lowerBucket, upperBucket, | ||
408 | [&](const QByteArray &value) { | ||
409 | auto sample = GetSample(value.data()); | ||
410 | |||
411 | QByteArray periodStart = QString::fromStdString(sample->periodStart()->str()).toUtf8(); | ||
412 | QByteArray periodEnd = QString::fromStdString(sample->periodEnd()->str()).toUtf8(); | ||
413 | |||
414 | // Inverse comparison since descending order | ||
415 | if (periodStart >= upperBound && lowerBound >= periodEnd) { | ||
416 | QByteArray key = QString::fromStdString(sample->reference()->str()).toUtf8(); | ||
417 | SinkTrace() << "Keeping:" << key; | ||
418 | keys << key; | ||
419 | } | ||
420 | }, | ||
421 | [bounds](const Index::Error &error) { | ||
422 | SinkWarning() << "Lookup error in index:" << error.message | ||
423 | << "with bounds:" << bounds[0] << bounds[1]; | ||
424 | }); | ||
425 | |||
426 | return keys; | ||
427 | } | ||
428 | |||
429 | QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArrayList> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) | ||
316 | { | 430 | { |
317 | const auto baseFilters = query.getBaseFilters(); | 431 | const auto baseFilters = query.getBaseFilters(); |
318 | for (auto it = baseFilters.constBegin(); it != baseFilters.constEnd(); it++) { | 432 | for (auto it = baseFilters.constBegin(); it != baseFilters.constEnd(); it++) { |
@@ -325,11 +439,25 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr | |||
325 | } | 439 | } |
326 | } | 440 | } |
327 | 441 | ||
442 | for (auto it = baseFilters.constBegin(); it != baseFilters.constEnd(); it++) { | ||
443 | if (it.value().comparator == QueryBase::Comparator::Overlap) { | ||
444 | if (mSampledPeriodProperties.contains({it.key()[0], it.key()[1]})) { | ||
445 | Index index(sampledPeriodIndexName(it.key()[0], it.key()[1]), transaction); | ||
446 | const auto keys = sampledIndexLookup(index, query.getFilter(it.key())); | ||
447 | appliedFilters << it.key(); | ||
448 | SinkTraceCtx(mLogCtx) << "Sampled period index lookup on" << it.key() << "found" << keys.size() << "keys."; | ||
449 | return keys; | ||
450 | } else { | ||
451 | SinkWarning() << "Overlap search without sampled period index"; | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | |||
328 | for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) { | 456 | for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) { |
329 | if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) { | 457 | if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) { |
330 | Index index(indexName(it.key(), it.value()), transaction); | 458 | Index index(indexName(it.key(), it.value()), transaction); |
331 | const auto keys = indexLookup(index, query.getFilter(it.key())); | 459 | const auto keys = indexLookup(index, query.getFilter(it.key())); |
332 | appliedFilters << it.key(); | 460 | appliedFilters.insert({it.key()}); |
333 | appliedSorting = it.value(); | 461 | appliedSorting = it.value(); |
334 | SinkTraceCtx(mLogCtx) << "Grouped sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; | 462 | SinkTraceCtx(mLogCtx) << "Grouped sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; |
335 | return keys; | 463 | return keys; |
@@ -340,7 +468,7 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr | |||
340 | if (query.hasFilter(property)) { | 468 | if (query.hasFilter(property)) { |
341 | Index index(sortedIndexName(property), transaction); | 469 | Index index(sortedIndexName(property), transaction); |
342 | const auto keys = sortedIndexLookup(index, query.getFilter(property)); | 470 | const auto keys = sortedIndexLookup(index, query.getFilter(property)); |
343 | appliedFilters << property; | 471 | appliedFilters.insert({property}); |
344 | SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << property << " found " << keys.size() << " keys."; | 472 | SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << property << " found " << keys.size() << " keys."; |
345 | return keys; | 473 | return keys; |
346 | } | 474 | } |
@@ -350,7 +478,7 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr | |||
350 | if (query.hasFilter(property)) { | 478 | if (query.hasFilter(property)) { |
351 | Index index(indexName(property), transaction); | 479 | Index index(indexName(property), transaction); |
352 | const auto keys = indexLookup(index, query.getFilter(property)); | 480 | const auto keys = indexLookup(index, query.getFilter(property)); |
353 | appliedFilters << property; | 481 | appliedFilters.insert({property}); |
354 | SinkTraceCtx(mLogCtx) << "Index lookup on " << property << " found " << keys.size() << " keys."; | 482 | SinkTraceCtx(mLogCtx) << "Index lookup on " << property << " found " << keys.size() << " keys."; |
355 | return keys; | 483 | return keys; |
356 | } | 484 | } |