summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/domain/typeimplementations.cpp5
-rw-r--r--common/domain/typeimplementations_p.h18
-rw-r--r--common/index.cpp14
-rw-r--r--common/index.h4
-rw-r--r--common/query.cpp12
-rw-r--r--common/query.h1
-rw-r--r--common/typeindex.cpp122
-rw-r--r--common/typeindex.h13
-rw-r--r--tests/querytest.cpp107
9 files changed, 275 insertions, 21 deletions
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp
index e65c998..a8f4baf 100644
--- a/common/domain/typeimplementations.cpp
+++ b/common/domain/typeimplementations.cpp
@@ -38,7 +38,7 @@ using namespace Sink::ApplicationDomain;
38 MAPPER.addMapping<ENTITYTYPE::PROPERTY, Sink::ApplicationDomain::Buffer::ENTITYTYPE, Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder>(&Sink::ApplicationDomain::Buffer::ENTITYTYPE::LOWERCASEPROPERTY, &Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder::add_##LOWERCASEPROPERTY); 38 MAPPER.addMapping<ENTITYTYPE::PROPERTY, Sink::ApplicationDomain::Buffer::ENTITYTYPE, Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder>(&Sink::ApplicationDomain::Buffer::ENTITYTYPE::LOWERCASEPROPERTY, &Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder::add_##LOWERCASEPROPERTY);
39 39
40typedef IndexConfig<Mail, 40typedef IndexConfig<Mail,
41 ValueIndex<Mail::Date>, 41 SortedIndex<Mail::Date>,
42 ValueIndex<Mail::Folder>, 42 ValueIndex<Mail::Folder>,
43 ValueIndex<Mail::ParentMessageId>, 43 ValueIndex<Mail::ParentMessageId>,
44 ValueIndex<Mail::MessageId>, 44 ValueIndex<Mail::MessageId>,
@@ -64,7 +64,8 @@ typedef IndexConfig<Addressbook,
64 > AddressbookIndexConfig; 64 > AddressbookIndexConfig;
65 65
66typedef IndexConfig<Event, 66typedef IndexConfig<Event,
67 ValueIndex<Event::Uid> 67 ValueIndex<Event::Uid>,
68 SortedIndex<Event::StartTime>
68 > EventIndexConfig; 69 > EventIndexConfig;
69 70
70typedef IndexConfig<Todo, 71typedef IndexConfig<Todo,
diff --git a/common/domain/typeimplementations_p.h b/common/domain/typeimplementations_p.h
index 6f77a2d..fc08048 100644
--- a/common/domain/typeimplementations_p.h
+++ b/common/domain/typeimplementations_p.h
@@ -62,7 +62,7 @@ public:
62}; 62};
63 63
64 64
65template <typename Property, typename SortProperty> 65template <typename Property, typename SortProperty = void>
66class SortedIndex 66class SortedIndex
67{ 67{
68public: 68public:
@@ -78,6 +78,22 @@ public:
78 } 78 }
79}; 79};
80 80
81template <typename SortProperty>
82class SortedIndex<SortProperty, void>
83{
84public:
85 static void configure(TypeIndex &index)
86 {
87 index.addSortedProperty<SortProperty>();
88 }
89
90 template <typename EntityType>
91 static QMap<QByteArray, int> databases()
92 {
93 return {{QByteArray{EntityType::name} +".index." + SortProperty::name + ".sorted", 1}};
94 }
95};
96
81template <typename Property, typename SecondaryProperty> 97template <typename Property, typename SecondaryProperty>
82class SecondaryIndex 98class SecondaryIndex
83{ 99{
diff --git a/common/index.cpp b/common/index.cpp
index ff87ae2..94b2eea 100644
--- a/common/index.cpp
+++ b/common/index.cpp
@@ -59,3 +59,17 @@ QByteArray Index::lookup(const QByteArray &key)
59 lookup(key, [&](const QByteArray &value) { result = QByteArray(value.constData(), value.size()); }, [](const Index::Error &) { }); 59 lookup(key, [&](const QByteArray &value) { result = QByteArray(value.constData(), value.size()); }, [](const Index::Error &) { });
60 return result; 60 return result;
61} 61}
62
63void Index::rangeLookup(const QByteArray &lowerBound, const QByteArray &upperBound,
64 const std::function<void(const QByteArray &value)> &resultHandler,
65 const std::function<void(const Error &error)> &errorHandler)
66{
67 mDb.findAllInRange(lowerBound, upperBound,
68 [&](const QByteArray &key, const QByteArray &value) {
69 resultHandler(value);
70 },
71 [&](const Sink::Storage::DataStore::Error &error) {
72 SinkWarningCtx(mLogCtx) << "Error while retrieving value:" << error << mName;
73 errorHandler(Error(error.store, error.code, error.message));
74 });
75}
diff --git a/common/index.h b/common/index.h
index f16a426..043cc90 100644
--- a/common/index.h
+++ b/common/index.h
@@ -40,6 +40,10 @@ public:
40 bool matchSubStringKeys = false); 40 bool matchSubStringKeys = false);
41 QByteArray lookup(const QByteArray &key); 41 QByteArray lookup(const QByteArray &key);
42 42
43 void rangeLookup(const QByteArray &lowerBound, const QByteArray &upperBound,
44 const std::function<void(const QByteArray &value)> &resultHandler,
45 const std::function<void(const Error &error)> &errorHandler);
46
43private: 47private:
44 Q_DISABLE_COPY(Index); 48 Q_DISABLE_COPY(Index);
45 Sink::Storage::DataStore::Transaction mTransaction; 49 Sink::Storage::DataStore::Transaction mTransaction;
diff --git a/common/query.cpp b/common/query.cpp
index 5f6d095..404a304 100644
--- a/common/query.cpp
+++ b/common/query.cpp
@@ -132,8 +132,8 @@ bool QueryBase::Filter::operator==(const QueryBase::Filter &other) const
132 132
133bool QueryBase::operator==(const QueryBase &other) const 133bool QueryBase::operator==(const QueryBase &other) const
134{ 134{
135 auto ret = mType == other.mType 135 auto ret = mType == other.mType
136 && mSortProperty == other.mSortProperty 136 && mSortProperty == other.mSortProperty
137 && mBaseFilterStage == other.mBaseFilterStage; 137 && mBaseFilterStage == other.mBaseFilterStage;
138 return ret; 138 return ret;
139} 139}
@@ -171,6 +171,14 @@ bool QueryBase::Comparator::matches(const QVariant &v) const
171 return false; 171 return false;
172 } 172 }
173 return value.value<QByteArrayList>().contains(v.toByteArray()); 173 return value.value<QByteArrayList>().contains(v.toByteArray());
174 case Within: {
175 auto range = value.value<QList<QVariant>>();
176 if (range.size() < 2) {
177 return false;
178 }
179
180 return range[0] <= v && v <= range[1];
181 }
174 case Fulltext: 182 case Fulltext:
175 case Invalid: 183 case Invalid:
176 default: 184 default:
diff --git a/common/query.h b/common/query.h
index f0c1e01..7130116 100644
--- a/common/query.h
+++ b/common/query.h
@@ -36,6 +36,7 @@ public:
36 Equals, 36 Equals,
37 Contains, 37 Contains,
38 In, 38 In,
39 Within,
39 Fulltext 40 Fulltext
40 }; 41 };
41 42
diff --git a/common/typeindex.cpp b/common/typeindex.cpp
index a897ad0..5564144 100644
--- a/common/typeindex.cpp
+++ b/common/typeindex.cpp
@@ -21,9 +21,12 @@
21#include "log.h" 21#include "log.h"
22#include "index.h" 22#include "index.h"
23#include "fulltextindex.h" 23#include "fulltextindex.h"
24
24#include <QDateTime> 25#include <QDateTime>
25#include <QDataStream> 26#include <QDataStream>
26 27
28#include <cmath>
29
27using namespace Sink; 30using namespace Sink;
28 31
29static QByteArray getByteArray(const QVariant &value) 32static QByteArray getByteArray(const QVariant &value)
@@ -50,15 +53,34 @@ static QByteArray getByteArray(const QVariant &value)
50 return "toplevel"; 53 return "toplevel";
51} 54}
52 55
53static QByteArray toSortableByteArray(const QDateTime &date) 56
57static QByteArray toSortableByteArrayImpl(const QDateTime &date)
54{ 58{
55 // Sort invalid last 59 // Sort invalid last
56 if (!date.isValid()) { 60 if (!date.isValid()) {
57 return QByteArray::number(std::numeric_limits<unsigned int>::max()); 61 return QByteArray::number(std::numeric_limits<unsigned int>::max());
58 } 62 }
59 return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()); 63 static unsigned int uint_num_digits = std::log10(std::numeric_limits<unsigned int>::max()) + 1;
64 return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()).rightJustified(uint_num_digits, '0');
60} 65}
61 66
67static QByteArray toSortableByteArray(const QVariant &value)
68{
69 if (!value.isValid()) {
70 // FIXME: we don't know the type, so we don't know what to return
71 // This mean we're fixing every sorted index keys to use unsigned int
72 return QByteArray::number(std::numeric_limits<unsigned int>::max());
73 }
74
75 switch (value.type()) {
76 case QMetaType::QDateTime:
77 return toSortableByteArrayImpl(value.toDateTime());
78 default:
79 SinkWarning() << "Not knowing how to convert a" << value.typeName()
80 << "to a sortable key, falling back to default conversion";
81 return getByteArray(value);
82 }
83}
62 84
63TypeIndex::TypeIndex(const QByteArray &type, const Sink::Log::Context &ctx) : mLogCtx(ctx), mType(type) 85TypeIndex::TypeIndex(const QByteArray &type, const Sink::Log::Context &ctx) : mLogCtx(ctx), mType(type)
64{ 86{
@@ -72,6 +94,11 @@ QByteArray TypeIndex::indexName(const QByteArray &property, const QByteArray &so
72 return mType + ".index." + property + ".sort." + sortProperty; 94 return mType + ".index." + property + ".sort." + sortProperty;
73} 95}
74 96
97QByteArray TypeIndex::sortedIndexName(const QByteArray &property) const
98{
99 return mType + ".index." + property + ".sorted";
100}
101
75template <> 102template <>
76void TypeIndex::addProperty<QByteArray>(const QByteArray &property) 103void TypeIndex::addProperty<QByteArray>(const QByteArray &property)
77{ 104{
@@ -138,6 +165,22 @@ void TypeIndex::addProperty<ApplicationDomain::Reference>(const QByteArray &prop
138} 165}
139 166
140template <> 167template <>
168void TypeIndex::addSortedProperty<QDateTime>(const QByteArray &property)
169{
170 auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value,
171 Sink::Storage::DataStore::Transaction &transaction) {
172 const auto sortableDate = toSortableByteArray(value);
173 if (add) {
174 Index(sortedIndexName(property), transaction).add(sortableDate, identifier);
175 } else {
176 Index(sortedIndexName(property), transaction).remove(sortableDate, identifier);
177 }
178 };
179 mSortIndexer.insert(property, indexer);
180 mSortedProperties << property;
181}
182
183template <>
141void TypeIndex::addPropertyWithSorting<QByteArray, QDateTime>(const QByteArray &property, const QByteArray &sortProperty) 184void TypeIndex::addPropertyWithSorting<QByteArray, QDateTime>(const QByteArray &property, const QByteArray &sortProperty)
142{ 185{
143 auto indexer = [=](bool add, const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction) { 186 auto indexer = [=](bool add, const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction) {
@@ -149,8 +192,8 @@ void TypeIndex::addPropertyWithSorting<QByteArray, QDateTime>(const QByteArray &
149 Index(indexName(property, sortProperty), transaction).remove(propertyValue + toSortableByteArray(date), identifier); 192 Index(indexName(property, sortProperty), transaction).remove(propertyValue + toSortableByteArray(date), identifier);
150 } 193 }
151 }; 194 };
152 mSortIndexer.insert(property + sortProperty, indexer); 195 mGroupedSortIndexer.insert(property + sortProperty, indexer);
153 mSortedProperties.insert(property, sortProperty); 196 mGroupedSortedProperties.insert(property, sortProperty);
154} 197}
155 198
156template <> 199template <>
@@ -166,10 +209,15 @@ void TypeIndex::updateIndex(bool add, const QByteArray &identifier, const Sink::
166 auto indexer = mIndexer.value(property); 209 auto indexer = mIndexer.value(property);
167 indexer(add, identifier, value, transaction); 210 indexer(add, identifier, value, transaction);
168 } 211 }
169 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { 212 for (const auto &property : mSortedProperties) {
213 const auto value = entity.getProperty(property);
214 auto indexer = mSortIndexer.value(property);
215 indexer(add, identifier, value, transaction);
216 }
217 for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) {
170 const auto value = entity.getProperty(it.key()); 218 const auto value = entity.getProperty(it.key());
171 const auto sortValue = entity.getProperty(it.value()); 219 const auto sortValue = entity.getProperty(it.value());
172 auto indexer = mSortIndexer.value(it.key() + it.value()); 220 auto indexer = mGroupedSortIndexer.value(it.key() + it.value());
173 indexer(add, identifier, value, sortValue, transaction); 221 indexer(add, identifier, value, sortValue, transaction);
174 } 222 }
175 for (const auto &indexer : mCustomIndexer) { 223 for (const auto &indexer : mCustomIndexer) {
@@ -207,22 +255,61 @@ void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDoma
207 updateIndex(false, identifier, entity, transaction, resourceInstanceId); 255 updateIndex(false, identifier, entity, transaction, resourceInstanceId);
208} 256}
209 257
210static QVector<QByteArray> indexLookup(Index &index, QueryBase::Comparator filter) 258static QVector<QByteArray> indexLookup(Index &index, QueryBase::Comparator filter,
259 std::function<QByteArray(const QVariant &)> valueToKey = getByteArray)
211{ 260{
212 QVector<QByteArray> keys; 261 QVector<QByteArray> keys;
213 QByteArrayList lookupKeys; 262 QByteArrayList lookupKeys;
214 if (filter.comparator == Query::Comparator::Equals) { 263 if (filter.comparator == Query::Comparator::Equals) {
215 lookupKeys << getByteArray(filter.value); 264 lookupKeys << valueToKey(filter.value);
216 } else if (filter.comparator == Query::Comparator::In) { 265 } else if (filter.comparator == Query::Comparator::In) {
217 lookupKeys = filter.value.value<QByteArrayList>(); 266 for(const QVariant &value : filter.value.value<QVariantList>()) {
267 lookupKeys << valueToKey(value);
268 }
269 //lookupKeys = filter.value.value<QByteArrayList>();
218 } else { 270 } else {
219 Q_ASSERT(false); 271 Q_ASSERT(false);
220 } 272 }
221 273
222 for (const auto &lookupKey : lookupKeys) { 274 for (const auto &lookupKey : lookupKeys) {
223 index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; }, 275 index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; },
224 [lookupKey](const Index::Error &error) { SinkWarning() << "Lookup error in index: " << error.message << lookupKey; }, true); 276 [lookupKey](const Index::Error &error) {
277 SinkWarning() << "Lookup error in index: " << error.message << lookupKey;
278 },
279 true);
280 }
281 return keys;
282}
283
284static QVector<QByteArray> sortedIndexLookup(Index &index, QueryBase::Comparator filter)
285{
286 if (filter.comparator == Query::Comparator::In || filter.comparator == Query::Comparator::Contains) {
287 SinkWarning() << "In and Contains comparison not supported on sorted indexes";
288 }
289
290 if (filter.comparator != Query::Comparator::Within) {
291 return indexLookup(index, filter, toSortableByteArray);
292 }
293
294 QVector<QByteArray> keys;
295
296 QByteArray lowerBound, upperBound;
297 auto bounds = filter.value.value<QVariantList>();
298 if (bounds[0].canConvert<QDateTime>()) {
299 // Inverse the bounds because dates are stored newest first
300 upperBound = toSortableByteArray(bounds[0].toDateTime());
301 lowerBound = toSortableByteArray(bounds[1].toDateTime());
302 } else {
303 lowerBound = bounds[0].toByteArray();
304 upperBound = bounds[1].toByteArray();
225 } 305 }
306
307 index.rangeLookup(lowerBound, upperBound, [&](const QByteArray &value) { keys << value; },
308 [bounds](const Index::Error &error) {
309 SinkWarning() << "Lookup error in index:" << error.message
310 << "with bounds:" << bounds[0] << bounds[1];
311 });
312
226 return keys; 313 return keys;
227} 314}
228 315
@@ -239,16 +326,27 @@ QVector<QByteArray> TypeIndex::query(const Sink::QueryBase &query, QSet<QByteArr
239 } 326 }
240 } 327 }
241 328
242 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { 329 for (auto it = mGroupedSortedProperties.constBegin(); it != mGroupedSortedProperties.constEnd(); it++) {
243 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) { 330 if (query.hasFilter(it.key()) && query.sortProperty() == it.value()) {
244 Index index(indexName(it.key(), it.value()), transaction); 331 Index index(indexName(it.key(), it.value()), transaction);
245 const auto keys = indexLookup(index, query.getFilter(it.key())); 332 const auto keys = indexLookup(index, query.getFilter(it.key()));
246 appliedFilters << it.key(); 333 appliedFilters << it.key();
247 appliedSorting = it.value(); 334 appliedSorting = it.value();
248 SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys."; 335 SinkTraceCtx(mLogCtx) << "Grouped sorted index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys.";
249 return keys; 336 return keys;
250 } 337 }
251 } 338 }
339
340 for (const auto &property : mSortedProperties) {
341 if (query.hasFilter(property)) {
342 Index index(sortedIndexName(property), transaction);
343 const auto keys = sortedIndexLookup(index, query.getFilter(property));
344 appliedFilters << property;
345 SinkTraceCtx(mLogCtx) << "Sorted index lookup on " << property << " found " << keys.size() << " keys.";
346 return keys;
347 }
348 }
349
252 for (const auto &property : mProperties) { 350 for (const auto &property : mProperties) {
253 if (query.hasFilter(property)) { 351 if (query.hasFilter(property)) {
254 Index index(indexName(property), transaction); 352 Index index(indexName(property), transaction);
diff --git a/common/typeindex.h b/common/typeindex.h
index b8b4d52..793dc1e 100644
--- a/common/typeindex.h
+++ b/common/typeindex.h
@@ -38,6 +38,8 @@ public:
38 38
39 template <typename T> 39 template <typename T>
40 void addProperty(const QByteArray &property); 40 void addProperty(const QByteArray &property);
41 template <typename T>
42 void addSortedProperty(const QByteArray &property);
41 template <typename T, typename S> 43 template <typename T, typename S>
42 void addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty); 44 void addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty);
43 45
@@ -54,9 +56,9 @@ public:
54 } 56 }
55 57
56 template <typename T> 58 template <typename T>
57 void addPropertyWithSorting() 59 void addSortedProperty()
58 { 60 {
59 addPropertyWithSorting<typename T::Type>(T::name); 61 addSortedProperty<typename T::Type>(T::name);
60 } 62 }
61 63
62 template <typename Left, typename Right> 64 template <typename Left, typename Right>
@@ -112,14 +114,17 @@ private:
112 friend class Sink::Storage::EntityStore; 114 friend class Sink::Storage::EntityStore;
113 void updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId); 115 void updateIndex(bool add, const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction, const QByteArray &resourceInstanceId);
114 QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; 116 QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const;
117 QByteArray sortedIndexName(const QByteArray &property) const;
115 Sink::Log::Context mLogCtx; 118 Sink::Log::Context mLogCtx;
116 QByteArray mType; 119 QByteArray mType;
117 QByteArrayList mProperties; 120 QByteArrayList mProperties;
118 QMap<QByteArray, QByteArray> mSortedProperties; 121 QByteArrayList mSortedProperties;
122 QMap<QByteArray, QByteArray> mGroupedSortedProperties;
119 //<Property, ResultProperty> 123 //<Property, ResultProperty>
120 QMap<QByteArray, QByteArray> mSecondaryProperties; 124 QMap<QByteArray, QByteArray> mSecondaryProperties;
121 QList<Sink::Indexer::Ptr> mCustomIndexer; 125 QList<Sink::Indexer::Ptr> mCustomIndexer;
122 Sink::Storage::DataStore::Transaction *mTransaction; 126 Sink::Storage::DataStore::Transaction *mTransaction;
123 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mIndexer; 127 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mIndexer;
124 QHash<QByteArray, std::function<void(bool, const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction)>> mSortIndexer; 128 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;
125}; 130};
diff --git a/tests/querytest.cpp b/tests/querytest.cpp
index fa016a2..36b6e90 100644
--- a/tests/querytest.cpp
+++ b/tests/querytest.cpp
@@ -1510,6 +1510,113 @@ private slots:
1510 } 1510 }
1511 } 1511 }
1512 1512
1513 void mailsWithDates()
1514 {
1515 {
1516 Mail mail("sink.dummy.instance1");
1517 mail.setExtractedDate(QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate));
1518 mail.setExtractedMessageId("message1");
1519 VERIFYEXEC(Sink::Store::create<Mail>(mail));
1520 }
1521 {
1522 Mail mail("sink.dummy.instance1");
1523 mail.setExtractedDate(QDateTime::fromString("2018-05-23T13:50:00Z", Qt::ISODate));
1524 mail.setExtractedMessageId("message2");
1525 VERIFYEXEC(Sink::Store::create<Mail>(mail));
1526 }
1527 {
1528 Mail mail("sink.dummy.instance1");
1529 mail.setExtractedDate(QDateTime::fromString("2018-05-27T13:50:00Z", Qt::ISODate));
1530 mail.setExtractedMessageId("message3");
1531 VERIFYEXEC(Sink::Store::create<Mail>(mail));
1532 }
1533 {
1534 Mail mail("sink.dummy.instance1");
1535 mail.setExtractedMessageId("message4");
1536 VERIFYEXEC(Sink::Store::create<Mail>(mail));
1537 }
1538 {
1539 Mail mail("sink.dummy.instance1");
1540 mail.setExtractedDate(QDateTime::fromString("2078-05-23T13:49:41Z", Qt::ISODate));
1541 mail.setExtractedMessageId("message5");
1542 VERIFYEXEC(Sink::Store::create<Mail>(mail));
1543 }
1544 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1"));
1545 }
1546
1547 void testMailDate()
1548 {
1549 mailsWithDates();
1550
1551 {
1552 Sink::Query query;
1553 query.resourceFilter("sink.dummy.instance1");
1554 query.filter<Mail::Date>(QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate));
1555 auto model = Sink::Store::loadModel<Mail>(query);
1556 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1557 QCOMPARE(model->rowCount(), 1);
1558 }
1559
1560 {
1561 Sink::Query query;
1562 query.resourceFilter("sink.dummy.instance1");
1563 query.filter<Mail::Date>(QDateTime::fromString("2018-05-27T13:49:41Z", Qt::ISODate));
1564 auto model = Sink::Store::loadModel<Mail>(query);
1565 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1566 QCOMPARE(model->rowCount(), 0);
1567 }
1568
1569 {
1570 Sink::Query query;
1571 query.resourceFilter("sink.dummy.instance1");
1572 query.filter<Mail::Date>(QDateTime::fromString("2018-05-27T13:50:00Z", Qt::ISODate));
1573 auto model = Sink::Store::loadModel<Mail>(query);
1574 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1575 QCOMPARE(model->rowCount(), 1);
1576 }
1577
1578 }
1579
1580 void testMailRange()
1581 {
1582 mailsWithDates();
1583
1584 {
1585 Sink::Query query;
1586 query.resourceFilter("sink.dummy.instance1");
1587 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate), QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within));
1588 auto model = Sink::Store::loadModel<Mail>(query);
1589 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1590 QCOMPARE(model->rowCount(), 1);
1591 }
1592
1593 {
1594 Sink::Query query;
1595 query.resourceFilter("sink.dummy.instance1");
1596 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-22T13:49:41Z", Qt::ISODate), QDateTime::fromString("2018-05-25T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within));
1597 auto model = Sink::Store::loadModel<Mail>(query);
1598 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1599 QCOMPARE(model->rowCount(), 2);
1600 }
1601
1602 {
1603 Sink::Query query;
1604 query.resourceFilter("sink.dummy.instance1");
1605 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-22T13:49:41Z", Qt::ISODate), QDateTime::fromString("2018-05-30T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within));
1606 auto model = Sink::Store::loadModel<Mail>(query);
1607 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1608 QCOMPARE(model->rowCount(), 3);
1609 }
1610
1611 {
1612 Sink::Query query;
1613 query.resourceFilter("sink.dummy.instance1");
1614 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-22T13:49:41Z", Qt::ISODate), QDateTime::fromString("2118-05-30T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within));
1615 auto model = Sink::Store::loadModel<Mail>(query);
1616 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool());
1617 QCOMPARE(model->rowCount(), 4);
1618 }
1619 }
1513}; 1620};
1514 1621
1515QTEST_MAIN(QueryTest) 1622QTEST_MAIN(QueryTest)