diff options
-rw-r--r-- | common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | common/datastorequery.cpp | 17 | ||||
-rw-r--r-- | common/domain/typeimplementations.cpp | 3 | ||||
-rw-r--r-- | common/domain/typeimplementations_p.h | 20 | ||||
-rw-r--r-- | common/query.cpp | 13 | ||||
-rw-r--r-- | common/query.h | 44 | ||||
-rw-r--r-- | common/resourcefacade.cpp | 8 | ||||
-rw-r--r-- | common/sample.fbs | 9 | ||||
-rw-r--r-- | common/storage/entitystore.cpp | 2 | ||||
-rw-r--r-- | common/storage/entitystore.h | 2 | ||||
-rw-r--r-- | common/store.cpp | 12 | ||||
-rw-r--r-- | common/typeindex.cpp | 140 | ||||
-rw-r--r-- | common/typeindex.h | 14 | ||||
-rw-r--r-- | tests/querytest.cpp | 121 |
14 files changed, 372 insertions, 34 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 970990f..aa400e1 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt | |||
@@ -109,6 +109,7 @@ generate_flatbuffers( | |||
109 | domain/folder | 109 | domain/folder |
110 | domain/dummy | 110 | domain/dummy |
111 | entity | 111 | entity |
112 | sample | ||
112 | metadata | 113 | metadata |
113 | queuedcommand | 114 | queuedcommand |
114 | ) | 115 | ) |
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp index f1b0ed2..f3343f3 100644 --- a/common/datastorequery.cpp +++ b/common/datastorequery.cpp | |||
@@ -119,7 +119,7 @@ class Filter : public FilterBase { | |||
119 | public: | 119 | public: |
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) { |
@@ -415,7 +424,7 @@ public: | |||
415 | })) | 424 | })) |
416 | {} | 425 | {} |
417 | mBloomed = true; | 426 | mBloomed = true; |
418 | propertyFilter.insert(mBloomProperty, mBloomValue); | 427 | propertyFilter.insert({mBloomProperty}, mBloomValue); |
419 | return foundValue; | 428 | return foundValue; |
420 | } else { | 429 | } else { |
421 | //Filter on bloom value | 430 | //Filter on bloom value |
@@ -593,7 +602,7 @@ void DataStoreQuery::setupQuery(const Sink::QueryBase &query_) | |||
593 | //We have a set of ids as a starting point | 602 | //We have a set of ids as a starting point |
594 | return Source::Ptr::create(query.ids().toVector(), this); | 603 | return Source::Ptr::create(query.ids().toVector(), this); |
595 | } else { | 604 | } else { |
596 | QSet<QByteArray> appliedFilters; | 605 | QSet<QByteArrayList> appliedFilters; |
597 | auto resultSet = mStore.indexLookup(mType, query, appliedFilters, appliedSorting); | 606 | auto resultSet = mStore.indexLookup(mType, query, appliedFilters, appliedSorting); |
598 | if (!appliedFilters.isEmpty()) { | 607 | if (!appliedFilters.isEmpty()) { |
599 | //We have an index lookup as starting point | 608 | //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 | ||
66 | typedef IndexConfig<Event, | 66 | typedef 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 | ||
71 | typedef IndexConfig<Todo, | 72 | typedef 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 | ||
129 | template <typename RangeBeginProperty, typename RangeEndProperty> | ||
130 | class 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 | |||
136 | public: | ||
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 | |||
129 | template <typename EntityType, typename ... Indexes> | 149 | template <typename EntityType, typename ... Indexes> |
130 | class IndexConfig | 150 | class 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 | ||
83 | static bool matchesFilter(const QHash<QByteArray, Query::Comparator> &filter, const ApplicationDomain::ApplicationDomainType &entity) | 83 | static 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/sample.fbs b/common/sample.fbs new file mode 100644 index 0000000..dbd0e03 --- /dev/null +++ b/common/sample.fbs | |||
@@ -0,0 +1,9 @@ | |||
1 | namespace Sink; | ||
2 | |||
3 | table Sample { | ||
4 | periodStart: string; | ||
5 | periodEnd: string; | ||
6 | reference: string; | ||
7 | } | ||
8 | |||
9 | root_type Sample; | ||
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 | ||
446 | QVector<QByteArray> EntityStore::indexLookup(const QByteArray &type, const QueryBase &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting) | 446 | QVector<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 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 | } |
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 | }; |
diff --git a/tests/querytest.cpp b/tests/querytest.cpp index 36b6e90..ca49021 100644 --- a/tests/querytest.cpp +++ b/tests/querytest.cpp | |||
@@ -59,7 +59,7 @@ private slots: | |||
59 | 59 | ||
60 | Sink::QueryBase::Filter filter; | 60 | Sink::QueryBase::Filter filter; |
61 | filter.ids << "id"; | 61 | filter.ids << "id"; |
62 | filter.propertyFilter.insert("foo", QVariant::fromValue(QByteArray("bar"))); | 62 | filter.propertyFilter.insert({"foo"}, QVariant::fromValue(QByteArray("bar"))); |
63 | 63 | ||
64 | Sink::Query query; | 64 | Sink::Query query; |
65 | query.setFilter(filter); | 65 | query.setFilter(filter); |
@@ -1617,6 +1617,125 @@ private slots: | |||
1617 | QCOMPARE(model->rowCount(), 4); | 1617 | QCOMPARE(model->rowCount(), 4); |
1618 | } | 1618 | } |
1619 | } | 1619 | } |
1620 | |||
1621 | void eventsWithDates() | ||
1622 | { | ||
1623 | { | ||
1624 | Event event("sink.dummy.instance1"); | ||
1625 | event.setExtractedStartTime(QDateTime::fromString("2018-05-23T12:00:00Z", Qt::ISODate)); | ||
1626 | event.setExtractedEndTime(QDateTime::fromString("2018-05-23T13:00:00Z", Qt::ISODate)); | ||
1627 | VERIFYEXEC(Sink::Store::create<Event>(event)); | ||
1628 | } | ||
1629 | { | ||
1630 | Event event("sink.dummy.instance1"); | ||
1631 | event.setExtractedStartTime(QDateTime::fromString("2018-05-23T13:00:00Z", Qt::ISODate)); | ||
1632 | event.setExtractedEndTime(QDateTime::fromString("2018-05-23T14:00:00Z", Qt::ISODate)); | ||
1633 | VERIFYEXEC(Sink::Store::create<Event>(event)); | ||
1634 | } | ||
1635 | { | ||
1636 | Event event("sink.dummy.instance1"); | ||
1637 | event.setExtractedStartTime(QDateTime::fromString("2018-05-23T14:00:00Z", Qt::ISODate)); | ||
1638 | event.setExtractedEndTime(QDateTime::fromString("2018-05-23T15:00:00Z", Qt::ISODate)); | ||
1639 | VERIFYEXEC(Sink::Store::create<Event>(event)); | ||
1640 | } | ||
1641 | { | ||
1642 | Event event("sink.dummy.instance1"); | ||
1643 | event.setExtractedStartTime(QDateTime::fromString("2018-05-23T12:00:00Z", Qt::ISODate)); | ||
1644 | event.setExtractedEndTime(QDateTime::fromString("2018-05-23T14:00:00Z", Qt::ISODate)); | ||
1645 | VERIFYEXEC(Sink::Store::create<Event>(event)); | ||
1646 | } | ||
1647 | { | ||
1648 | Event event("sink.dummy.instance1"); | ||
1649 | event.setExtractedStartTime(QDateTime::fromString("2018-05-24T12:00:00Z", Qt::ISODate)); | ||
1650 | event.setExtractedEndTime(QDateTime::fromString("2018-05-24T14:00:00Z", Qt::ISODate)); | ||
1651 | VERIFYEXEC(Sink::Store::create<Event>(event)); | ||
1652 | } | ||
1653 | { | ||
1654 | Event event("sink.dummy.instance1"); | ||
1655 | VERIFYEXEC(Sink::Store::create<Event>(event)); | ||
1656 | } | ||
1657 | |||
1658 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); | ||
1659 | } | ||
1660 | |||
1661 | void testOverlap() | ||
1662 | { | ||
1663 | eventsWithDates(); | ||
1664 | |||
1665 | { | ||
1666 | Sink::Query query; | ||
1667 | query.resourceFilter("sink.dummy.instance1"); | ||
1668 | query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( | ||
1669 | QVariantList{ QDateTime::fromString("2018-05-22T12:00:00Z", Qt::ISODate), | ||
1670 | QDateTime::fromString("2018-05-30T13:00:00Z", Qt::ISODate) }, | ||
1671 | QueryBase::Comparator::Overlap)); | ||
1672 | auto model = Sink::Store::loadModel<Event>(query); | ||
1673 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
1674 | QCOMPARE(model->rowCount(), 5); | ||
1675 | } | ||
1676 | |||
1677 | { | ||
1678 | Sink::Query query; | ||
1679 | query.resourceFilter("sink.dummy.instance1"); | ||
1680 | query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( | ||
1681 | QVariantList{ QDateTime::fromString("2018-05-22T12:30:00Z", Qt::ISODate), | ||
1682 | QDateTime::fromString("2018-05-22T12:31:00Z", Qt::ISODate) }, | ||
1683 | QueryBase::Comparator::Overlap)); | ||
1684 | auto model = Sink::Store::loadModel<Event>(query); | ||
1685 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
1686 | QCOMPARE(model->rowCount(), 0); | ||
1687 | } | ||
1688 | |||
1689 | { | ||
1690 | Sink::Query query; | ||
1691 | query.resourceFilter("sink.dummy.instance1"); | ||
1692 | query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( | ||
1693 | QVariantList{ QDateTime::fromString("2018-05-24T10:00:00Z", Qt::ISODate), | ||
1694 | QDateTime::fromString("2018-05-24T11:00:00Z", Qt::ISODate) }, | ||
1695 | QueryBase::Comparator::Overlap)); | ||
1696 | auto model = Sink::Store::loadModel<Event>(query); | ||
1697 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
1698 | QCOMPARE(model->rowCount(), 0); | ||
1699 | } | ||
1700 | |||
1701 | { | ||
1702 | Sink::Query query; | ||
1703 | query.resourceFilter("sink.dummy.instance1"); | ||
1704 | query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( | ||
1705 | QVariantList{ QDateTime::fromString("2018-05-23T12:30:00Z", Qt::ISODate), | ||
1706 | QDateTime::fromString("2018-05-23T12:31:00Z", Qt::ISODate) }, | ||
1707 | QueryBase::Comparator::Overlap)); | ||
1708 | auto model = Sink::Store::loadModel<Event>(query); | ||
1709 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
1710 | QCOMPARE(model->rowCount(), 2); | ||
1711 | } | ||
1712 | |||
1713 | { | ||
1714 | Sink::Query query; | ||
1715 | query.resourceFilter("sink.dummy.instance1"); | ||
1716 | query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( | ||
1717 | QVariantList{ QDateTime::fromString("2018-05-22T12:30:00Z", Qt::ISODate), | ||
1718 | QDateTime::fromString("2018-05-23T12:00:00Z", Qt::ISODate) }, | ||
1719 | QueryBase::Comparator::Overlap)); | ||
1720 | auto model = Sink::Store::loadModel<Event>(query); | ||
1721 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
1722 | QCOMPARE(model->rowCount(), 2); | ||
1723 | } | ||
1724 | |||
1725 | { | ||
1726 | Sink::Query query; | ||
1727 | query.resourceFilter("sink.dummy.instance1"); | ||
1728 | query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( | ||
1729 | QVariantList{ QDateTime::fromString("2018-05-23T14:30:00Z", Qt::ISODate), | ||
1730 | QDateTime::fromString("2018-05-23T16:00:00Z", Qt::ISODate) }, | ||
1731 | QueryBase::Comparator::Overlap)); | ||
1732 | auto model = Sink::Store::loadModel<Event>(query); | ||
1733 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
1734 | QCOMPARE(model->rowCount(), 1); | ||
1735 | } | ||
1736 | |||
1737 | } | ||
1738 | |||
1620 | }; | 1739 | }; |
1621 | 1740 | ||
1622 | QTEST_MAIN(QueryTest) | 1741 | QTEST_MAIN(QueryTest) |