diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-02-20 20:49:17 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-02-20 20:49:17 +0100 |
commit | ed42bdd74d70c7bcb9e1fb8f071ccb92b1515406 (patch) | |
tree | fc068b2f678d3d38d5ad398e6c85474306a0aa02 /common | |
parent | bc06643cd0c16140f6013be35b64732c1676e794 (diff) | |
download | sink-ed42bdd74d70c7bcb9e1fb8f071ccb92b1515406.tar.gz sink-ed42bdd74d70c7bcb9e1fb8f071ccb92b1515406.zip |
Fetch more data on demand
We skip values we've already seen and only retrieve the new ones.
This currently only properly works in a non-live query and we don't
give the model any feedback when we can't fetch more data anymore.
However, it generally works and we get the desired effect.
Diffstat (limited to 'common')
-rw-r--r-- | common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | common/modelresult.cpp | 4 | ||||
-rw-r--r-- | common/queryrunner.cpp | 13 | ||||
-rw-r--r-- | common/resultset.cpp | 134 | ||||
-rw-r--r-- | common/resultset.h | 101 | ||||
-rw-r--r-- | common/store.cpp | 3 | ||||
-rw-r--r-- | common/typeindex.cpp | 4 |
7 files changed, 164 insertions, 96 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index fe72605..0d61d90 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt | |||
@@ -58,6 +58,7 @@ set(command_SRCS | |||
58 | typeindex.cpp | 58 | typeindex.cpp |
59 | resourcefacade.cpp | 59 | resourcefacade.cpp |
60 | resourceconfig.cpp | 60 | resourceconfig.cpp |
61 | resultset.cpp | ||
61 | domain/applicationdomaintype.cpp | 62 | domain/applicationdomaintype.cpp |
62 | domain/event.cpp | 63 | domain/event.cpp |
63 | domain/mail.cpp | 64 | domain/mail.cpp |
diff --git a/common/modelresult.cpp b/common/modelresult.cpp index 5b5a473..ceefa76 100644 --- a/common/modelresult.cpp +++ b/common/modelresult.cpp | |||
@@ -155,7 +155,8 @@ bool ModelResult<T, Ptr>::hasChildren(const QModelIndex &parent) const | |||
155 | template<class T, class Ptr> | 155 | template<class T, class Ptr> |
156 | bool ModelResult<T, Ptr>::canFetchMore(const QModelIndex &parent) const | 156 | bool ModelResult<T, Ptr>::canFetchMore(const QModelIndex &parent) const |
157 | { | 157 | { |
158 | return !mEntityChildrenFetched.contains(parent.internalId()); | 158 | const auto id = parent.internalId(); |
159 | return !mEntityChildrenFetched.contains(id) || mEntityChildrenFetchComplete.contains(id); | ||
159 | } | 160 | } |
160 | 161 | ||
161 | template<class T, class Ptr> | 162 | template<class T, class Ptr> |
@@ -218,6 +219,7 @@ template<class T, class Ptr> | |||
218 | void ModelResult<T, Ptr>::fetchEntities(const QModelIndex &parent) | 219 | void ModelResult<T, Ptr>::fetchEntities(const QModelIndex &parent) |
219 | { | 220 | { |
220 | const auto id = getIdentifier(parent); | 221 | const auto id = getIdentifier(parent); |
222 | mEntityChildrenFetchComplete.remove(id); | ||
221 | mEntityChildrenFetched.insert(id); | 223 | mEntityChildrenFetched.insert(id); |
222 | Trace() << "Loading child entities of parent " << id; | 224 | Trace() << "Loading child entities of parent " << id; |
223 | if (loadEntities) { | 225 | if (loadEntities) { |
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp index 2f627bf..5ac1344 100644 --- a/common/queryrunner.cpp +++ b/common/queryrunner.cpp | |||
@@ -79,9 +79,12 @@ QueryRunner<DomainType>::QueryRunner(const Sink::Query &query, const Sink::Resou | |||
79 | mBatchSize(query.limit) | 79 | mBatchSize(query.limit) |
80 | { | 80 | { |
81 | Trace() << "Starting query"; | 81 | Trace() << "Starting query"; |
82 | if (query.limit && query.sortProperty.isEmpty()) { | ||
83 | Warning() << "A limited query without sorting is typically a bad idea."; | ||
84 | } | ||
82 | //We delegate loading of initial data to the result provider, os it can decide for itself what it needs to load. | 85 | //We delegate loading of initial data to the result provider, os it can decide for itself what it needs to load. |
83 | mResultProvider->setFetcher([=](const typename DomainType::Ptr &parent) { | 86 | mResultProvider->setFetcher([=](const typename DomainType::Ptr &parent) { |
84 | Trace() << "Running fetcher"; | 87 | Trace() << "Running fetcher. Offset: " << mOffset << " Batchsize: " << mBatchSize; |
85 | auto resultProvider = mResultProvider; | 88 | auto resultProvider = mResultProvider; |
86 | async::run<qint64>([=]() -> qint64 { | 89 | async::run<qint64>([=]() -> qint64 { |
87 | QueryWorker<DomainType> worker(query, instanceIdentifier, factory, bufferType, mResultTransformation); | 90 | QueryWorker<DomainType> worker(query, instanceIdentifier, factory, bufferType, mResultTransformation); |
@@ -89,6 +92,7 @@ QueryRunner<DomainType>::QueryRunner(const Sink::Query &query, const Sink::Resou | |||
89 | return newRevision; | 92 | return newRevision; |
90 | }) | 93 | }) |
91 | .template then<void, qint64>([query, this](qint64 newRevision) { | 94 | .template then<void, qint64>([query, this](qint64 newRevision) { |
95 | mOffset += mBatchSize; | ||
92 | //Only send the revision replayed information if we're connected to the resource, there's no need to start the resource otherwise. | 96 | //Only send the revision replayed information if we're connected to the resource, there's no need to start the resource otherwise. |
93 | if (query.liveQuery) { | 97 | if (query.liveQuery) { |
94 | mResourceAccess->sendRevisionReplayedCommand(newRevision); | 98 | mResourceAccess->sendRevisionReplayedCommand(newRevision); |
@@ -325,7 +329,9 @@ ResultSet QueryWorker<DomainType>::filterAndSortSet(ResultSet &resultSet, const | |||
325 | }; | 329 | }; |
326 | 330 | ||
327 | auto skip = [iterator]() { | 331 | auto skip = [iterator]() { |
328 | iterator->next(); | 332 | if (iterator->hasNext()) { |
333 | iterator->next(); | ||
334 | } | ||
329 | }; | 335 | }; |
330 | return ResultSet(generator, skip); | 336 | return ResultSet(generator, skip); |
331 | } else { | 337 | } else { |
@@ -339,8 +345,7 @@ ResultSet QueryWorker<DomainType>::filterAndSortSet(ResultSet &resultSet, const | |||
339 | if ((operation != Sink::Operation_Removal) && filter(domainObject)) { | 345 | if ((operation != Sink::Operation_Removal) && filter(domainObject)) { |
340 | //In the initial set every entity is new | 346 | //In the initial set every entity is new |
341 | callback(domainObject, Sink::Operation_Creation); | 347 | callback(domainObject, Sink::Operation_Creation); |
342 | } | 348 | } } else { |
343 | } else { | ||
344 | //Always remove removals, they probably don't match due to non-available properties | 349 | //Always remove removals, they probably don't match due to non-available properties |
345 | if ((operation == Sink::Operation_Removal) || filter(domainObject)) { | 350 | if ((operation == Sink::Operation_Removal) || filter(domainObject)) { |
346 | //TODO only replay if this is in the currently visible set (or just always replay, worst case we have a couple to many results) | 351 | //TODO only replay if this is in the currently visible set (or just always replay, worst case we have a couple to many results) |
diff --git a/common/resultset.cpp b/common/resultset.cpp new file mode 100644 index 0000000..6e1479a --- /dev/null +++ b/common/resultset.cpp | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Christian Mollekopf <chrigi_1@fastmail.fm> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the | ||
16 | * Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | */ | ||
19 | #include "resultset.h" | ||
20 | |||
21 | #include "common/log.h" | ||
22 | |||
23 | ResultSet::ResultSet() | ||
24 | : mIt(nullptr) | ||
25 | { | ||
26 | |||
27 | } | ||
28 | |||
29 | ResultSet::ResultSet(const ValueGenerator &generator, const SkipValue &skip) | ||
30 | : mIt(nullptr), | ||
31 | mValueGenerator(generator), | ||
32 | mSkip(skip) | ||
33 | { | ||
34 | |||
35 | } | ||
36 | |||
37 | ResultSet::ResultSet(const IdGenerator &generator) | ||
38 | : mIt(nullptr), | ||
39 | mGenerator(generator), | ||
40 | mSkip([this]() { | ||
41 | next(); | ||
42 | }) | ||
43 | { | ||
44 | |||
45 | } | ||
46 | |||
47 | ResultSet::ResultSet(const QVector<QByteArray> &resultSet) | ||
48 | : mResultSet(resultSet), | ||
49 | mIt(mResultSet.constBegin()), | ||
50 | mSkip([this]() { | ||
51 | if (mIt != mResultSet.constEnd()) { | ||
52 | mIt++; | ||
53 | } | ||
54 | }), | ||
55 | mFirst(true) | ||
56 | { | ||
57 | |||
58 | } | ||
59 | |||
60 | ResultSet::ResultSet(const ResultSet &other) | ||
61 | : mResultSet(other.mResultSet), | ||
62 | mIt(nullptr), | ||
63 | mFirst(true) | ||
64 | { | ||
65 | if (other.mValueGenerator) { | ||
66 | mValueGenerator = other.mValueGenerator; | ||
67 | mSkip = other.mSkip; | ||
68 | } else if (other.mGenerator) { | ||
69 | mGenerator = other.mGenerator; | ||
70 | mSkip = [this]() { | ||
71 | next(); | ||
72 | }; | ||
73 | } else { | ||
74 | mResultSet = other.mResultSet; | ||
75 | mIt = mResultSet.constBegin(); | ||
76 | mSkip = [this]() { | ||
77 | if (mIt != mResultSet.constEnd()) { | ||
78 | mIt++; | ||
79 | } | ||
80 | }; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | bool ResultSet::next() | ||
85 | { | ||
86 | if (mIt) { | ||
87 | if (mIt != mResultSet.constEnd() && !mFirst) { | ||
88 | mIt++; | ||
89 | } | ||
90 | mFirst = false; | ||
91 | return mIt != mResultSet.constEnd(); | ||
92 | } else if (mGenerator) { | ||
93 | Q_ASSERT(mGenerator); | ||
94 | mCurrentValue = mGenerator(); | ||
95 | if (!mCurrentValue.isNull()) { | ||
96 | return true; | ||
97 | } | ||
98 | } else { | ||
99 | next([](const Sink::ApplicationDomain::ApplicationDomainType::Ptr &value, Sink::Operation){ return false; }); | ||
100 | } | ||
101 | return false; | ||
102 | } | ||
103 | |||
104 | bool ResultSet::next(std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &value, Sink::Operation)> callback) | ||
105 | { | ||
106 | Q_ASSERT(mValueGenerator); | ||
107 | return mValueGenerator(callback); | ||
108 | } | ||
109 | |||
110 | void ResultSet::skip(int number) | ||
111 | { | ||
112 | Q_ASSERT(mSkip); | ||
113 | for (int i = 0; i < number; i++) { | ||
114 | mSkip(); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | QByteArray ResultSet::id() | ||
119 | { | ||
120 | if (mIt) { | ||
121 | if (mIt == mResultSet.constEnd()) { | ||
122 | return QByteArray(); | ||
123 | } | ||
124 | Q_ASSERT(mIt != mResultSet.constEnd()); | ||
125 | return *mIt; | ||
126 | } else { | ||
127 | return mCurrentValue; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | bool ResultSet::isEmpty() | ||
132 | { | ||
133 | return mResultSet.isEmpty(); | ||
134 | } | ||
diff --git a/common/resultset.h b/common/resultset.h index 8a0720d..e513460 100644 --- a/common/resultset.h +++ b/common/resultset.h | |||
@@ -34,100 +34,20 @@ class ResultSet { | |||
34 | typedef std::function<QByteArray()> IdGenerator; | 34 | typedef std::function<QByteArray()> IdGenerator; |
35 | typedef std::function<void()> SkipValue; | 35 | typedef std::function<void()> SkipValue; |
36 | 36 | ||
37 | ResultSet() | 37 | ResultSet(); |
38 | : mIt(nullptr) | 38 | ResultSet(const ValueGenerator &generator, const SkipValue &skip); |
39 | { | 39 | ResultSet(const IdGenerator &generator); |
40 | ResultSet(const QVector<QByteArray> &resultSet); | ||
41 | ResultSet(const ResultSet &other); | ||
40 | 42 | ||
41 | } | 43 | bool next(); |
44 | bool next(std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &value, Sink::Operation)> callback); | ||
42 | 45 | ||
43 | ResultSet(const ValueGenerator &generator, const SkipValue &skip) | 46 | void skip(int number); |
44 | : mIt(nullptr), | ||
45 | mValueGenerator(generator), | ||
46 | mSkip(skip) | ||
47 | { | ||
48 | 47 | ||
49 | } | 48 | QByteArray id(); |
50 | 49 | ||
51 | ResultSet(const IdGenerator &generator) | 50 | bool isEmpty(); |
52 | : mIt(nullptr), | ||
53 | mGenerator(generator), | ||
54 | mSkip([this]() { | ||
55 | mGenerator(); | ||
56 | }) | ||
57 | { | ||
58 | |||
59 | } | ||
60 | |||
61 | ResultSet(const QVector<QByteArray> &resultSet) | ||
62 | : mResultSet(resultSet), | ||
63 | mIt(nullptr), | ||
64 | mSkip([this]() { | ||
65 | mGenerator(); | ||
66 | }) | ||
67 | { | ||
68 | |||
69 | } | ||
70 | |||
71 | bool next() | ||
72 | { | ||
73 | if (mGenerator) { | ||
74 | mCurrentValue = mGenerator(); | ||
75 | } else { | ||
76 | if (!mIt) { | ||
77 | mIt = mResultSet.constBegin(); | ||
78 | } else { | ||
79 | mIt++; | ||
80 | } | ||
81 | return mIt != mResultSet.constEnd(); | ||
82 | } | ||
83 | if (!mCurrentValue.isNull()) { | ||
84 | return true; | ||
85 | } | ||
86 | return false; | ||
87 | } | ||
88 | |||
89 | bool next(std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &value, Sink::Operation)> callback) | ||
90 | { | ||
91 | Q_ASSERT(mValueGenerator); | ||
92 | return mValueGenerator(callback); | ||
93 | } | ||
94 | |||
95 | bool next(std::function<void(const QByteArray &key)> callback) | ||
96 | { | ||
97 | if (mGenerator) { | ||
98 | mCurrentValue = mGenerator(); | ||
99 | } else { | ||
100 | if (!mIt) { | ||
101 | mIt = mResultSet.constBegin(); | ||
102 | } else { | ||
103 | mIt++; | ||
104 | } | ||
105 | return mIt != mResultSet.constEnd(); | ||
106 | } | ||
107 | return false; | ||
108 | } | ||
109 | |||
110 | void skip(int number) | ||
111 | { | ||
112 | Q_ASSERT(mSkip); | ||
113 | for (int i = 0; i < number; i++) { | ||
114 | mSkip(); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | QByteArray id() | ||
119 | { | ||
120 | if (mIt) { | ||
121 | return *mIt; | ||
122 | } else { | ||
123 | return mCurrentValue; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | bool isEmpty() | ||
128 | { | ||
129 | return mResultSet.isEmpty(); | ||
130 | } | ||
131 | 51 | ||
132 | private: | 52 | private: |
133 | QVector<QByteArray> mResultSet; | 53 | QVector<QByteArray> mResultSet; |
@@ -136,5 +56,6 @@ class ResultSet { | |||
136 | IdGenerator mGenerator; | 56 | IdGenerator mGenerator; |
137 | ValueGenerator mValueGenerator; | 57 | ValueGenerator mValueGenerator; |
138 | SkipValue mSkip; | 58 | SkipValue mSkip; |
59 | bool mFirst; | ||
139 | }; | 60 | }; |
140 | 61 | ||
diff --git a/common/store.cpp b/common/store.cpp index 2f88c6d..6847d22 100644 --- a/common/store.cpp +++ b/common/store.cpp | |||
@@ -75,12 +75,13 @@ static QList<QByteArray> getResources(const QList<QByteArray> &resourceFilter, c | |||
75 | template <class DomainType> | 75 | template <class DomainType> |
76 | QSharedPointer<QAbstractItemModel> Store::loadModel(Query query) | 76 | QSharedPointer<QAbstractItemModel> Store::loadModel(Query query) |
77 | { | 77 | { |
78 | Trace() << "Query: "; | 78 | Trace() << "Query: " << ApplicationDomain::getTypeName<DomainType>(); |
79 | Trace() << " Requested: " << query.requestedProperties; | 79 | Trace() << " Requested: " << query.requestedProperties; |
80 | Trace() << " Filter: " << query.propertyFilter; | 80 | Trace() << " Filter: " << query.propertyFilter; |
81 | Trace() << " Parent: " << query.parentProperty; | 81 | Trace() << " Parent: " << query.parentProperty; |
82 | Trace() << " Ids: " << query.ids; | 82 | Trace() << " Ids: " << query.ids; |
83 | Trace() << " IsLive: " << query.liveQuery; | 83 | Trace() << " IsLive: " << query.liveQuery; |
84 | Trace() << " Sorting: " << query.sortProperty; | ||
84 | auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr> >::create(query, query.requestedProperties); | 85 | auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr> >::create(query, query.requestedProperties); |
85 | 86 | ||
86 | //* Client defines lifetime of model | 87 | //* Client defines lifetime of model |
diff --git a/common/typeindex.cpp b/common/typeindex.cpp index 3ee2492..ddf5df5 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp | |||
@@ -36,6 +36,10 @@ static QByteArray getByteArray(const QVariant &value) | |||
36 | 36 | ||
37 | static QByteArray toSortableByteArray(const QDateTime &date) | 37 | static QByteArray toSortableByteArray(const QDateTime &date) |
38 | { | 38 | { |
39 | //Sort invalid last | ||
40 | if (!date.isValid()) { | ||
41 | return QByteArray::number(std::numeric_limits<unsigned int>::max()); | ||
42 | } | ||
39 | return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()); | 43 | return QByteArray::number(std::numeric_limits<unsigned int>::max() - date.toTime_t()); |
40 | } | 44 | } |
41 | 45 | ||