summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-02-20 20:49:17 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-02-20 20:49:17 +0100
commited42bdd74d70c7bcb9e1fb8f071ccb92b1515406 (patch)
treefc068b2f678d3d38d5ad398e6c85474306a0aa02 /common
parentbc06643cd0c16140f6013be35b64732c1676e794 (diff)
downloadsink-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.txt1
-rw-r--r--common/modelresult.cpp4
-rw-r--r--common/queryrunner.cpp13
-rw-r--r--common/resultset.cpp134
-rw-r--r--common/resultset.h101
-rw-r--r--common/store.cpp3
-rw-r--r--common/typeindex.cpp4
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
155template<class T, class Ptr> 155template<class T, class Ptr>
156bool ModelResult<T, Ptr>::canFetchMore(const QModelIndex &parent) const 156bool 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
161template<class T, class Ptr> 162template<class T, class Ptr>
@@ -218,6 +219,7 @@ template<class T, class Ptr>
218void ModelResult<T, Ptr>::fetchEntities(const QModelIndex &parent) 219void 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
23ResultSet::ResultSet()
24 : mIt(nullptr)
25{
26
27}
28
29ResultSet::ResultSet(const ValueGenerator &generator, const SkipValue &skip)
30 : mIt(nullptr),
31 mValueGenerator(generator),
32 mSkip(skip)
33{
34
35}
36
37ResultSet::ResultSet(const IdGenerator &generator)
38 : mIt(nullptr),
39 mGenerator(generator),
40 mSkip([this]() {
41 next();
42 })
43{
44
45}
46
47ResultSet::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
60ResultSet::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
84bool 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
104bool 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
110void ResultSet::skip(int number)
111{
112 Q_ASSERT(mSkip);
113 for (int i = 0; i < number; i++) {
114 mSkip();
115 }
116}
117
118QByteArray 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
131bool 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
75template <class DomainType> 75template <class DomainType>
76QSharedPointer<QAbstractItemModel> Store::loadModel(Query query) 76QSharedPointer<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
37static QByteArray toSortableByteArray(const QDateTime &date) 37static 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