summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorRémi Nicole <nicole@kolabsystems.com>2018-06-19 11:04:17 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-06-19 11:10:47 +0200
commit077e3cb30ace5f6ee20ee15e0d32d2bfb197fde0 (patch)
tree3cfdaf0912ef22dba71755b4332354d579f6e7cf /common
parent1ff4456e5dc2b9a9dfa80047f9e5a4a9e1395cdf (diff)
downloadsink-077e3cb30ace5f6ee20ee15e0d32d2bfb197fde0.tar.gz
sink-077e3cb30ace5f6ee20ee15e0d32d2bfb197fde0.zip
Implement Overlap queries
Summary: Notes: - Introduces the concept of queries on multiple properties (which meant changing query's internals a bit) - Dates are stored as well as the "reference" in the index to allow quick filtering without fetching the whole entity - Buckets are weeks starting on Monday (guaranteed by the use of the Julian calendar) - Some size improvements are definitely possible (dates are padded numbers again, not using integer databases, Julian calendar starts at a very old date, etc.) Test Plan: Tested in querytest Reviewers: cmollekopf Reviewed By: cmollekopf Tags: #sink Differential Revision: https://phabricator.kde.org/D13477
Diffstat (limited to 'common')
-rw-r--r--common/datastorequery.cpp17
-rw-r--r--common/domain/typeimplementations.cpp3
-rw-r--r--common/domain/typeimplementations_p.h20
-rw-r--r--common/query.cpp13
-rw-r--r--common/query.h44
-rw-r--r--common/resourcefacade.cpp8
-rw-r--r--common/storage/entitystore.cpp2
-rw-r--r--common/storage/entitystore.h2
-rw-r--r--common/store.cpp12
-rw-r--r--common/typeindex.cpp122
-rw-r--r--common/typeindex.h14
11 files changed, 224 insertions, 33 deletions
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp
index 12c0ae1..263d3ea 100644
--- a/common/datastorequery.cpp
+++ b/common/datastorequery.cpp
@@ -119,7 +119,7 @@ class Filter : public FilterBase {
119public: 119public:
120 typedef QSharedPointer<Filter> Ptr; 120 typedef QSharedPointer<Filter> Ptr;
121 121
122 QHash<QByteArray, Sink::QueryBase::Comparator> propertyFilter; 122 QHash<QByteArrayList, Sink::QueryBase::Comparator> propertyFilter;
123 123
124 Filter(FilterBase::Ptr source, DataStoreQuery *store) 124 Filter(FilterBase::Ptr source, DataStoreQuery *store)
125 : FilterBase(source, store) 125 : FilterBase(source, store)
@@ -158,7 +158,16 @@ public:
158 158
159 bool matchesFilter(const ApplicationDomain::ApplicationDomainType &entity) { 159 bool matchesFilter(const ApplicationDomain::ApplicationDomainType &entity) {
160 for (const auto &filterProperty : propertyFilter.keys()) { 160 for (const auto &filterProperty : propertyFilter.keys()) {
161 const auto property = entity.getProperty(filterProperty); 161 QVariant property;
162 if (filterProperty.size() == 1) {
163 property = entity.getProperty(filterProperty[0]);
164 } else {
165 QVariantList propList;
166 for (const auto &propName : filterProperty) {
167 propList.push_back(entity.getProperty(propName));
168 }
169 property = propList;
170 }
162 const auto comparator = propertyFilter.value(filterProperty); 171 const auto comparator = propertyFilter.value(filterProperty);
163 //We can't deal with a fulltext filter 172 //We can't deal with a fulltext filter
164 if (comparator.comparator == QueryBase::Comparator::Fulltext) { 173 if (comparator.comparator == QueryBase::Comparator::Fulltext) {
@@ -420,7 +429,7 @@ public:
420 })) 429 }))
421 {} 430 {}
422 mBloomed = true; 431 mBloomed = true;
423 propertyFilter.insert(mBloomProperty, mBloomValue); 432 propertyFilter.insert({mBloomProperty}, mBloomValue);
424 return foundValue; 433 return foundValue;
425 } else { 434 } else {
426 //Filter on bloom value 435 //Filter on bloom value
@@ -598,7 +607,7 @@ void DataStoreQuery::setupQuery(const Sink::QueryBase &query_)
598 //We have a set of ids as a starting point 607 //We have a set of ids as a starting point
599 return Source::Ptr::create(query.ids().toVector(), this); 608 return Source::Ptr::create(query.ids().toVector(), this);
600 } else { 609 } else {
601 QSet<QByteArray> appliedFilters; 610 QSet<QByteArrayList> appliedFilters;
602 auto resultSet = mStore.indexLookup(mType, query, appliedFilters, appliedSorting); 611 auto resultSet = mStore.indexLookup(mType, query, appliedFilters, appliedSorting);
603 if (!appliedFilters.isEmpty()) { 612 if (!appliedFilters.isEmpty()) {
604 //We have an index lookup as starting point 613 //We have an index lookup as starting point
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp
index a8f4baf..2b2d2ac 100644
--- a/common/domain/typeimplementations.cpp
+++ b/common/domain/typeimplementations.cpp
@@ -65,7 +65,8 @@ typedef IndexConfig<Addressbook,
65 65
66typedef IndexConfig<Event, 66typedef IndexConfig<Event,
67 ValueIndex<Event::Uid>, 67 ValueIndex<Event::Uid>,
68 SortedIndex<Event::StartTime> 68 SortedIndex<Event::StartTime>,
69 SampledPeriodIndex<Event::StartTime, Event::EndTime>
69 > EventIndexConfig; 70 > EventIndexConfig;
70 71
71typedef IndexConfig<Todo, 72typedef IndexConfig<Todo,
diff --git a/common/domain/typeimplementations_p.h b/common/domain/typeimplementations_p.h
index fc08048..51af113 100644
--- a/common/domain/typeimplementations_p.h
+++ b/common/domain/typeimplementations_p.h
@@ -126,6 +126,26 @@ public:
126 } 126 }
127}; 127};
128 128
129template <typename RangeBeginProperty, typename RangeEndProperty>
130class SampledPeriodIndex
131{
132 static_assert(std::is_same<typename RangeBeginProperty::Type, QDateTime>::value &&
133 std::is_same<typename RangeEndProperty::Type, QDateTime>::value,
134 "Date range index is not supported for types other than 'QDateTime's");
135
136public:
137 static void configure(TypeIndex &index)
138 {
139 index.addSampledPeriodIndex<RangeBeginProperty, RangeEndProperty>();
140 }
141
142 template <typename EntityType>
143 static QMap<QByteArray, int> databases()
144 {
145 return {{QByteArray{EntityType::name} +".index." + RangeBeginProperty::name + ".range." + RangeEndProperty::name, 1}};
146 }
147};
148
129template <typename EntityType, typename ... Indexes> 149template <typename EntityType, typename ... Indexes>
130class IndexConfig 150class IndexConfig
131{ 151{
diff --git a/common/query.cpp b/common/query.cpp
index 404a304..ceb1897 100644
--- a/common/query.cpp
+++ b/common/query.cpp
@@ -179,6 +179,19 @@ bool QueryBase::Comparator::matches(const QVariant &v) const
179 179
180 return range[0] <= v && v <= range[1]; 180 return range[0] <= v && v <= range[1];
181 } 181 }
182 case Overlap: {
183 auto bounds = value.value<QList<QVariant>>();
184 if (bounds.size() < 2) {
185 return false;
186 }
187
188 auto range = v.value<QList<QVariant>>();
189 if (range.size() < 2) {
190 return false;
191 }
192
193 return range[0] <= bounds[1] && bounds[0] <= range[1];
194 }
182 case Fulltext: 195 case Fulltext:
183 case Invalid: 196 case Invalid:
184 default: 197 default:
diff --git a/common/query.h b/common/query.h
index 7130116..cb9c8ca 100644
--- a/common/query.h
+++ b/common/query.h
@@ -37,6 +37,7 @@ public:
37 Contains, 37 Contains,
38 In, 38 In,
39 Within, 39 Within,
40 Overlap,
40 Fulltext 41 Fulltext
41 }; 42 };
42 43
@@ -53,7 +54,7 @@ public:
53 class SINK_EXPORT Filter { 54 class SINK_EXPORT Filter {
54 public: 55 public:
55 QByteArrayList ids; 56 QByteArrayList ids;
56 QHash<QByteArray, Comparator> propertyFilter; 57 QHash<QByteArrayList, Comparator> propertyFilter;
57 bool operator==(const Filter &other) const; 58 bool operator==(const Filter &other) const;
58 }; 59 };
59 60
@@ -64,7 +65,12 @@ public:
64 65
65 Comparator getFilter(const QByteArray &property) const 66 Comparator getFilter(const QByteArray &property) const
66 { 67 {
67 return mBaseFilterStage.propertyFilter.value(property); 68 return mBaseFilterStage.propertyFilter.value({property});
69 }
70
71 Comparator getFilter(const QByteArrayList &properties) const
72 {
73 return mBaseFilterStage.propertyFilter.value(properties);
68 } 74 }
69 75
70 template <class T> 76 template <class T>
@@ -73,9 +79,15 @@ public:
73 return getFilter(T::name); 79 return getFilter(T::name);
74 } 80 }
75 81
82 template <class T1, class T2, class... Rest>
83 Comparator getFilter() const
84 {
85 return getFilter({T1::name, T2::name, Rest::name...});
86 }
87
76 bool hasFilter(const QByteArray &property) const 88 bool hasFilter(const QByteArray &property) const
77 { 89 {
78 return mBaseFilterStage.propertyFilter.contains(property); 90 return mBaseFilterStage.propertyFilter.contains({property});
79 } 91 }
80 92
81 template <class T> 93 template <class T>
@@ -94,7 +106,7 @@ public:
94 return mId; 106 return mId;
95 } 107 }
96 108
97 void setBaseFilters(const QHash<QByteArray, Comparator> &filter) 109 void setBaseFilters(const QHash<QByteArrayList, Comparator> &filter)
98 { 110 {
99 mBaseFilterStage.propertyFilter = filter; 111 mBaseFilterStage.propertyFilter = filter;
100 } 112 }
@@ -104,7 +116,7 @@ public:
104 mBaseFilterStage = filter; 116 mBaseFilterStage = filter;
105 } 117 }
106 118
107 QHash<QByteArray, Comparator> getBaseFilters() const 119 QHash<QByteArrayList, Comparator> getBaseFilters() const
108 { 120 {
109 return mBaseFilterStage.propertyFilter; 121 return mBaseFilterStage.propertyFilter;
110 } 122 }
@@ -131,7 +143,12 @@ public:
131 143
132 void filter(const QByteArray &property, const QueryBase::Comparator &comparator) 144 void filter(const QByteArray &property, const QueryBase::Comparator &comparator)
133 { 145 {
134 mBaseFilterStage.propertyFilter.insert(property, comparator); 146 mBaseFilterStage.propertyFilter.insert({property}, comparator);
147 }
148
149 void filter(const QByteArrayList &properties, const QueryBase::Comparator &comparator)
150 {
151 mBaseFilterStage.propertyFilter.insert(properties, comparator);
135 } 152 }
136 153
137 void setType(const QByteArray &type) 154 void setType(const QByteArray &type)
@@ -373,6 +390,13 @@ public:
373 return *this; 390 return *this;
374 } 391 }
375 392
393 template <typename T1, typename T2, typename... Rest>
394 Query &filter(const QueryBase::Comparator &comparator)
395 {
396 QueryBase::filter({T1::name, T2::name, Rest::name...}, comparator);
397 return *this;
398 }
399
376 Query &filter(const QByteArray &id) 400 Query &filter(const QByteArray &id)
377 { 401 {
378 QueryBase::filter(id); 402 QueryBase::filter(id);
@@ -465,13 +489,13 @@ public:
465 template <typename T> 489 template <typename T>
466 Query &resourceFilter(const ApplicationDomain::ApplicationDomainType &entity) 490 Query &resourceFilter(const ApplicationDomain::ApplicationDomainType &entity)
467 { 491 {
468 mResourceFilter.propertyFilter.insert(T::name, Comparator(entity.identifier())); 492 mResourceFilter.propertyFilter.insert({T::name}, Comparator(entity.identifier()));
469 return *this; 493 return *this;
470 } 494 }
471 495
472 Query &resourceFilter(const QByteArray &name, const Comparator &comparator) 496 Query &resourceFilter(const QByteArray &name, const Comparator &comparator)
473 { 497 {
474 mResourceFilter.propertyFilter.insert(name, comparator); 498 mResourceFilter.propertyFilter.insert({name}, comparator);
475 return *this; 499 return *this;
476 } 500 }
477 501
@@ -531,13 +555,13 @@ public:
531 template <typename T> 555 template <typename T>
532 SyncScope &resourceFilter(const ApplicationDomain::ApplicationDomainType &entity) 556 SyncScope &resourceFilter(const ApplicationDomain::ApplicationDomainType &entity)
533 { 557 {
534 mResourceFilter.propertyFilter.insert(T::name, Comparator(entity.identifier())); 558 mResourceFilter.propertyFilter.insert({T::name}, Comparator(entity.identifier()));
535 return *this; 559 return *this;
536 } 560 }
537 561
538 SyncScope &resourceFilter(const QByteArray &name, const Comparator &comparator) 562 SyncScope &resourceFilter(const QByteArray &name, const Comparator &comparator)
539 { 563 {
540 mResourceFilter.propertyFilter.insert(name, comparator); 564 mResourceFilter.propertyFilter.insert({name}, comparator);
541 return *this; 565 return *this;
542 } 566 }
543 567
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp
index 7998692..90194d4 100644
--- a/common/resourcefacade.cpp
+++ b/common/resourcefacade.cpp
@@ -80,13 +80,13 @@ typename ApplicationDomain::SinkResource::Ptr readFromConfig<ApplicationDomain::
80 return object; 80 return object;
81} 81}
82 82
83static bool matchesFilter(const QHash<QByteArray, Query::Comparator> &filter, const ApplicationDomain::ApplicationDomainType &entity) 83static bool matchesFilter(const QHash<QByteArrayList, Query::Comparator> &filter, const ApplicationDomain::ApplicationDomainType &entity)
84{ 84{
85 for (const auto &filterProperty : filter.keys()) { 85 for (const auto &filterProperty : filter.keys()) {
86 if (filterProperty == ApplicationDomain::SinkResource::ResourceType::name) { 86 if (filterProperty[0] == ApplicationDomain::SinkResource::ResourceType::name) {
87 continue; 87 continue;
88 } 88 }
89 if (!filter.value(filterProperty).matches(entity.getProperty(filterProperty))) { 89 if (!filter.value(filterProperty).matches(entity.getProperty(filterProperty[0]))) {
90 return false; 90 return false;
91 } 91 }
92 } 92 }
@@ -432,7 +432,7 @@ KAsync::Job<void> AccountFacade::remove(const Sink::ApplicationDomain::SinkAccou
432 //Remove all identities 432 //Remove all identities
433 job = job.then(Store::fetch<Identity>(Sink::Query{}.filter<Identity::Account>(account))) 433 job = job.then(Store::fetch<Identity>(Sink::Query{}.filter<Identity::Account>(account)))
434 .each([] (const Identity::Ptr &identity) { return Store::remove(*identity); }); 434 .each([] (const Identity::Ptr &identity) { return Store::remove(*identity); });
435 435
436 return job.then(LocalStorageFacade<Sink::ApplicationDomain::SinkAccount>::remove(account)); 436 return job.then(LocalStorageFacade<Sink::ApplicationDomain::SinkAccount>::remove(account));
437} 437}
438 438
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp
index 230dbc7..4fe7e3b 100644
--- a/common/storage/entitystore.cpp
+++ b/common/storage/entitystore.cpp
@@ -443,7 +443,7 @@ QVector<QByteArray> EntityStore::fullScan(const QByteArray &type)
443 return keys.toList().toVector(); 443 return keys.toList().toVector();
444} 444}
445 445
446QVector<QByteArray> EntityStore::indexLookup(const QByteArray &type, const QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting) 446QVector<QByteArray> EntityStore::indexLookup(const QByteArray &type, const QueryBase &query, QSet<QByteArrayList> &appliedFilters, QByteArray &appliedSorting)
447{ 447{
448 if (!d->exists()) { 448 if (!d->exists()) {
449 SinkTraceCtx(d->logCtx) << "Database is not existing: " << type; 449 SinkTraceCtx(d->logCtx) << "Database is not existing: " << type;
diff --git a/common/storage/entitystore.h b/common/storage/entitystore.h
index d79a0b5..ffa70b9 100644
--- a/common/storage/entitystore.h
+++ b/common/storage/entitystore.h
@@ -57,7 +57,7 @@ public:
57 bool hasTransaction() const; 57 bool hasTransaction() const;
58 58
59 QVector<QByteArray> fullScan(const QByteArray &type); 59 QVector<QByteArray> fullScan(const QByteArray &type);
60 QVector<QByteArray> indexLookup(const QByteArray &type, const QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting); 60 QVector<QByteArray> indexLookup(const QByteArray &type, const QueryBase &query, QSet<QByteArrayList> &appliedFilters, QByteArray &appliedSorting);
61 QVector<QByteArray> indexLookup(const QByteArray &type, const QByteArray &property, const QVariant &value); 61 QVector<QByteArray> indexLookup(const QByteArray &type, const QByteArray &property, const QVariant &value);
62 void indexLookup(const QByteArray &type, const QByteArray &property, const QVariant &value, const std::function<void(const QByteArray &uid)> &callback); 62 void indexLookup(const QByteArray &type, const QByteArray &property, const QVariant &value, const std::function<void(const QByteArray &uid)> &callback);
63 template<typename EntityType, typename PropertyType> 63 template<typename EntityType, typename PropertyType>
diff --git a/common/store.cpp b/common/store.cpp
index be2488a..0328c7f 100644
--- a/common/store.cpp
+++ b/common/store.cpp
@@ -117,11 +117,13 @@ QPair<typename AggregatingResultEmitter<typename DomainType::Ptr>::Ptr, typenam
117 117
118 //Filter resources by available content types (unless the query already specifies a capability filter) 118 //Filter resources by available content types (unless the query already specifies a capability filter)
119 auto resourceFilter = query.getResourceFilter(); 119 auto resourceFilter = query.getResourceFilter();
120 if (!resourceFilter.propertyFilter.contains(ApplicationDomain::SinkResource::Capabilities::name)) { 120 if (!resourceFilter.propertyFilter.contains({ApplicationDomain::SinkResource::Capabilities::name})) {
121 resourceFilter.propertyFilter.insert(ApplicationDomain::SinkResource::Capabilities::name, Query::Comparator{ApplicationDomain::getTypeName<DomainType>(), Query::Comparator::Contains}); 121 resourceFilter.propertyFilter.insert({ApplicationDomain::SinkResource::Capabilities::name}, Query::Comparator{ApplicationDomain::getTypeName<DomainType>(), Query::Comparator::Contains});
122 } 122 }
123 resourceQuery.setFilter(resourceFilter); 123 resourceQuery.setFilter(resourceFilter);
124 resourceQuery.requestedProperties << resourceFilter.propertyFilter.keys(); 124 for (auto const &properties : resourceFilter.propertyFilter.keys()) {
125 resourceQuery.requestedProperties << properties;
126 }
125 127
126 auto result = facade->load(resourceQuery, resourceCtx); 128 auto result = facade->load(resourceQuery, resourceCtx);
127 auto emitter = result.second; 129 auto emitter = result.second;
@@ -403,8 +405,8 @@ KAsync::Job<void> Store::synchronize(const Sink::SyncScope &scope)
403{ 405{
404 auto resourceFilter = scope.getResourceFilter(); 406 auto resourceFilter = scope.getResourceFilter();
405 //Filter resources by type by default 407 //Filter resources by type by default
406 if (!resourceFilter.propertyFilter.contains(ApplicationDomain::SinkResource::Capabilities::name) && !scope.type().isEmpty()) { 408 if (!resourceFilter.propertyFilter.contains({ApplicationDomain::SinkResource::Capabilities::name}) && !scope.type().isEmpty()) {
407 resourceFilter.propertyFilter.insert(ApplicationDomain::SinkResource::Capabilities::name, Query::Comparator{scope.type(), Query::Comparator::Contains}); 409 resourceFilter.propertyFilter.insert({ApplicationDomain::SinkResource::Capabilities::name}, Query::Comparator{scope.type(), Query::Comparator::Contains});
408 } 410 }
409 Sink::Query query; 411 Sink::Query query;
410 query.setFilter(resourceFilter); 412 query.setFilter(resourceFilter);
diff --git a/common/typeindex.cpp b/common/typeindex.cpp
index 41821cb..6aa3796 100644
--- a/common/typeindex.cpp
+++ b/common/typeindex.cpp
@@ -53,6 +53,12 @@ static QByteArray getByteArray(const QVariant &value)
53 return "toplevel"; 53 return "toplevel";
54} 54}
55 55
56template <typename T>
57static QByteArray padNumber(T number)
58{
59 static T uint_num_digits = (T)std::log10(std::numeric_limits<T>::max()) + 1;
60 return QByteArray::number(number).rightJustified(uint_num_digits, '0');
61}
56 62
57static QByteArray toSortableByteArrayImpl(const QDateTime &date) 63static QByteArray toSortableByteArrayImpl(const QDateTime &date)
58{ 64{
@@ -60,8 +66,7 @@ static QByteArray toSortableByteArrayImpl(const QDateTime &date)
60 if (!date.isValid()) { 66 if (!date.isValid()) {
61 return QByteArray::number(std::numeric_limits<unsigned int>::max()); 67 return QByteArray::number(std::numeric_limits<unsigned int>::max());
62 } 68 }
63 static unsigned int uint_num_digits = (unsigned int)std::log10(std::numeric_limits<unsigned int>::max()) + 1; 69 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} 70}
66 71
67static QByteArray toSortableByteArray(const QVariant &value) 72static QByteArray toSortableByteArray(const QVariant &value)
@@ -99,6 +104,22 @@ QByteArray TypeIndex::sortedIndexName(const QByteArray &property) const
99 return mType + ".index." + property + ".sorted"; 104 return mType + ".index." + property + ".sorted";
100} 105}
101 106
107QByteArray TypeIndex::sampledPeriodIndexName(const QByteArray &rangeBeginProperty, const QByteArray &rangeEndProperty) const
108{
109 return mType + ".index." + rangeBeginProperty + ".range." + rangeEndProperty;
110}
111
112static unsigned int bucketOf(const QVariant &value)
113{
114 switch (value.type()) {
115 case QMetaType::QDateTime:
116 return value.value<QDateTime>().date().toJulianDay() / 7;
117 default:
118 SinkError() << "Not knowing how to get the bucket of a" << value.typeName();
119 return {};
120 }
121}
122
102template <> 123template <>
103void TypeIndex::addProperty<QByteArray>(const QByteArray &property) 124void TypeIndex::addProperty<QByteArray>(const QByteArray &property)
104{ 125{
@@ -202,6 +223,41 @@ void TypeIndex::addPropertyWithSorting<ApplicationDomain::Reference, QDateTime>(
202 addPropertyWithSorting<QByteArray, QDateTime>(property, sortProperty); 223 addPropertyWithSorting<QByteArray, QDateTime>(property, sortProperty);
203} 224}
204 225
226template <>
227void TypeIndex::addSampledPeriodIndex<QDateTime, QDateTime>(
228 const QByteArray &beginProperty, const QByteArray &endProperty)
229{
230 auto indexer = [=](bool add, const QByteArray &identifier, const QVariant &begin,
231 const QVariant &end, Sink::Storage::DataStore::Transaction &transaction) {
232 SinkTraceCtx(mLogCtx) << "Adding entity to sampled period index";
233 const auto beginDate = begin.toDateTime();
234 const auto endDate = end.toDateTime();
235
236 auto beginBucket = bucketOf(beginDate);
237 auto endBucket = bucketOf(endDate);
238
239 if (beginBucket > endBucket) {
240 SinkError() << "End bucket greater than begin bucket";
241 return;
242 }
243
244 Index index(sampledPeriodIndexName(beginProperty, endProperty), transaction);
245 for (auto bucket = beginBucket; bucket <= endBucket; ++bucket) {
246 QByteArray bucketKey = padNumber(bucket);
247 if (add) {
248 SinkTraceCtx(mLogCtx) << "Adding entity to bucket:" << bucketKey;
249 index.add(bucketKey, identifier);
250 } else {
251 SinkTraceCtx(mLogCtx) << "Removing entity from bucket:" << bucketKey;
252 index.remove(bucketKey, identifier);
253 }
254 }
255 };
256
257 mSampledPeriodProperties.insert({ beginProperty, endProperty });
258 mSampledPeriodIndexer.insert({ beginProperty, endProperty }, indexer);
259}
260
205void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) 261void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId)
206{ 262{
207 for (const auto &property : mProperties) { 263 for (const auto &property : mProperties) {
@@ -209,6 +265,12 @@ void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::
209 auto indexer = mIndexer.value(property); 265 auto indexer = mIndexer.value(property);
210 indexer(add, identifier, value, transaction); 266 indexer(add, identifier, value, transaction);
211 } 267 }
268 for (const auto &properties : mSampledPeriodProperties) {
269 const auto beginValue = entity.getProperty(properties.first);
270 const auto endValue = entity.getProperty(properties.second);
271 auto indexer = mSampledPeriodIndexer.value(properties);
272 indexer(add, identifier, beginValue, endValue, transaction);
273 }
212 for (const auto &property : mSortedProperties) { 274 for (const auto &property : mSortedProperties) {
213 const auto value = entity.getProperty(property); 275 const auto value = entity.getProperty(property);
214 auto indexer = mSortIndexer.value(property); 276 auto indexer = mSortIndexer.value(property);
@@ -312,7 +374,38 @@ static QVector<QByteArray> sortedIndexLookup(Index &index, QueryBase::Comparator
312 return keys; 374 return keys;
313} 375}
314 376
315QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId) 377static QVector<QByteArray> sampledIndexLookup(Index &index, QueryBase::Comparator filter)
378{
379 if (filter.comparator != Query::Comparator::Overlap) {
380 SinkWarning() << "Comparisons other than Overlap not supported on sampled period indexes";
381 return {};
382 }
383
384 QVector<QByteArray> keys;
385
386 auto bounds = filter.value.value<QVariantList>();
387
388 QByteArray lowerBound = toSortableByteArray(bounds[0]);
389 QByteArray upperBound = toSortableByteArray(bounds[1]);
390
391 QByteArray lowerBucket = padNumber(bucketOf(bounds[0]));
392 QByteArray upperBucket = padNumber(bucketOf(bounds[1]));
393
394 SinkTrace() << "Looking up from bucket:" << lowerBucket << "to:" << upperBucket;
395
396 index.rangeLookup(lowerBucket, upperBucket,
397 [&](const QByteArray &value) {
398 keys << value.data();
399 },
400 [bounds](const Index::Error &error) {
401 SinkWarning() << "Lookup error in index:" << error.message
402 << "with bounds:" << bounds[0] << bounds[1];
403 });
404
405 return keys;
406}
407
408QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArrayList> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId)
316{ 409{
317 const auto baseFilters = query.getBaseFilters(); 410 const auto baseFilters = query.getBaseFilters();
318 for (auto it = baseFilters.constBegin(); it != baseFilters.constEnd(); it++) { 411 for (auto it = baseFilters.constBegin(); it != baseFilters.constEnd(); it++) {
@@ -325,11 +418,28 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr
325 } 418 }
326 } 419 }
327 420
421 for (auto it = baseFilters.constBegin(); it != baseFilters.constEnd(); it++) {
422 if (it.value().comparator == QueryBase::Comparator::Overlap) {
423 if (mSampledPeriodProperties.contains({it.key()[0], it.key()[1]})) {
424 Index index(sampledPeriodIndexName(it.key()[0], it.key()[1]), transaction);
425 const auto keys = sampledIndexLookup(index, query.getFilter(it.key()));
426 // The filter is not completely applied, we need post-filtering
427 // in the case the overlap period is not completely aligned
428 // with a week starting on monday
429 //appliedFilters << it.key();
430 SinkTraceCtx(mLogCtx) << "Sampled period index lookup on" << it.key() << "found" << keys.size() << "keys.";
431 return keys;
432 } else {
433 SinkWarning() << "Overlap search without sampled period index";
434 }
435 }
436 }
437
328 for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) { 438 for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) {
329 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) { 439 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) {
330 Index index(indexName(it.key(), it.value()), transaction); 440 Index index(indexName(it.key(), it.value()), transaction);
331 const auto keys = indexLookup(index, query.getFilter(it.key())); 441 const auto keys = indexLookup(index, query.getFilter(it.key()));
332 appliedFilters << it.key(); 442 appliedFilters.insert({it.key()});
333 appliedSorting = it.value(); 443 appliedSorting = it.value();
334 SinkTraceCtx(mLogCtx) << "Grouped sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; 444 SinkTraceCtx(mLogCtx) << "Grouped sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys.";
335 return keys; 445 return keys;
@@ -340,7 +450,7 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr
340 if (query.hasFilter(property)) { 450 if (query.hasFilter(property)) {
341 Index index(sortedIndexName(property), transaction); 451 Index index(sortedIndexName(property), transaction);
342 const auto keys = sortedIndexLookup(index, query.getFilter(property)); 452 const auto keys = sortedIndexLookup(index, query.getFilter(property));
343 appliedFilters << property; 453 appliedFilters.insert({property});
344 SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << property << " found " << keys.size() << " keys."; 454 SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << property << " found " << keys.size() << " keys.";
345 return keys; 455 return keys;
346 } 456 }
@@ -350,7 +460,7 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr
350 if (query.hasFilter(property)) { 460 if (query.hasFilter(property)) {
351 Index index(indexName(property), transaction); 461 Index index(indexName(property), transaction);
352 const auto keys = indexLookup(index, query.getFilter(property)); 462 const auto keys = indexLookup(index, query.getFilter(property));
353 appliedFilters << property; 463 appliedFilters.insert({property});
354 SinkTraceCtx(mLogCtx) << "Index lookup on " << property << " found " << keys.size() << " keys."; 464 SinkTraceCtx(mLogCtx) << "Index lookup on " << property << " found " << keys.size() << " keys.";
355 return keys; 465 return keys;
356 } 466 }
diff --git a/common/typeindex.h b/common/typeindex.h
index 793dc1e..a8c0e10 100644
--- a/common/typeindex.h
+++ b/common/typeindex.h
@@ -73,10 +73,19 @@ public:
73 mCustomIndexer << CustomIndexer::Ptr::create(); 73 mCustomIndexer << CustomIndexer::Ptr::create();
74 } 74 }
75 75
76 template <typename Begin, typename End>
77 void addSampledPeriodIndex(const QByteArray &beginProperty, const QByteArray &endProperty);
78
79 template <typename Begin, typename End>
80 void addSampledPeriodIndex()
81 {
82 addSampledPeriodIndex<typename Begin::Type, typename End::Type>(Begin::name, End::name);
83 }
84
76 void add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId); 85 void add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
77 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId); 86 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
78 87
79 QVector<QByteArray> query(const Sink::QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId); 88 QVector<QByteArray> query(const Sink::QueryBase &query, QSet<QByteArrayList> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
80 QVector<QByteArray> lookup(const QByteArray &property, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction); 89 QVector<QByteArray> lookup(const QByteArray &property, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction);
81 90
82 template <typename Left, typename Right> 91 template <typename Left, typename Right>
@@ -115,6 +124,7 @@ private:
115 void updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId); 124 void updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
116 QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; 125 QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const;
117 QByteArray sortedIndexName(const QByteArray &property) const; 126 QByteArray sortedIndexName(const QByteArray &property) const;
127 QByteArray sampledPeriodIndexName(const QByteArray &rangeBeginProperty, const QByteArray &rangeEndProperty) const;
118 Sink::Log::Context mLogCtx; 128 Sink::Log::Context mLogCtx;
119 QByteArray mType; 129 QByteArray mType;
120 QByteArrayList mProperties; 130 QByteArrayList mProperties;
@@ -122,9 +132,11 @@ private:
122 QMap<QByteArray, QByteArray> mGroupedSortedProperties; 132 QMap<QByteArray, QByteArray> mGroupedSortedProperties;
123 //<Property, ResultProperty> 133 //<Property, ResultProperty>
124 QMap<QByteArray, QByteArray> mSecondaryProperties; 134 QMap<QByteArray, QByteArray> mSecondaryProperties;
135 QSet<QPair<QByteArray, QByteArray>> mSampledPeriodProperties;
125 QList<Sink::Indexer::Ptr> mCustomIndexer; 136 QList<Sink::Indexer::Ptr> mCustomIndexer;
126 Sink::Storage::DataStore::Transaction *mTransaction; 137 Sink::Storage::DataStore::Transaction *mTransaction;
127 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mIndexer; 138 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mIndexer;
128 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mSortIndexer; 139 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mSortIndexer;
129 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction)>> mGroupedSortIndexer; 140 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction)>> mGroupedSortIndexer;
141 QHash<QPair<QByteArray, QByteArray>, std::function<void(bool, const QByteArray &identifier, const QVariant &begin, const QVariant &end, Sink::Storage::DataStore::Transaction &transaction)>> mSampledPeriodIndexer;
130}; 142};