summaryrefslogtreecommitdiffstats
path: root/common/typeindex.cpp
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2018-06-11 11:08:12 +0200
committerMinijackson <minijackson@riseup.net>2018-06-11 11:08:12 +0200
commita520527c8983bf1cba50fd3d86d406e7e68ddb88 (patch)
treea2b4a104e00c6a14c11651a2112b6b45ce93babd /common/typeindex.cpp
parentbdf8ada25760ca9f4054d9ac719c703ce8abc300 (diff)
downloadsink-a520527c8983bf1cba50fd3d86d406e7e68ddb88.tar.gz
sink-a520527c8983bf1cba50fd3d86d406e7e68ddb88.zip
Add overlap query
Diffstat (limited to 'common/typeindex.cpp')
-rw-r--r--common/typeindex.cpp140
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
58template <typename T>
59static 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
57static QByteArray toSortableByteArrayImpl(const QDateTime &date) 65static 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
67static QByteArray toSortableByteArray(const QVariant &value) 74static 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
109QByteArray TypeIndex::sampledPeriodIndexName(const QByteArray &rangeBeginProperty, const QByteArray &rangeEndProperty) const
110{
111 return mType + ".index." + rangeBeginProperty + ".range." + rangeEndProperty;
112}
113
114static 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
102template <> 125template <>
103void TypeIndex::addProperty<QByteArray>(const QByteArray &property) 126void 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
228template <>
229void 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
205void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) 272void 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
315QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) 388static 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
429QVector<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 }