summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/domain/event.cpp4
-rw-r--r--common/domain/event.h2
-rw-r--r--common/domain/folder.cpp4
-rw-r--r--common/domain/folder.h2
-rw-r--r--common/domain/mail.cpp4
-rw-r--r--common/domain/mail.h2
-rw-r--r--common/domainadaptor.h2
-rw-r--r--common/index.cpp6
-rw-r--r--common/index.h2
-rw-r--r--common/modelresult.cpp20
-rw-r--r--common/pipeline.cpp1
-rw-r--r--common/queryrunner.cpp32
-rw-r--r--common/typeindex.cpp95
-rw-r--r--common/typeindex.h7
14 files changed, 128 insertions, 55 deletions
diff --git a/common/domain/event.cpp b/common/domain/event.cpp
index 4210125..96e2d44 100644
--- a/common/domain/event.cpp
+++ b/common/domain/event.cpp
@@ -50,9 +50,9 @@ static TypeIndex &getIndex()
50 return *index; 50 return *index;
51} 51}
52 52
53ResultSet TypeImplementation<Event>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) 53ResultSet TypeImplementation<Event>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction)
54{ 54{
55 return getIndex().query(query, appliedFilters, transaction); 55 return getIndex().query(query, appliedFilters, appliedSorting, transaction);
56} 56}
57 57
58void TypeImplementation<Event>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) 58void TypeImplementation<Event>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction)
diff --git a/common/domain/event.h b/common/domain/event.h
index 479969d..5315566 100644
--- a/common/domain/event.h
+++ b/common/domain/event.h
@@ -55,7 +55,7 @@ public:
55 * 55 *
56 * An empty result set indicates that a full scan is required. 56 * An empty result set indicates that a full scan is required.
57 */ 57 */
58 static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction); 58 static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction);
59 static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 59 static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
60 static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 60 static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
61 static QSharedPointer<ReadPropertyMapper<Buffer> > initializeReadPropertyMapper(); 61 static QSharedPointer<ReadPropertyMapper<Buffer> > initializeReadPropertyMapper();
diff --git a/common/domain/folder.cpp b/common/domain/folder.cpp
index 16b2ec9..1cb9217 100644
--- a/common/domain/folder.cpp
+++ b/common/domain/folder.cpp
@@ -51,9 +51,9 @@ static TypeIndex &getIndex()
51 return *index; 51 return *index;
52} 52}
53 53
54ResultSet TypeImplementation<Folder>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) 54ResultSet TypeImplementation<Folder>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction)
55{ 55{
56 return getIndex().query(query, appliedFilters, transaction); 56 return getIndex().query(query, appliedFilters, appliedSorting, transaction);
57} 57}
58 58
59void TypeImplementation<Folder>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) 59void TypeImplementation<Folder>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction)
diff --git a/common/domain/folder.h b/common/domain/folder.h
index 40c799c..6e066e1 100644
--- a/common/domain/folder.h
+++ b/common/domain/folder.h
@@ -45,7 +45,7 @@ public:
45 typedef Sink::ApplicationDomain::Buffer::Folder Buffer; 45 typedef Sink::ApplicationDomain::Buffer::Folder Buffer;
46 typedef Sink::ApplicationDomain::Buffer::FolderBuilder BufferBuilder; 46 typedef Sink::ApplicationDomain::Buffer::FolderBuilder BufferBuilder;
47 static QSet<QByteArray> indexedProperties(); 47 static QSet<QByteArray> indexedProperties();
48 static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction); 48 static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction);
49 static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 49 static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
50 static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 50 static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
51 static QSharedPointer<ReadPropertyMapper<Buffer> > initializeReadPropertyMapper(); 51 static QSharedPointer<ReadPropertyMapper<Buffer> > initializeReadPropertyMapper();
diff --git a/common/domain/mail.cpp b/common/domain/mail.cpp
index 518331f..69c351c 100644
--- a/common/domain/mail.cpp
+++ b/common/domain/mail.cpp
@@ -55,9 +55,9 @@ static TypeIndex &getIndex()
55 return *index; 55 return *index;
56} 56}
57 57
58ResultSet TypeImplementation<Mail>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) 58ResultSet TypeImplementation<Mail>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction)
59{ 59{
60 return getIndex().query(query, appliedFilters, transaction); 60 return getIndex().query(query, appliedFilters, appliedSorting, transaction);
61} 61}
62 62
63void TypeImplementation<Mail>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) 63void TypeImplementation<Mail>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction)
diff --git a/common/domain/mail.h b/common/domain/mail.h
index e45d64d..ff169dd 100644
--- a/common/domain/mail.h
+++ b/common/domain/mail.h
@@ -50,7 +50,7 @@ public:
50 * 50 *
51 * An empty result set indicates that a full scan is required. 51 * An empty result set indicates that a full scan is required.
52 */ 52 */
53 static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction); 53 static ResultSet queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction);
54 static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 54 static void index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
55 static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 55 static void removeIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
56 static QSharedPointer<ReadPropertyMapper<Buffer> > initializeReadPropertyMapper(); 56 static QSharedPointer<ReadPropertyMapper<Buffer> > initializeReadPropertyMapper();
diff --git a/common/domainadaptor.h b/common/domainadaptor.h
index 893c8d0..9ce5a8a 100644
--- a/common/domainadaptor.h
+++ b/common/domainadaptor.h
@@ -101,7 +101,7 @@ public:
101 } else if (mLocalBuffer && mLocalMapper->hasMapping(key)) { 101 } else if (mLocalBuffer && mLocalMapper->hasMapping(key)) {
102 return mLocalMapper->getProperty(key, mLocalBuffer); 102 return mLocalMapper->getProperty(key, mLocalBuffer);
103 } 103 }
104 Warning() << "No mapping available for key " << key; 104 Warning() << "No mapping available for key " << key << mLocalBuffer << mResourceBuffer;
105 return QVariant(); 105 return QVariant();
106 } 106 }
107 107
diff --git a/common/index.cpp b/common/index.cpp
index ae56da1..e35b838 100644
--- a/common/index.cpp
+++ b/common/index.cpp
@@ -31,7 +31,7 @@ void Index::remove(const QByteArray &key, const QByteArray &value)
31} 31}
32 32
33void Index::lookup(const QByteArray &key, const std::function<void(const QByteArray &value)> &resultHandler, 33void Index::lookup(const QByteArray &key, const std::function<void(const QByteArray &value)> &resultHandler,
34 const std::function<void(const Error &error)> &errorHandler) 34 const std::function<void(const Error &error)> &errorHandler, bool matchSubStringKeys)
35{ 35{
36 mDb.scan(key, [this, resultHandler](const QByteArray &key, const QByteArray &value) -> bool { 36 mDb.scan(key, [this, resultHandler](const QByteArray &key, const QByteArray &value) -> bool {
37 resultHandler(value); 37 resultHandler(value);
@@ -40,8 +40,8 @@ void Index::lookup(const QByteArray &key, const std::function<void(const QByteAr
40 [errorHandler](const Sink::Storage::Error &error) { 40 [errorHandler](const Sink::Storage::Error &error) {
41 Warning() << "Error while retrieving value" << error.message; 41 Warning() << "Error while retrieving value" << error.message;
42 errorHandler(Error(error.store, error.code, error.message)); 42 errorHandler(Error(error.store, error.code, error.message));
43 } 43 },
44 ); 44 matchSubStringKeys);
45} 45}
46 46
47QByteArray Index::lookup(const QByteArray &key) 47QByteArray Index::lookup(const QByteArray &key)
diff --git a/common/index.h b/common/index.h
index df79fc3..3ee322a 100644
--- a/common/index.h
+++ b/common/index.h
@@ -33,7 +33,7 @@ public:
33 void remove(const QByteArray &key, const QByteArray &value); 33 void remove(const QByteArray &key, const QByteArray &value);
34 34
35 void lookup(const QByteArray &key, const std::function<void(const QByteArray &value)> &resultHandler, 35 void lookup(const QByteArray &key, const std::function<void(const QByteArray &value)> &resultHandler,
36 const std::function<void(const Error &error)> &errorHandler); 36 const std::function<void(const Error &error)> &errorHandler, bool matchSubStringKeys = false);
37 QByteArray lookup(const QByteArray &key); 37 QByteArray lookup(const QByteArray &key);
38 38
39private: 39private:
diff --git a/common/modelresult.cpp b/common/modelresult.cpp
index f28c665..5b5a473 100644
--- a/common/modelresult.cpp
+++ b/common/modelresult.cpp
@@ -27,9 +27,9 @@
27#undef DEBUG_AREA 27#undef DEBUG_AREA
28#define DEBUG_AREA "client.modelresult" 28#define DEBUG_AREA "client.modelresult"
29 29
30static uint qHash(const Sink::ApplicationDomain::ApplicationDomainType &type) 30static uint qHash(const Sink::ApplicationDomain::ApplicationDomainType &type)
31{ 31{
32 Q_ASSERT(!type.resourceInstanceIdentifier().isEmpty()); 32 // Q_ASSERT(!type.resourceInstanceIdentifier().isEmpty());
33 Q_ASSERT(!type.identifier().isEmpty()); 33 Q_ASSERT(!type.identifier().isEmpty());
34 return qHash(type.resourceInstanceIdentifier() + type.identifier()); 34 return qHash(type.resourceInstanceIdentifier() + type.identifier());
35} 35}
@@ -88,18 +88,18 @@ QVariant ModelResult<T, Ptr>::headerData(int section, Qt::Orientation orientatio
88template<class T, class Ptr> 88template<class T, class Ptr>
89QVariant ModelResult<T, Ptr>::data(const QModelIndex &index, int role) const 89QVariant ModelResult<T, Ptr>::data(const QModelIndex &index, int role) const
90{ 90{
91 if (role == DomainObjectRole) { 91 if (role == DomainObjectRole && index.isValid()) {
92 Q_ASSERT(mEntities.contains(index.internalId())); 92 Q_ASSERT(mEntities.contains(index.internalId()));
93 return QVariant::fromValue(mEntities.value(index.internalId())); 93 return QVariant::fromValue(mEntities.value(index.internalId()));
94 } 94 }
95 if (role == DomainObjectBaseRole) { 95 if (role == DomainObjectBaseRole && index.isValid()) {
96 Q_ASSERT(mEntities.contains(index.internalId())); 96 Q_ASSERT(mEntities.contains(index.internalId()));
97 return QVariant::fromValue(mEntities.value(index.internalId()). template staticCast<Sink::ApplicationDomain::ApplicationDomainType>()); 97 return QVariant::fromValue(mEntities.value(index.internalId()). template staticCast<Sink::ApplicationDomain::ApplicationDomainType>());
98 } 98 }
99 if (role == ChildrenFetchedRole) { 99 if (role == ChildrenFetchedRole) {
100 return childrenFetched(index); 100 return childrenFetched(index);
101 } 101 }
102 if (role == Qt::DisplayRole) { 102 if (role == Qt::DisplayRole && index.isValid()) {
103 if (index.column() < mPropertyColumns.size()) { 103 if (index.column() < mPropertyColumns.size()) {
104 Q_ASSERT(mEntities.contains(index.internalId())); 104 Q_ASSERT(mEntities.contains(index.internalId()));
105 auto entity = mEntities.value(index.internalId()); 105 auto entity = mEntities.value(index.internalId());
@@ -115,8 +115,13 @@ template<class T, class Ptr>
115QModelIndex ModelResult<T, Ptr>::index(int row, int column, const QModelIndex &parent) const 115QModelIndex ModelResult<T, Ptr>::index(int row, int column, const QModelIndex &parent) const
116{ 116{
117 const auto id = getIdentifier(parent); 117 const auto id = getIdentifier(parent);
118 const auto childId = mTree.value(id).at(row); 118 const auto list = mTree.value(id);
119 return createIndex(row, column, childId); 119 if (list.size() > row) {
120 const auto childId = list.at(row);
121 return createIndex(row, column, childId);
122 }
123 Warning() << "Index not available " << row << column << parent;
124 return QModelIndex();
120} 125}
121 126
122template<class T, class Ptr> 127template<class T, class Ptr>
@@ -156,6 +161,7 @@ bool ModelResult<T, Ptr>::canFetchMore(const QModelIndex &parent) const
156template<class T, class Ptr> 161template<class T, class Ptr>
157void ModelResult<T, Ptr>::fetchMore(const QModelIndex &parent) 162void ModelResult<T, Ptr>::fetchMore(const QModelIndex &parent)
158{ 163{
164 Trace() << "Fetching more: " << parent;
159 fetchEntities(parent); 165 fetchEntities(parent);
160} 166}
161 167
diff --git a/common/pipeline.cpp b/common/pipeline.cpp
index 93d8236..35e582b 100644
--- a/common/pipeline.cpp
+++ b/common/pipeline.cpp
@@ -209,6 +209,7 @@ KAsync::Job<qint64> Pipeline::newEntity(void const *command, size_t size)
209 Log() << "Pipeline: wrote entity: " << key << newRevision << bufferType; 209 Log() << "Pipeline: wrote entity: " << key << newRevision << bufferType;
210 Storage::mainDatabase(d->transaction, bufferType).scan(Storage::assembleKey(key, newRevision), [this, bufferType, newRevision, adaptorFactory, key](const QByteArray &, const QByteArray &value) -> bool { 210 Storage::mainDatabase(d->transaction, bufferType).scan(Storage::assembleKey(key, newRevision), [this, bufferType, newRevision, adaptorFactory, key](const QByteArray &, const QByteArray &value) -> bool {
211 auto entity = GetEntity(value); 211 auto entity = GetEntity(value);
212 Q_ASSERT(entity->resource() || entity->local());
212 auto adaptor = adaptorFactory->createAdaptor(*entity); 213 auto adaptor = adaptorFactory->createAdaptor(*entity);
213 for (auto processor : d->processors[bufferType]) { 214 for (auto processor : d->processors[bufferType]) {
214 processor->newEntity(key, newRevision, *adaptor, d->transaction); 215 processor->newEntity(key, newRevision, *adaptor, d->transaction);
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp
index 2d188ae..299b987 100644
--- a/common/queryrunner.cpp
+++ b/common/queryrunner.cpp
@@ -54,12 +54,12 @@ private:
54 54
55 void readEntity(const Sink::Storage::NamedDatabase &db, const QByteArray &key, const std::function<void(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &, Sink::Operation)> &resultCallback); 55 void readEntity(const Sink::Storage::NamedDatabase &db, const QByteArray &key, const std::function<void(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &, Sink::Operation)> &resultCallback);
56 56
57 ResultSet loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters); 57 ResultSet loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters, QByteArray &remainingSorting);
58 ResultSet loadIncrementalResultSet(qint64 baseRevision, const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters); 58 ResultSet loadIncrementalResultSet(qint64 baseRevision, const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters);
59 59
60 ResultSet filterAndSortSet(ResultSet &resultSet, const std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Sink::Storage::NamedDatabase &db, bool initialQuery, const QByteArray &sortProperty); 60 ResultSet filterAndSortSet(ResultSet &resultSet, const std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Sink::Storage::NamedDatabase &db, bool initialQuery, const QByteArray &sortProperty);
61 std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> getFilter(const QSet<QByteArray> remainingFilters, const Sink::Query &query); 61 std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> getFilter(const QSet<QByteArray> remainingFilters, const Sink::Query &query);
62 qint64 load(const Sink::Query &query, const std::function<ResultSet(Sink::Storage::Transaction &, QSet<QByteArray> &)> &baseSetRetriever, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider, bool initialQuery, int offset, int batchSize); 62 qint64 load(const Sink::Query &query, const std::function<ResultSet(Sink::Storage::Transaction &, QSet<QByteArray> &, QByteArray &)> &baseSetRetriever, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider, bool initialQuery, int offset, int batchSize);
63 63
64private: 64private:
65 QueryRunnerBase::ResultTransformation mResultTransformation; 65 QueryRunnerBase::ResultTransformation mResultTransformation;
@@ -154,6 +154,7 @@ static inline ResultSet fullScan(const Sink::Storage::Transaction &transaction,
154 Warning() << "Error during query: " << error.message; 154 Warning() << "Error during query: " << error.message;
155 }); 155 });
156 156
157 Trace() << "Full scan retrieved " << keys.size() << " results.";
157 return ResultSet(keys); 158 return ResultSet(keys);
158} 159}
159 160
@@ -207,7 +208,7 @@ void QueryWorker<DomainType>::replaySet(ResultSet &resultSet, Sink::ResultProvid
207 } 208 }
208 return true; 209 return true;
209 })){}; 210 })){};
210 Trace() << "Replayed " << counter << " results"; 211 Trace() << "Replayed " << counter << " results." << "Limit " << batchSize;
211} 212}
212 213
213template<class DomainType> 214template<class DomainType>
@@ -234,14 +235,18 @@ void QueryWorker<DomainType>::readEntity(const Sink::Storage::NamedDatabase &db,
234} 235}
235 236
236template<class DomainType> 237template<class DomainType>
237ResultSet QueryWorker<DomainType>::loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) 238ResultSet QueryWorker<DomainType>::loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters, QByteArray &remainingSorting)
238{ 239{
239 if (!query.ids.isEmpty()) { 240 if (!query.ids.isEmpty()) {
240 return ResultSet(query.ids.toVector()); 241 return ResultSet(query.ids.toVector());
241 } 242 }
242 QSet<QByteArray> appliedFilters; 243 QSet<QByteArray> appliedFilters;
243 auto resultSet = Sink::ApplicationDomain::TypeImplementation<DomainType>::queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, transaction); 244 QByteArray appliedSorting;
245 auto resultSet = Sink::ApplicationDomain::TypeImplementation<DomainType>::queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, appliedSorting, transaction);
244 remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; 246 remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters;
247 if (appliedSorting.isEmpty()) {
248 remainingSorting = query.sortProperty;
249 }
245 250
246 //We do a full scan if there were no indexes available to create the initial set. 251 //We do a full scan if there were no indexes available to create the initial set.
247 if (appliedFilters.isEmpty()) { 252 if (appliedFilters.isEmpty()) {
@@ -284,6 +289,7 @@ ResultSet QueryWorker<DomainType>::filterAndSortSet(ResultSet &resultSet, const
284{ 289{
285 const bool sortingRequired = !sortProperty.isEmpty(); 290 const bool sortingRequired = !sortProperty.isEmpty();
286 if (initialQuery && sortingRequired) { 291 if (initialQuery && sortingRequired) {
292 Trace() << "Sorting the resultset in memory according to property: " << sortProperty;
287 //Sort the complete set by reading the sort property and filling into a sorted map 293 //Sort the complete set by reading the sort property and filling into a sorted map
288 auto sortedMap = QSharedPointer<QMap<QByteArray, QByteArray>>::create(); 294 auto sortedMap = QSharedPointer<QMap<QByteArray, QByteArray>>::create();
289 while (resultSet.next()) { 295 while (resultSet.next()) {
@@ -293,7 +299,7 @@ ResultSet QueryWorker<DomainType>::filterAndSortSet(ResultSet &resultSet, const
293 if ((operation != Sink::Operation_Removal) && filter(domainObject)) { 299 if ((operation != Sink::Operation_Removal) && filter(domainObject)) {
294 if (!sortProperty.isEmpty()) { 300 if (!sortProperty.isEmpty()) {
295 const auto sortValue = domainObject->getProperty(sortProperty); 301 const auto sortValue = domainObject->getProperty(sortProperty);
296 if (sortValue.canConvert<QDateTime>()) { 302 if (sortValue.type() == QVariant::DateTime) {
297 sortedMap->insert(QByteArray::number(std::numeric_limits<unsigned int>::max() - sortValue.toDateTime().toTime_t()), domainObject->identifier()); 303 sortedMap->insert(QByteArray::number(std::numeric_limits<unsigned int>::max() - sortValue.toDateTime().toTime_t()), domainObject->identifier());
298 } else { 304 } else {
299 sortedMap->insert(sortValue.toString().toLatin1(), domainObject->identifier()); 305 sortedMap->insert(sortValue.toString().toLatin1(), domainObject->identifier());
@@ -305,6 +311,7 @@ ResultSet QueryWorker<DomainType>::filterAndSortSet(ResultSet &resultSet, const
305 }); 311 });
306 } 312 }
307 313
314 Trace() << "Sorted " << sortedMap->size() << " values.";
308 auto iterator = QSharedPointer<QMapIterator<QByteArray, QByteArray> >::create(*sortedMap); 315 auto iterator = QSharedPointer<QMapIterator<QByteArray, QByteArray> >::create(*sortedMap);
309 ResultSet::ValueGenerator generator = [this, iterator, sortedMap, &db, filter, initialQuery](std::function<void(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &, Sink::Operation)> callback) -> bool { 316 ResultSet::ValueGenerator generator = [this, iterator, sortedMap, &db, filter, initialQuery](std::function<void(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &, Sink::Operation)> callback) -> bool {
310 if (iterator->hasNext()) { 317 if (iterator->hasNext()) {
@@ -377,7 +384,7 @@ std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &do
377} 384}
378 385
379template<class DomainType> 386template<class DomainType>
380qint64 QueryWorker<DomainType>::load(const Sink::Query &query, const std::function<ResultSet(Sink::Storage::Transaction &, QSet<QByteArray> &)> &baseSetRetriever, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider, bool initialQuery, int offset, int batchSize) 387qint64 QueryWorker<DomainType>::load(const Sink::Query &query, const std::function<ResultSet(Sink::Storage::Transaction &, QSet<QByteArray> &, QByteArray &)> &baseSetRetriever, Sink::ResultProviderInterface<typename DomainType::Ptr> &resultProvider, bool initialQuery, int offset, int batchSize)
381{ 388{
382 QTime time; 389 QTime time;
383 time.start(); 390 time.start();
@@ -390,9 +397,10 @@ qint64 QueryWorker<DomainType>::load(const Sink::Query &query, const std::functi
390 auto db = Storage::mainDatabase(transaction, mBufferType); 397 auto db = Storage::mainDatabase(transaction, mBufferType);
391 398
392 QSet<QByteArray> remainingFilters; 399 QSet<QByteArray> remainingFilters;
393 auto resultSet = baseSetRetriever(transaction, remainingFilters); 400 QByteArray remainingSorting;
401 auto resultSet = baseSetRetriever(transaction, remainingFilters, remainingSorting);
394 Trace() << "Base set retrieved. " << time.elapsed(); 402 Trace() << "Base set retrieved. " << time.elapsed();
395 auto filteredSet = filterAndSortSet(resultSet, getFilter(remainingFilters, query), db, initialQuery, query.sortProperty); 403 auto filteredSet = filterAndSortSet(resultSet, getFilter(remainingFilters, query), db, initialQuery, remainingSorting);
396 Trace() << "Filtered set retrieved. " << time.elapsed(); 404 Trace() << "Filtered set retrieved. " << time.elapsed();
397 replaySet(filteredSet, resultProvider, query.requestedProperties, offset, batchSize); 405 replaySet(filteredSet, resultProvider, query.requestedProperties, offset, batchSize);
398 Trace() << "Filtered set replayed. " << time.elapsed(); 406 Trace() << "Filtered set replayed. " << time.elapsed();
@@ -408,7 +416,7 @@ qint64 QueryWorker<DomainType>::executeIncrementalQuery(const Sink::Query &query
408 416
409 const qint64 baseRevision = resultProvider.revision() + 1; 417 const qint64 baseRevision = resultProvider.revision() + 1;
410 Trace() << "Running incremental query " << baseRevision; 418 Trace() << "Running incremental query " << baseRevision;
411 auto revision = load(query, [&](Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) -> ResultSet { 419 auto revision = load(query, [&](Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters, QByteArray &remainingSorting) -> ResultSet {
412 return loadIncrementalResultSet(baseRevision, query, transaction, remainingFilters); 420 return loadIncrementalResultSet(baseRevision, query, transaction, remainingFilters);
413 }, resultProvider, false, 0, 0); 421 }, resultProvider, false, 0, 0);
414 Trace() << "Incremental query took: " << time.elapsed() << " ms"; 422 Trace() << "Incremental query took: " << time.elapsed() << " ms";
@@ -431,8 +439,8 @@ qint64 QueryWorker<DomainType>::executeInitialQuery(const Sink::Query &query, co
431 modifiedQuery.propertyFilter.insert(query.parentProperty, QVariant()); 439 modifiedQuery.propertyFilter.insert(query.parentProperty, QVariant());
432 } 440 }
433 } 441 }
434 auto revision = load(modifiedQuery, [&](Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) -> ResultSet { 442 auto revision = load(modifiedQuery, [&](Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters, QByteArray &remainingSorting) -> ResultSet {
435 return loadInitialResultSet(modifiedQuery, transaction, remainingFilters); 443 return loadInitialResultSet(modifiedQuery, transaction, remainingFilters, remainingSorting);
436 }, resultProvider, true, offset, batchsize); 444 }, resultProvider, true, offset, batchsize);
437 Trace() << "Initial query took: " << time.elapsed() << " ms"; 445 Trace() << "Initial query took: " << time.elapsed() << " ms";
438 resultProvider.initialResultSetComplete(parent); 446 resultProvider.initialResultSetComplete(parent);
diff --git a/common/typeindex.cpp b/common/typeindex.cpp
index 37c0517..e945dcb 100644
--- a/common/typeindex.cpp
+++ b/common/typeindex.cpp
@@ -25,22 +25,41 @@
25#undef DEBUG_AREA 25#undef DEBUG_AREA
26#define DEBUG_AREA "common.typeindex" 26#define DEBUG_AREA "common.typeindex"
27 27
28static QByteArray getByteArray(const QVariant &value)
29{
30 if (value.isValid() && !value.toByteArray().isEmpty()) {
31 return value.toByteArray();
32 }
33 //LMDB can't handle empty keys, so use something different
34 return "toplevel";
35}
36
37static QByteArray toSortableByteArray(const QDateTime &date)
38{
39 return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t());
40}
41
42
28TypeIndex::TypeIndex(const QByteArray &type) 43TypeIndex::TypeIndex(const QByteArray &type)
29 : mType(type) 44 : mType(type)
30{ 45{
31 46
32} 47}
33 48
49QByteArray TypeIndex::indexName(const QByteArray &property, const QByteArray &sortProperty) const
50{
51 if (sortProperty.isEmpty()) {
52 return mType + ".index." + property;
53 }
54 return mType + ".index." + property + ".sort." + sortProperty;
55}
56
34template<> 57template<>
35void TypeIndex::addProperty<QByteArray>(const QByteArray &property) 58void TypeIndex::addProperty<QByteArray>(const QByteArray &property)
36{ 59{
37 auto indexer = [this, property](const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction) { 60 auto indexer = [this, property](const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction) {
38 // Trace() << "Indexing " << mType + ".index." + property << value.toByteArray(); 61 // Trace() << "Indexing " << mType + ".index." + property << value.toByteArray();
39 if (value.isValid() && !value.toByteArray().isEmpty()) { 62 Index(indexName(property), transaction).add(getByteArray(value), identifier);
40 Index(mType + ".index." + property, transaction).add(value.toByteArray(), identifier);
41 } else {
42 Index(mType + ".index." + property, transaction).add("toplevel", identifier);
43 }
44 }; 63 };
45 mIndexer.insert(property, indexer); 64 mIndexer.insert(property, indexer);
46 mProperties << property; 65 mProperties << property;
@@ -51,11 +70,7 @@ void TypeIndex::addProperty<QString>(const QByteArray &property)
51{ 70{
52 auto indexer = [this, property](const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction) { 71 auto indexer = [this, property](const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction) {
53 // Trace() << "Indexing " << mType + ".index." + property << value.toByteArray(); 72 // Trace() << "Indexing " << mType + ".index." + property << value.toByteArray();
54 if (value.isValid() && !value.toByteArray().isEmpty()) { 73 Index(indexName(property), transaction).add(getByteArray(value), identifier);
55 Index(mType + ".index." + property, transaction).add(value.toByteArray(), identifier);
56 } else {
57 Index(mType + ".index." + property, transaction).add("toplevel", identifier);
58 }
59 }; 74 };
60 mIndexer.insert(property, indexer); 75 mIndexer.insert(property, indexer);
61 mProperties << property; 76 mProperties << property;
@@ -68,13 +83,25 @@ void TypeIndex::addProperty<QDateTime>(const QByteArray &property)
68 const auto date = value.toDateTime(); 83 const auto date = value.toDateTime();
69 // Trace() << "Indexing " << mType + ".index." + property << date.toString(); 84 // Trace() << "Indexing " << mType + ".index." + property << date.toString();
70 if (date.isValid()) { 85 if (date.isValid()) {
71 Index(mType + ".index." + property, transaction).add(date.toString().toLatin1(), identifier); 86 Index(indexName(property), transaction).add(date.toString().toLatin1(), identifier);
72 } 87 }
73 }; 88 };
74 mIndexer.insert(property, indexer); 89 mIndexer.insert(property, indexer);
75 mProperties << property; 90 mProperties << property;
76} 91}
77 92
93template<>
94void TypeIndex::addPropertyWithSorting<QByteArray, QDateTime>(const QByteArray &property, const QByteArray &sortProperty)
95{
96 auto indexer = [=](const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::Transaction &transaction) {
97 const auto date = sortValue.toDateTime();
98 const auto propertyValue = getByteArray(value);
99 Index(indexName(property, sortProperty), transaction).add(propertyValue + toSortableByteArray(date), identifier);
100 };
101 mSortIndexer.insert(property+sortProperty, indexer);
102 mSortedProperties.insert(property, sortProperty);
103}
104
78void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) 105void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction)
79{ 106{
80 for (const auto &property : mProperties) { 107 for (const auto &property : mProperties) {
@@ -82,31 +109,57 @@ void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain:
82 auto indexer = mIndexer.value(property); 109 auto indexer = mIndexer.value(property);
83 indexer(identifier, value, transaction); 110 indexer(identifier, value, transaction);
84 } 111 }
112 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) {
113 const auto value = bufferAdaptor.getProperty(it.key());
114 const auto sortValue = bufferAdaptor.getProperty(it.value());
115 auto indexer = mSortIndexer.value(it.key()+it.value());
116 indexer(identifier, value, sortValue, transaction);
117 }
85} 118}
86 119
87void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) 120void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction)
88{ 121{
89 for (const auto &property : mProperties) { 122 for (const auto &property : mProperties) {
90 const auto value = bufferAdaptor.getProperty(property); 123 const auto value = bufferAdaptor.getProperty(property);
91 if (value.isValid()) { 124 //FIXME don't always convert to byte array
92 //FIXME don't always convert to byte array 125 Index(indexName(property), transaction).remove(getByteArray(value), identifier);
93 Index(mType + ".index." + property, transaction).remove(value.toByteArray(), identifier); 126 }
127 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) {
128 const auto propertyValue = bufferAdaptor.getProperty(it.key());
129 const auto sortValue = bufferAdaptor.getProperty(it.value());
130 if (sortValue.type() == QVariant::DateTime) {
131 Index(indexName(it.key(), it.value()), transaction).remove(propertyValue.toByteArray() + toSortableByteArray(sortValue.toDateTime()), identifier);
94 } else { 132 } else {
95 Index(mType + ".index." + property, transaction).remove("toplevel", identifier); 133 Index(indexName(it.key(), it.value()), transaction).remove(propertyValue.toByteArray() + sortValue.toByteArray(), identifier);
96 } 134 }
97 } 135 }
98} 136}
99 137
100ResultSet TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) 138ResultSet TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction)
101{ 139{
102 QVector<QByteArray> keys; 140 QVector<QByteArray> keys;
141 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) {
142 if (query.propertyFilter.contains(it.key()) && query.sortProperty == it.value()) {
143 Index index(indexName(it.key(), it.value()), transaction);
144 const auto lookupKey = getByteArray(query.propertyFilter.value(it.key()));
145 Trace() << "looking for " << lookupKey;
146 index.lookup(lookupKey, [&](const QByteArray &value) {
147 keys << value;
148 },
149 [it](const Index::Error &error) {
150 Warning() << "Error in index: " << error.message << it.key() << it.value();
151 }, true);
152 appliedFilters << it.key();
153 appliedSorting = it.value();
154 Trace() << "Index lookup on " << it.key() << it.value() << " found " << keys.size() << " keys.";
155 return ResultSet(keys);
156
157 }
158 }
103 for (const auto &property : mProperties) { 159 for (const auto &property : mProperties) {
104 if (query.propertyFilter.contains(property)) { 160 if (query.propertyFilter.contains(property)) {
105 Index index(mType + ".index." + property, transaction); 161 Index index(indexName(property), transaction);
106 auto lookupKey = query.propertyFilter.value(property).toByteArray(); 162 const auto lookupKey = getByteArray(query.propertyFilter.value(property));
107 if (lookupKey.isEmpty()) {
108 lookupKey = "toplevel";
109 }
110 index.lookup(lookupKey, [&](const QByteArray &value) { 163 index.lookup(lookupKey, [&](const QByteArray &value) {
111 keys << value; 164 keys << value;
112 }, 165 },
diff --git a/common/typeindex.h b/common/typeindex.h
index b8a6e39..c19780c 100644
--- a/common/typeindex.h
+++ b/common/typeindex.h
@@ -31,15 +31,20 @@ public:
31 31
32 template<typename T> 32 template<typename T>
33 void addProperty(const QByteArray &property); 33 void addProperty(const QByteArray &property);
34 template<typename T, typename S>
35 void addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty);
34 36
35 void add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 37 void add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
36 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); 38 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction);
37 39
38 ResultSet query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction); 40 ResultSet query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction);
39 41
40private: 42private:
43 QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const;
41 QByteArray mType; 44 QByteArray mType;
42 QByteArrayList mProperties; 45 QByteArrayList mProperties;
46 QMap<QByteArray, QByteArray> mSortedProperties;
43 QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction)> > mIndexer; 47 QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction)> > mIndexer;
48 QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::Transaction &transaction)> > mSortIndexer;
44}; 49};
45 50