diff options
-rw-r--r-- | common/domain/event.cpp | 4 | ||||
-rw-r--r-- | common/domain/event.h | 2 | ||||
-rw-r--r-- | common/domain/folder.cpp | 4 | ||||
-rw-r--r-- | common/domain/folder.h | 2 | ||||
-rw-r--r-- | common/domain/mail.cpp | 4 | ||||
-rw-r--r-- | common/domain/mail.h | 2 | ||||
-rw-r--r-- | common/domainadaptor.h | 2 | ||||
-rw-r--r-- | common/index.cpp | 6 | ||||
-rw-r--r-- | common/index.h | 2 | ||||
-rw-r--r-- | common/modelresult.cpp | 20 | ||||
-rw-r--r-- | common/pipeline.cpp | 1 | ||||
-rw-r--r-- | common/queryrunner.cpp | 32 | ||||
-rw-r--r-- | common/typeindex.cpp | 95 | ||||
-rw-r--r-- | common/typeindex.h | 7 |
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 | ||
53 | ResultSet TypeImplementation<Event>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) | 53 | ResultSet 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 | ||
58 | void TypeImplementation<Event>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) | 58 | void 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 | ||
54 | ResultSet TypeImplementation<Folder>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) | 54 | ResultSet 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 | ||
59 | void TypeImplementation<Folder>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) | 59 | void 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 | ||
58 | ResultSet TypeImplementation<Mail>::queryIndexes(const Sink::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) | 58 | ResultSet 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 | ||
63 | void TypeImplementation<Mail>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) | 63 | void 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 | ||
33 | void Index::lookup(const QByteArray &key, const std::function<void(const QByteArray &value)> &resultHandler, | 33 | void 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 | ||
47 | QByteArray Index::lookup(const QByteArray &key) | 47 | QByteArray 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 | ||
39 | private: | 39 | private: |
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 | ||
30 | static uint qHash(const Sink::ApplicationDomain::ApplicationDomainType &type) | 30 | static 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 | |||
88 | template<class T, class Ptr> | 88 | template<class T, class Ptr> |
89 | QVariant ModelResult<T, Ptr>::data(const QModelIndex &index, int role) const | 89 | QVariant 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> | |||
115 | QModelIndex ModelResult<T, Ptr>::index(int row, int column, const QModelIndex &parent) const | 115 | QModelIndex 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 | ||
122 | template<class T, class Ptr> | 127 | template<class T, class Ptr> |
@@ -156,6 +161,7 @@ bool ModelResult<T, Ptr>::canFetchMore(const QModelIndex &parent) const | |||
156 | template<class T, class Ptr> | 161 | template<class T, class Ptr> |
157 | void ModelResult<T, Ptr>::fetchMore(const QModelIndex &parent) | 162 | void 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 | ||
64 | private: | 64 | private: |
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 | ||
213 | template<class DomainType> | 214 | template<class DomainType> |
@@ -234,14 +235,18 @@ void QueryWorker<DomainType>::readEntity(const Sink::Storage::NamedDatabase &db, | |||
234 | } | 235 | } |
235 | 236 | ||
236 | template<class DomainType> | 237 | template<class DomainType> |
237 | ResultSet QueryWorker<DomainType>::loadInitialResultSet(const Sink::Query &query, Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) | 238 | ResultSet 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 | ||
379 | template<class DomainType> | 386 | template<class DomainType> |
380 | qint64 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) | 387 | qint64 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 | ||
28 | static 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 | |||
37 | static QByteArray toSortableByteArray(const QDateTime &date) | ||
38 | { | ||
39 | return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()); | ||
40 | } | ||
41 | |||
42 | |||
28 | TypeIndex::TypeIndex(const QByteArray &type) | 43 | TypeIndex::TypeIndex(const QByteArray &type) |
29 | : mType(type) | 44 | : mType(type) |
30 | { | 45 | { |
31 | 46 | ||
32 | } | 47 | } |
33 | 48 | ||
49 | QByteArray 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 | |||
34 | template<> | 57 | template<> |
35 | void TypeIndex::addProperty<QByteArray>(const QByteArray &property) | 58 | void 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 | ||
93 | template<> | ||
94 | void 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 | |||
78 | void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) | 105 | void 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 | ||
87 | void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) | 120 | void 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 | ||
100 | ResultSet TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, Sink::Storage::Transaction &transaction) | 138 | ResultSet 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 | ||
40 | private: | 42 | private: |
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 | ||