summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCristiano di Flora <cristiano.di-flora@nokia.com>2012-04-27 11:21:51 +0300
committerQt by Nokia <qt-info@nokia.com>2012-06-04 11:32:23 +0200
commite3fff955aba148efa24e268ce1723b3421ef8834 (patch)
tree71b8c759f1e196149cf6ec666e85be124732a71c
parent58f67c7a13052363c8683c2ac9019b5f39231da6 (diff)
Add new private QJsonDbQueryModel class to client.
This change separates the cachinglistmodel logic from the implementation of qml-specific aspects of the jsondbcachinglistmodel. The code is migrated into new private classes in client and reused by the imports/jsondb models. The new classes include functionality of the following components previously embedded in the qml jsondb plugin: - JsonDbCachingListModel - modelutils - modelcache In the new setup, the QJsonDbQueryModel class implements the functionality previously provided by the src/imports/jsondb JsonDbCachingListModel and JsonDbCachingListModelPrivate classes. Modelutils and modelcache helpers are now also part of the private API in client. No functional / performance regression is observed after running models tests and benchmarks on top of this. The idea is to make QJsonDbQueryModel part of the public C++ api after a few rounds of reviewes & iterations. Change-Id: Ia6e2368a4cb7c7485087e714642e00ff686945b7 Reviewed-by: Tapani Mikola <tapani.mikola@nokia.com>
-rw-r--r--src/client/client.pro12
-rw-r--r--src/client/qjsondbmodelcache_p.cpp (renamed from src/imports/jsondb/jsondbmodelcache.cpp)2
-rw-r--r--src/client/qjsondbmodelcache_p.h (renamed from src/imports/jsondb/jsondbmodelcache.h)24
-rw-r--r--src/client/qjsondbmodelutils_p.cpp (renamed from src/imports/jsondb/jsondbmodelutils.cpp)35
-rw-r--r--src/client/qjsondbmodelutils_p.h (renamed from src/imports/jsondb/jsondbmodelutils.h)46
-rw-r--r--src/client/qjsondbquerymodel_p.cpp1794
-rw-r--r--src/client/qjsondbquerymodel_p.h147
-rw-r--r--src/client/qjsondbquerymodel_p_p.h (renamed from src/imports/jsondb/jsondbcachinglistmodel_p.h)83
-rw-r--r--src/imports/jsondb/jsondb.pro5
-rw-r--r--src/imports/jsondb/jsondbcachinglistmodel.cpp1768
-rw-r--r--src/imports/jsondb/jsondbcachinglistmodel.h86
-rw-r--r--src/imports/jsondb/jsondblistmodel.cpp1
-rw-r--r--src/imports/jsondb/jsondblistmodel_p.h8
-rw-r--r--src/imports/jsondb/jsondbpartition.cpp24
-rw-r--r--src/imports/jsondb/jsondbpartition.h5
-rw-r--r--src/imports/jsondb/jsondbqueryobject.cpp2
-rw-r--r--src/imports/jsondb/jsondbsortinglistmodel_p.h3
-rw-r--r--tests/auto/jsondbcachinglistmodel/jsondbcachinglistmodel.pro2
-rw-r--r--tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.cpp39
-rw-r--r--tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.h1
20 files changed, 2343 insertions, 1744 deletions
diff --git a/src/client/client.pro b/src/client/client.pro
index 65fbb3a..187fb11 100644
--- a/src/client/client.pro
+++ b/src/client/client.pro
@@ -38,7 +38,12 @@ HEADERS += \
qjsondbprivatepartition_p.h \
qjsondbstandardpaths_p.h \
qjsondblogrequest_p.h \
- qjsondblogrequest_p_p.h
+ qjsondblogrequest_p_p.h \
+ qjsondbquerymodel_p_p.h \
+ qjsondbmodelcache_p.h \
+ qjsondbmodelutils_p.h \
+ qjsondbquerymodel_p.h
+
SOURCES += \
qjsondbconnection.cpp \
@@ -50,6 +55,9 @@ SOURCES += \
qjsondbobject.cpp \
qjsondbprivatepartition.cpp \
qjsondbstandardpaths.cpp \
- qjsondblogrequest.cpp
+ qjsondblogrequest.cpp \
+ qjsondbmodelcache_p.cpp \
+ qjsondbmodelutils_p.cpp \
+ qjsondbquerymodel_p.cpp
mac:QMAKE_FRAMEWORK_BUNDLE_NAME = $$QT.jsondb.name
diff --git a/src/imports/jsondb/jsondbmodelcache.cpp b/src/client/qjsondbmodelcache_p.cpp
index 4db1b1d..3b98e11 100644
--- a/src/imports/jsondb/jsondbmodelcache.cpp
+++ b/src/client/qjsondbmodelcache_p.cpp
@@ -40,7 +40,7 @@
****************************************************************************/
//#define JSONDB_LISTMODEL_DEBUG
-#include "jsondbmodelcache.h"
+#include "qjsondbmodelcache_p.h"
#include <QMap>
#include <QDebug>
diff --git a/src/imports/jsondb/jsondbmodelcache.h b/src/client/qjsondbmodelcache_p.h
index 83ace7d..35e2b21 100644
--- a/src/imports/jsondb/jsondbmodelcache.h
+++ b/src/client/qjsondbmodelcache_p.h
@@ -40,21 +40,33 @@
****************************************************************************/
-#ifndef JSONDBMODELCACHE_H
-#define JSONDBMODELCACHE_H
+#ifndef JSONDBMODELCACHE_P_H
+#define JSONDBMODELCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtJsonDb API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include <QHash>
#include <QObject>
#include <QList>
#include <QJsonObject>
-#include "jsondbmodelutils.h"
+#include "qjsondbmodelutils_p.h"
QT_BEGIN_NAMESPACE_JSONDB
typedef QMap<SortingKey, QString> JsonDbModelIndexType;
typedef QHash<QString, QJsonObject> JsonDbModelObjectType;
-class ModelPage
+class Q_JSONDB_EXPORT ModelPage
{
public:
int index;
@@ -75,7 +87,7 @@ public:
void dumpPageDetails();
};
-class ModelCache
+class Q_JSONDB_EXPORT ModelCache
{
public:
static qulonglong currentCounter;
@@ -116,4 +128,4 @@ public:
QT_END_NAMESPACE_JSONDB
-#endif // JSONDBMODELCACHE_H
+#endif // JSONDBMODELCACHE_P_H
diff --git a/src/imports/jsondb/jsondbmodelutils.cpp b/src/client/qjsondbmodelutils_p.cpp
index 372aab8..d9003ab 100644
--- a/src/imports/jsondb/jsondbmodelutils.cpp
+++ b/src/client/qjsondbmodelutils_p.cpp
@@ -39,7 +39,7 @@
**
****************************************************************************/
-#include "jsondbmodelutils.h"
+#include "qjsondbmodelutils_p.h"
#include <qdebug.h>
#include <QJsonValue>
#include <QJsonArray>
@@ -82,6 +82,18 @@ int SortingKey::partitionIndex() const
return d->partitionIndex;
}
+QVariant SortingKey::value() const
+{
+ if (d->count == 1)
+ return d->values[0];
+ else if (d->count == 0)
+ return QVariant();
+ QVariantList ret;
+ for (int i = 0; i < d->count; i++)
+ ret << d->values[i];
+ return ret;
+}
+
static bool operator<(const QVariant& lhs, const QVariant& rhs)
{
if ((lhs.type() == QVariant::Int) && (rhs.type() == QVariant::Int))
@@ -267,6 +279,25 @@ QString removeArrayOperator(QString propertyName)
return propertyName;
}
+QList<QJsonObject> qvariantlist_to_qjsonobject_list(const QVariantList &list)
+{
+ QList<QJsonObject> objects;
+ int count = list.count();
+ for (int i = 0; i < count; i++) {
+ objects.append(QJsonObject::fromVariantMap(list[i].toMap()));
+ }
+ return objects;
+}
+
+QVariantList qjsonobject_list_to_qvariantlist(const QList<QJsonObject> &list)
+{
+ QVariantList objects;
+ int count = list.count();
+ for (int i = 0; i < count; i++) {
+ objects.append(list[i].toVariantMap());
+ }
+ return objects;
+}
ModelRequest::ModelRequest(QObject *parent)
:QObject(parent)
@@ -305,5 +336,5 @@ void ModelRequest::onQueryFinished()
emit finished(index, request->takeResults(), request->sortKey());
}
-#include "moc_jsondbmodelutils.cpp"
+#include "moc_qjsondbmodelutils_p.cpp"
QT_END_NAMESPACE_JSONDB
diff --git a/src/imports/jsondb/jsondbmodelutils.h b/src/client/qjsondbmodelutils_p.h
index 0d75b03..28427c2 100644
--- a/src/imports/jsondb/jsondbmodelutils.h
+++ b/src/client/qjsondbmodelutils_p.h
@@ -39,12 +39,25 @@
**
****************************************************************************/
-#ifndef JSONDBMODELUTILS_H
-#define JSONDBMODELUTILS_H
+
+#ifndef JSONDBMODELUTILS_P_H
+#define JSONDBMODELUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtJsonDb API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
#include <QSharedData>
#include <QStringList>
#include <QUuid>
-#include <QJSValue>
#include <QVariant>
#include <QPointer>
#include <QJsonDbWatcher>
@@ -52,12 +65,6 @@
QT_BEGIN_NAMESPACE_JSONDB
-struct CallbackInfo {
- int index;
- QJSValue successCallback;
- QJSValue errorCallback;
-};
-
struct NotificationItem {
int partitionIndex;
QJsonObject item;
@@ -87,10 +94,9 @@ struct SortIndexSpec
{}
};
-class JsonDbListModelPrivate;
-class ModelRequest : public QObject
+
+class Q_JSONDB_EXPORT ModelRequest : public QObject
{
- friend class JsonDbListModelPrivate;
Q_OBJECT
public:
@@ -124,7 +130,7 @@ struct IndexInfo
class SortingKeyPrivate;
-class SortingKey {
+class Q_JSONDB_EXPORT SortingKey {
public:
SortingKey(int partitionIndex, const QVariantMap &object, const QList<bool> &directions, const QList<QStringList> &paths, const SortIndexSpec &spec = SortIndexSpec());
SortingKey(int partitionIndex, const QVariantList &object, const QList<bool> &directions, const SortIndexSpec &spec = SortIndexSpec());
@@ -133,6 +139,7 @@ public:
SortingKey(const SortingKey&);
SortingKey() {}
int partitionIndex() const;
+ QVariant value() const;
bool operator <(const SortingKey &rhs) const;
bool operator ==(const SortingKey &rhs) const;
private:
@@ -211,13 +218,12 @@ template <typename T> int iterator_position(T &begin, T &end, T &value)
return i;
}
-QVariant lookupProperty(QVariantMap object, const QStringList &path);
-QJsonValue lookupJsonProperty(QJsonObject object, const QStringList &path);
-QString removeArrayOperator(QString propertyName);
-QList<QJsonObject> qvariantlist_to_qjsonobject_list(const QVariantList &list);
-QVariantList qjsonobject_list_to_qvariantlist(const QList<QJsonObject> &list);
-QJSValue qjsonobject_list_to_qjsvalue(const QList<QJsonObject> &list);
+Q_JSONDB_EXPORT QVariant lookupProperty(QVariantMap object, const QStringList &path);
+Q_JSONDB_EXPORT QJsonValue lookupJsonProperty(QJsonObject object, const QStringList &path);
+Q_JSONDB_EXPORT QString removeArrayOperator(QString propertyName);
+Q_JSONDB_EXPORT QList<QJsonObject> qvariantlist_to_qjsonobject_list(const QVariantList &list);
+Q_JSONDB_EXPORT QVariantList qjsonobject_list_to_qvariantlist(const QList<QJsonObject> &list);
QT_END_NAMESPACE_JSONDB
-#endif // JSONDBMODELUTILS_H
+#endif // JSONDBMODELUTILS_P_H
diff --git a/src/client/qjsondbquerymodel_p.cpp b/src/client/qjsondbquerymodel_p.cpp
new file mode 100644
index 0000000..fddac44
--- /dev/null
+++ b/src/client/qjsondbquerymodel_p.cpp
@@ -0,0 +1,1794 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define JSONDB_LISTMODEL_DEBUG
+//#define JSONDB_LISTMODEL_BENCHMARK
+
+#include "qjsondbquerymodel_p.h"
+#include "qjsondbquerymodel_p_p.h"
+#include "qjsondbconnection.h"
+#include <QDebug>
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_NAMESPACE_JSONDB
+
+QJsonDbQueryModelPrivate::QJsonDbQueryModelPrivate(QJsonDbQueryModel *q)
+ : q_ptr(q)
+ , componentComplete(false)
+ , resetModel(true)
+ , cacheSize(-1)
+ , state(QJsonDbQueryModel::None)
+ , errorCode(0)
+ , lastQueriedIndex(-1)
+{
+ setCacheParams(INT_MAX/10);
+}
+
+void QJsonDbQueryModelPrivate::init(QJsonDbConnection *dbConnection)
+{
+ mConnection = dbConnection;
+}
+
+void QJsonDbQueryModelPrivate::setCacheParams(int maxItems)
+{
+ objectCache.setPageSize(maxItems);
+ chunkSize = objectCache.chunkSize();
+ lowWaterMark = objectCache.chunkSize()/4;
+}
+
+QJsonDbQueryModelPrivate::~QJsonDbQueryModelPrivate()
+{
+ clearNotifications();
+ while (!keyRequests.isEmpty()) {
+ delete keyRequests[0];
+ keyRequests.removeFirst();
+ }
+ while (!indexRequests.isEmpty()) {
+ delete indexRequests[0];
+ indexRequests.removeFirst();
+ }
+ while (!valueRequests.isEmpty()) {
+ delete valueRequests[0];
+ valueRequests.removeFirst();
+ }
+
+}
+
+// insert item notification handler
+// + add items, for chunked read
+void QJsonDbQueryModelPrivate::addItem(const QJsonObject &item, int partitionIndex)
+{
+ Q_Q(QJsonDbQueryModel);
+ const QString &uuid = item.value(QLatin1String("_uuid")).toString();
+ // ignore duplicates.
+ if (objectSortValues.contains(uuid))
+ return;
+
+ QVariantList vl;
+ vl.append(uuid);
+ vl.append(item.value(QLatin1String("_indexValue")).toVariant());
+ SortingKey key(partitionIndex, vl, QList<bool>() << ascendingOrder, partitionIndexDetails[partitionIndex].spec);
+ QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
+ QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
+ QMap<SortingKey, QString>::const_iterator i = objectUuids.upperBound(key);
+ int index = iterator_position(begin, end, i);
+ if (index <= lastQueriedIndex)
+ lastQueriedIndex++;
+
+ q->beginInsertRows(parent, index, index);
+ objectUuids.insert(key, uuid);
+ objectCache.insert(index, uuid, item, objectUuids);
+ partitionObjectUuids[partitionIndex].insert(key, uuid);
+ objectSortValues.insert(uuid, key);
+ q->endInsertRows();
+ emit q->rowCountChanged(objectSortValues.count());
+}
+
+
+// deleteitem notification handler
+void QJsonDbQueryModelPrivate::deleteItem(const QJsonObject &item, int partitionIndex)
+{
+ Q_Q(QJsonDbQueryModel);
+ QString uuid = item.value(QLatin1String("_uuid")).toString();
+ QMap<QString, SortingKey>::const_iterator keyIndex = objectSortValues.constFind(uuid);
+ if (keyIndex != objectSortValues.constEnd()) {
+ SortingKey key = keyIndex.value();
+ QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
+ QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
+ QMap<SortingKey, QString>::const_iterator i = objectUuids.constFind(key);
+ if (i != end) {
+ int index = iterator_position(begin, end, i);
+ q->beginRemoveRows(parent, index, index);
+ objectCache.remove(index, uuid);
+ partitionObjectUuids[partitionIndex].remove(key);
+ objectUuids.remove(key);
+ objectSortValues.remove(uuid);
+ if (index == lastQueriedIndex)
+ lastQueriedIndex = -1;
+ else if (index < lastQueriedIndex)
+ lastQueriedIndex--;
+ q->endRemoveRows();
+ emit q->rowCountChanged(objectUuids.count());
+ }
+ }
+}
+
+// updateitem notification handler
+void QJsonDbQueryModelPrivate::updateItem(const QJsonObject &item, int partitionIndex)
+{
+ Q_Q(QJsonDbQueryModel);
+ QString uuid = item.value(QLatin1String("_uuid")).toString();
+ QMap<QString, SortingKey>::const_iterator keyIndex = objectSortValues.constFind(uuid);
+ if (keyIndex != objectSortValues.constEnd()) {
+ SortingKey key = keyIndex.value();
+ QVariantList vl;
+ vl.append(uuid);
+ vl.append(item.value(QLatin1String("_indexValue")).toVariant());
+ SortingKey newKey(partitionIndex, vl, QList<bool>() << ascendingOrder, partitionIndexDetails[partitionIndex].spec);
+ QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
+ QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
+ QMap<SortingKey, QString>::const_iterator oldPos = objectUuids.constFind(key);
+ int oldIndex = iterator_position(begin, end, oldPos);
+ if (oldIndex == lastQueriedIndex) // Cached object has changed
+ lastQueriedIndex = -1;
+ // keys are same, modify the object
+ if (key == newKey) {
+ objectCache.update(uuid, item);
+ QModelIndex modelIndex = q->createIndex(oldIndex, 0);
+ emit q->dataChanged(modelIndex, modelIndex);
+ return;
+ }
+ // keys are different
+ QMap<SortingKey, QString>::const_iterator newPos = objectUuids.upperBound(newKey);
+ int newIndex = iterator_position(begin, end, newPos);
+ if ((newIndex != oldIndex) && (newIndex != oldIndex+1)) {
+ if (oldIndex < lastQueriedIndex && newIndex > lastQueriedIndex)
+ lastQueriedIndex--;
+ else if (oldIndex > lastQueriedIndex && newIndex <= lastQueriedIndex)
+ lastQueriedIndex++;
+ q->beginMoveRows(parent, oldIndex, oldIndex, parent, newIndex);
+ objectUuids.remove(key);
+ partitionObjectUuids[partitionIndex].remove(key);
+ objectCache.remove(oldIndex, uuid);
+
+ objectUuids.insert(newKey, uuid);
+ partitionObjectUuids[partitionIndex].insert(newKey, uuid);
+
+ objectSortValues.remove(uuid);
+ objectSortValues.insert(uuid, newKey);
+
+ // recompute the new position
+ newPos = objectUuids.constFind(newKey);
+ begin = objectUuids.constBegin();
+ end = objectUuids.constEnd();
+ newIndex = iterator_position(begin, end, newPos);
+ objectCache.insert(newIndex, uuid, item, objectUuids);
+ q->endMoveRows();
+ // send data changed and return
+ QModelIndex modelIndex = q->createIndex(newIndex, 0);
+ emit q->dataChanged(modelIndex, modelIndex);
+ } else {
+ // same position, update the object
+ objectCache.update(uuid, item);
+ objectUuids.remove(key);
+ objectUuids.insert(newKey, uuid);
+ partitionObjectUuids[partitionIndex].remove(key);
+ partitionObjectUuids[partitionIndex].insert(newKey, uuid);
+ objectSortValues.remove(uuid);
+ objectSortValues.insert(uuid, newKey);
+ newPos = objectUuids.constFind(newKey);
+ begin = objectUuids.constBegin();
+ end = objectUuids.constEnd();
+ newIndex = iterator_position(begin, end, newPos);
+ QModelIndex modelIndex = q->createIndex(newIndex, 0);
+ emit q->dataChanged(modelIndex, modelIndex);
+ }
+ } else {
+ addItem(item, partitionIndex);
+ }
+}
+
+int findIndexOf(const JsonDbModelIndexType::const_iterator &begin, const SortingKey &key, int low, int high)
+{
+ if (high < low)
+ return -1;
+ int mid = (low + high) / 2;
+ JsonDbModelIndexType::const_iterator midItr = begin + mid;
+ if (midItr.key() == key) // ==
+ return mid;
+ else if (midItr.key() < key) // <
+ return findIndexOf(begin, key, mid+1, high);
+ else // >
+ return findIndexOf(begin, key, low, mid-1);
+}
+
+inline void setQueryBindings(QJsonDbReadRequest *request, const QVariantMap &bindings)
+{
+ QVariantMap::ConstIterator i = bindings.constBegin();
+ while (i != bindings.constEnd()) {
+ request->bindValue(i.key(), QJsonValue::fromVariant(i.value()));
+ ++i;
+ }
+}
+
+void QJsonDbQueryModelPrivate::createObjectRequests(int startIndex, int maxItems)
+{
+ Q_Q(QJsonDbQueryModel);
+
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<Q_FUNC_INFO<<startIndex<<maxItems<<objectUuids.count();
+#endif
+ Q_ASSERT(startIndex>=0);
+
+ if (startIndex >= objectUuids.size())
+ return;
+ if ((startIndex + maxItems) > objectUuids.size())
+ maxItems = objectUuids.size() - startIndex;
+
+ if (state == QJsonDbQueryModel::Querying &&
+ currentCacheRequest.index == startIndex &&
+ currentCacheRequest.count == maxItems) {
+ // we are fetching the same set, skip this
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<"Skip this request";
+#endif
+ return;
+ }
+ currentCacheRequest.index = startIndex;
+ currentCacheRequest.count = maxItems;
+
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<"startIndex"<<startIndex<<" maxItems "<<maxItems;
+#endif
+ JsonDbModelIndexNSize *indexNSizes = new JsonDbModelIndexNSize[partitionObjects.count()] ;
+ JsonDbModelIndexType::const_iterator itr = objectUuids.constBegin()+startIndex;
+ for (int i = startIndex; i < startIndex+maxItems; i++, itr++) {
+ const SortingKey &key = itr.key();
+ int index = key.partitionIndex();
+ if (indexNSizes[index].index == -1) {
+ indexNSizes[index].index = findIndexOf(partitionObjectUuids[index].constBegin(),
+ key, 0, partitionObjectUuids[index].count()-1);
+ Q_ASSERT(indexNSizes[index].index != -1);
+ }
+ indexNSizes[index].count++;
+ }
+ for (int i = 0; i < partitionObjects.count(); i++) {
+ RequestInfo &r = partitionObjectDetails[i];
+ if (indexNSizes[i].count) {
+ if (state != QJsonDbQueryModel::Querying) {
+ state = QJsonDbQueryModel::Querying;
+ emit q->stateChanged(state);
+ }
+
+ r.lastOffset = indexNSizes[i].index;
+ r.lastSize = -1;
+ r.requestCount = indexNSizes[i].count;
+ QJsonDbReadRequest *request = valueRequests[i]->newRequest(i);
+ request->setQuery(query+sortOrder);
+ request->setProperty("queryOffset", indexNSizes[i].index);
+ request->setQueryLimit(qMin(r.requestCount, chunkSize));
+ request->setPartition(partitionObjects[i]);
+ setQueryBindings(request, queryBindings);
+ if (mConnection)
+ mConnection->send(request);
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<"Query"<<query+sortOrder<<partitionObjects[i];
+ qDebug()<<"Request "<<request->property("requestId") <<
+ "Total Count "<<r.requestCount <<
+ "Offset"<<r.lastOffset<<
+ "Count "<<qMin(r.requestCount,chunkSize);
+#endif
+ } else {
+ r.lastSize = 0;
+ r.requestCount = 0;
+ }
+ }
+ delete [] indexNSizes;
+}
+
+void QJsonDbQueryModelPrivate::verifyIndexSpec(const QList<QJsonObject> &items, int partitionIndex)
+{
+ Q_Q(QJsonDbQueryModel);
+ SortIndexSpec &indexSpec = partitionIndexDetails[partitionIndex].spec;
+ bool validIndex = false;
+ if (items.count()) {
+ for (int i = 0; i < items.length() && !validIndex; i++) {
+ QJsonObject spec = items[i];
+ indexSpec.propertyName = QLatin1String("_indexValue");
+ QString propertyType = spec.value(QLatin1String("propertyType")).toString();
+ indexSpec.name = spec.value(QLatin1String("name")).toString();
+ if (indexSpec.name.isEmpty())
+ indexSpec.name = spec.value(QLatin1String("propertyName")).toString();
+ indexSpec.caseSensitive = true;
+ if (!indexName.isEmpty()) {
+ if (indexSpec.name == indexName) {
+ if (!propertyType.compare(QLatin1String("string"), Qt::CaseInsensitive)) {
+ indexSpec.type = SortIndexSpec::String;
+ if (spec.value(QLatin1String("caseSensitive")).isBool())
+ indexSpec.caseSensitive = spec.value(QLatin1String("caseSensitive")).toBool();
+ validIndex = true;
+ } else if (!propertyType.compare(QLatin1String("number"), Qt::CaseInsensitive)) {
+ indexSpec.type = SortIndexSpec::Number;
+ validIndex = true;
+ } else if (!propertyType.compare(QLatin1String("UUID"), Qt::CaseInsensitive)) {
+ indexSpec.type = SortIndexSpec::UUID;
+ indexSpec.caseSensitive = false;
+ validIndex = true;
+ }
+ }
+ }
+ }
+ }
+ if (!validIndex) {
+ qWarning() << "Error JsonDbCachingListModel requires a supported Index for "<<indexName << partitionObjects[partitionIndex];
+ reset();
+ state = QJsonDbQueryModel::Error;
+ emit q->stateChanged(state);
+ } else {
+ partitionIndexDetails[partitionIndex].valid = true;
+ //Check if all index specs are supported.
+ bool checkedAll = true;
+ for (int i = 0; i < partitionIndexDetails.count(); i++) {
+ if (availablePartitions[i].state != PartitionStateOnline)
+ continue;
+ if (!partitionIndexDetails[i].valid) {
+ checkedAll = false;
+ break;
+ }
+ }
+ if (checkedAll) {
+ //Start fetching the keys.
+ setQueryForSortKeys();
+ for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
+ fetchPartitionKeys(i);
+ }
+ }
+ }
+}
+
+void QJsonDbQueryModelPrivate::fillKeys(const QList<QJsonObject> &items, int partitionIndex)
+{
+ RequestInfo &r = partitionKeyRequestDetails[partitionIndex];
+ r.lastSize = items.size();
+ for (int i = 0; i < r.lastSize; i++) {
+ const QJsonObject &item = items.at(i);
+ QString uuidStr = item.value(QLatin1String("_uuid")).toString();
+ QByteArray uuid = QUuid(uuidStr).toRfc4122();
+ SortingKey key(partitionIndex, uuid, item.value(QLatin1String("_indexValue")).toVariant(), ascendingOrder, partitionIndexDetails[partitionIndex].spec);
+ objectUuids.insert(key, uuidStr);
+ partitionObjectUuids[partitionIndex].insert(key, uuidStr);
+ objectSortValues.insert(uuidStr, key);
+
+ }
+ // Check if requests from different partitions returned
+ // all the results
+ bool allRequestsFinished = true;
+ for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
+ if (availablePartitions[i].state != PartitionStateOnline)
+ continue;
+ if (partitionKeyRequestDetails[i].lastSize >= chunkSize || partitionKeyRequestDetails[i].lastSize == -1) {
+ allRequestsFinished = false;
+ break;
+ }
+ }
+ if (allRequestsFinished) {
+ // retrieve the first chunk of data
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<"All Keys Received "<<objectUuids.count();
+#endif
+ if (!objectUuids.count()) {
+ for (int i = 0; i<partitionObjectDetails.count(); i++) {
+ fillData(QList<QJsonObject>(), i);
+ }
+ return;
+ }
+ createObjectRequests(0, qMin(objectCache.maxItems(), objectUuids.count()));
+ } else if (r.lastSize >= chunkSize){
+ // more items, fetch next chunk of keys
+ fetchNextKeyChunk(partitionIndex);
+ }
+}
+
+void QJsonDbQueryModelPrivate::emitDataChanged(int from, int to)
+{
+ Q_Q(QJsonDbQueryModel);
+ QModelIndex modelIndexFrom = q->createIndex(from, 0);
+ QModelIndex modelIndexTo = q->createIndex(to, 0);
+ emit q->dataChanged(modelIndexFrom, modelIndexTo);
+}
+
+void QJsonDbQueryModelPrivate::fillData(const QList<QJsonObject> &items, int partitionIndex)
+{
+ Q_Q(QJsonDbQueryModel);
+ RequestInfo &r = partitionObjectDetails[partitionIndex];
+ r.lastSize = items.size();
+ r.requestCount -= r.lastSize;
+ r.lastOffset += r.lastSize;
+
+ for (int i = 0; i < r.lastSize; i++) {
+ const QJsonObject &item = items.at(i);
+ const QString &uuid = item.value(QLatin1String("_uuid")).toString();
+ tmpObjects.insert(uuid, item);
+ }
+
+ // Check if requests from different partitions returned
+ // all the results
+ bool allRequestsFinished = true;
+ for (int i = 0; i < partitionObjectDetails.count(); i++) {
+ if (availablePartitions[i].state != PartitionStateOnline)
+ continue;
+ if (partitionObjectDetails[i].lastSize >= chunkSize || partitionObjectDetails[i].lastSize == -1) {
+ allRequestsFinished = false;
+ break;
+ }
+ }
+ if (allRequestsFinished) {
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<"Finished Req For:"<<currentCacheRequest.index<<currentCacheRequest.count;
+ qDebug()<<"Finished Req received count: "<<tmpObjects.count()<<" Total Items:"<<objectUuids.count();
+#endif
+ objectCache.addObjects(currentCacheRequest.index, objectUuids, tmpObjects);
+ tmpObjects.clear();
+ JsonDbModelIndexNSize req = currentCacheRequest;
+ currentCacheRequest.clear();
+ // send the update for missed items
+ QList<int> pendingCacheMiss;
+ int changedFrom = -1, changedTo = -2;
+ for (int i = 0; i < cacheMiss.size(); i++) {
+ if (cacheMiss[i] >= req.index &&
+ cacheMiss[i] < req.index + req.count) {
+ if (changedFrom >= 0 && cacheMiss[i] != changedTo+1) {
+ emitDataChanged(changedFrom, changedTo);
+ changedFrom = -1;
+ }
+ if (changedFrom < 0) changedFrom = cacheMiss[i];
+ changedTo = cacheMiss[i];
+ } else {
+ pendingCacheMiss.append(cacheMiss[i]);
+ }
+ }
+ if (changedFrom >= 0)
+ emitDataChanged(changedFrom, changedTo);
+ cacheMiss.clear();
+ cacheMiss = pendingCacheMiss;
+
+ for (int i = 0; i<req.count; i++) {
+ emit q->objectAvailable(req.index + i,
+ getJsonObject(req.index + i),
+ getItemPartition(req.index + i));
+ }
+
+ if (resetModel) {
+ q->beginResetModel();
+ q->endResetModel();
+ emit q->rowCountChanged(objectUuids.count());
+ resetModel = false;
+ }
+ // retrieved all elements
+ state = QJsonDbQueryModel::Ready;
+ emit q->stateChanged(state);
+ if (!pendingNotifications.isEmpty()) {
+ foreach (NotificationItem pending, pendingNotifications)
+ sendNotification(pending.partitionIndex, pending.item, pending.action);
+ pendingNotifications.clear();
+ }
+ if (requestQueue.count()) {
+ QPair<int, int> req = requestQueue.takeFirst();
+ createObjectRequests(req.first, req.second);
+ }
+ } else if (r.lastSize >= chunkSize){
+ // more items, fetch next chunk
+ fetchNextChunk(partitionIndex);
+ }
+}
+
+
+//Clears all the state information.
+void QJsonDbQueryModelPrivate::reset()
+{
+ Q_Q(QJsonDbQueryModel);
+ lastQueriedIndex = -1;
+ q->beginResetModel();
+ clearNotifications();
+ for (int i = 0; i < partitionObjectDetails.count(); i++) {
+ partitionObjectDetails[i].clear();
+ }
+ for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
+ partitionKeyRequestDetails[i].clear();
+ }
+ for (int i = 0; i < partitionIndexDetails.count(); i++) {
+ partitionIndexDetails[i].clear();
+ }
+ for (int i = 0; i < partitionObjectUuids.count(); i++) {
+ partitionObjectUuids[i].clear();
+ }
+
+ objectCache.clear();
+ objectUuids.clear();
+ objectSortValues.clear();
+ currentCacheRequest.clear();
+ cacheMiss.clear();
+ requestQueue.clear();
+ q->endResetModel();
+ emit q->rowCountChanged(0);
+ state = QJsonDbQueryModel::None;
+ emit q->stateChanged(state);
+}
+
+bool QJsonDbQueryModelPrivate::checkForDefaultIndexTypes(int index)
+{
+ Q_Q(QJsonDbQueryModel);
+ bool defaultType = false;
+ if (!indexName.compare(QLatin1String("_uuid")) || !indexName.compare(QLatin1String("_type"))) {
+ defaultType = true;
+ QMetaObject::invokeMethod(q, "_q_verifyDefaultIndexType", Qt::QueuedConnection,
+ QGenericReturnArgument(),
+ Q_ARG(int, index));
+ }
+ return defaultType;
+}
+
+void QJsonDbQueryModelPrivate::fetchIndexSpec(int index)
+{
+ Q_Q(QJsonDbQueryModel);
+ if (index >= partitionObjects.count())
+ return;
+ if (checkForDefaultIndexTypes(index))
+ return;
+ if (state != QJsonDbQueryModel::Querying) {
+ state = QJsonDbQueryModel::Querying;
+ emit q->stateChanged(state);
+ }
+ if (availablePartitions[index].state == PartitionStateOnline) {
+ QString partitionName = partitionObjects[index];
+ QJsonDbReadRequest *request = indexRequests[index]->newRequest(index);
+ request->setQuery(queryForIndexSpec);
+ request->setPartition(partitionName);
+ if (mConnection)
+ mConnection->send(request);
+ }
+}
+
+void QJsonDbQueryModelPrivate::fetchPartitionKeys(int index)
+{
+ Q_Q(QJsonDbQueryModel);
+ if (index >= partitionObjects.count())
+ return;
+
+ if (state != QJsonDbQueryModel::Querying) {
+ state = QJsonDbQueryModel::Querying;
+ emit q->stateChanged(state);
+ }
+ if (availablePartitions[index].state == PartitionStateOnline) {
+ RequestInfo &r = partitionKeyRequestDetails[index];
+ QString partitionName = partitionObjects[index];
+ r.lastSize = -1;
+ r.lastOffset = 0;
+ QJsonDbReadRequest *request = keyRequests[index]->newRequest(index);
+ request->setQuery(queryForSortKeys);
+ request->setQueryLimit(chunkSize);
+ request->setPartition(partitionName);
+ setQueryBindings(request, queryBindings);
+ if (mConnection)
+ mConnection->send(request);
+ }
+}
+
+void QJsonDbQueryModelPrivate::initializeModel(bool reset)
+{
+ resetModel = reset;
+ if (resetModel) {
+ objectCache.clear();
+ objectUuids.clear();
+ objectSortValues.clear();
+ for (int i = 0; i < partitionObjectUuids.count(); i++) {
+ partitionObjectUuids[i].clear();
+ }
+ for (int i = 0; i < partitionIndexDetails.count(); i++) {
+ partitionIndexDetails[i].clear();
+ }
+ }
+ for (int i = 0; i < partitionObjects.count(); i++) {
+ fetchIndexSpec(i);
+ }
+}
+
+void QJsonDbQueryModelPrivate::fetchModel(bool reset)
+{
+ parseSortOrder();
+ initializeModel(reset);
+}
+
+void QJsonDbQueryModelPrivate::fetchNextKeyChunk(int partitionIndex)
+{
+ RequestInfo &r = partitionKeyRequestDetails[partitionIndex];
+ r.lastOffset += chunkSize;
+ QJsonDbReadRequest *request = keyRequests[partitionIndex]->newRequest(partitionIndex);
+ request->setQuery(queryForSortKeys);
+ request->setProperty("queryOffset", r.lastOffset);
+ request->setQueryLimit(chunkSize);
+ request->setPartition(partitionObjects[partitionIndex]);
+ setQueryBindings(request, queryBindings);
+ if (mConnection)
+ mConnection->send(request);
+}
+
+void QJsonDbQueryModelPrivate::fetchNextChunk(int partitionIndex)
+{
+ RequestInfo &r = partitionObjectDetails[partitionIndex];
+ QJsonDbReadRequest *request = valueRequests[partitionIndex]->newRequest(partitionIndex);
+ request->setQuery(query+sortOrder);
+ request->setProperty("queryOffset", r.lastOffset);
+ request->setQueryLimit(qMin(r.requestCount, chunkSize));
+ request->setPartition(partitionObjects[partitionIndex]);
+ setQueryBindings(request, queryBindings);
+ if (mConnection)
+ mConnection->send(request);
+}
+
+void QJsonDbQueryModelPrivate::prefetchNearbyPages(int index)
+{
+ int pos = objectCache.findPrefetchIndex(index, lowWaterMark);
+ if (pos != -1 && index <= objectUuids.count()) {
+ createObjectRequests(pos, objectCache.findChunkSize(pos));
+ }
+}
+void QJsonDbQueryModelPrivate::addIndexToQueue(int index)
+{
+ int maxItems = 0;
+ int start = objectCache.findIndexNSize(index, maxItems);
+ QPair<int, int> req;
+ foreach (req, requestQueue) {
+ if (start == req.first && maxItems == req.second) {
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<"Allready in Queue "<<start<<maxItems;
+#endif
+ return;
+ }
+ }
+ requestQueue.append(QPair<int, int>(start,maxItems));
+}
+
+void QJsonDbQueryModelPrivate::requestPageContaining(int index)
+{
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<Q_FUNC_INFO<<index;
+#endif
+ if (state == QJsonDbQueryModel::Querying) {
+ if (index >= currentCacheRequest.index &&
+ index < currentCacheRequest.index+currentCacheRequest.count) {
+ // Check if we are querying for this range already
+ if (!cacheMiss.contains(index))
+ cacheMiss.append(index);
+ return;
+ } else {
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug()<<"Add new Request to Queue" << index <<" currentCacheRequest = " <<
+ currentCacheRequest.index << ", " << currentCacheRequest.count;
+#endif
+ addIndexToQueue(index);
+ return;
+ }
+ }
+ int maxItems = 0;
+ int start = objectCache.findIndexNSize(index, maxItems);
+ createObjectRequests(start, maxItems);
+
+}
+
+void QJsonDbQueryModelPrivate::clearNotification(int index)
+{
+ if (index >= partitionObjects.count())
+ return;
+
+ RequestInfo &r = partitionObjectDetails[index];
+ if (r.watcher && mConnection) {
+ mConnection->removeWatcher(r.watcher);
+ }
+ r.clear();
+}
+
+void QJsonDbQueryModelPrivate::clearNotifications()
+{
+ for (int i = 0; i < partitionObjects.count(); i++)
+ clearNotification(i);
+}
+
+void QJsonDbQueryModelPrivate::createOrUpdateNotification(int index)
+{
+ Q_Q(QJsonDbQueryModel);
+ if (index >= partitionObjects.count())
+ return;
+ clearNotification(index);
+ if (availablePartitions[index].state != PartitionStateOnline)
+ return;
+ QJsonDbWatcher *watcher = new QJsonDbWatcher();
+ watcher->setQuery(query+sortOrder);
+ watcher->setWatchedActions(QJsonDbWatcher::Created | QJsonDbWatcher::Updated |QJsonDbWatcher::Removed);
+ watcher->setPartition(partitionObjects[index]);
+ QVariantMap::ConstIterator i = queryBindings.constBegin();
+ while (i != queryBindings.constEnd()) {
+ watcher->bindValue(i.key(), QJsonValue::fromVariant(i.value()));
+ ++i;
+ }
+ QObject::connect(watcher, SIGNAL(notificationsAvailable(int)),
+ q, SLOT(_q_notificationsAvailable()));
+ QObject::connect(watcher, SIGNAL(error(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)),
+ q, SLOT(_q_notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)));
+ if (mConnection) {
+ mConnection->addWatcher(watcher);
+ partitionObjectDetails[index].watcher = watcher;
+ }
+}
+
+void QJsonDbQueryModelPrivate::createOrUpdateNotifications()
+{
+ for (int i = 0; i < partitionObjects.count(); i++) {
+ createOrUpdateNotification(i);
+ }
+}
+
+void QJsonDbQueryModelPrivate::parseSortOrder()
+{
+ Q_Q(QJsonDbQueryModel);
+ QRegExp orderMatch(QStringLiteral("\\[([/\\\\[\\]])[ ]*([^\\[\\]]+)[ ]*\\]"));
+ if (orderMatch.indexIn(sortOrder, 0) >= 0) {
+ ascendingOrder = false;
+ if (!orderMatch.cap(1).compare(QLatin1String("/")))
+ ascendingOrder = true;
+ indexName = orderMatch.cap(2);
+ }
+ if (!indexName.isEmpty()) {
+ queryForIndexSpec = QString(QLatin1String("[?_type=\"Index\"][?name=\"%1\" | propertyName=\"%1\"]")).arg(indexName);
+ } else {
+ // Set default sort order (by _uuid)
+ q->setSortOrder(QLatin1String("[/_uuid]"));
+ }
+}
+
+void QJsonDbQueryModelPrivate::setQueryForSortKeys()
+{
+ // Query to retrieve the sortKeys
+ // TODO remove the "[= {}]" from query
+ queryForSortKeys = query + QLatin1String("[= { _uuid: _uuid, _indexValue: _indexValue }]");
+ queryForSortKeys += sortOrder;
+}
+
+int QJsonDbQueryModelPrivate::indexOfWatcher(QJsonDbWatcher *watcher)
+{
+ for (int i = 0; i < partitionObjectDetails.count(); i++) {
+ if (watcher == partitionObjectDetails[i].watcher)
+ return i;
+ }
+ return -1;
+}
+
+int QJsonDbQueryModelPrivate::indexOfPartitionObjectWatcher(QJsonDbWatcher *watcher)
+{
+ for (int i = 0; i < availablePartitions.count(); i++) {
+ if (watcher == availablePartitions[i].watcher)
+ return i;
+ }
+ return -1;
+}
+
+QJsonObject QJsonDbQueryModelPrivate::getJsonObject(int index)
+{
+ if (index == lastQueriedIndex)
+ return lastQueriedObject;
+ if (index < 0 || index >= objectUuids.size()) {
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug() << "getItem" << index << "size " << objectUuids.size();
+#endif
+ return QJsonObject();
+ }
+ int page = objectCache.findPage(index);
+ if (page == -1) {
+ if (!cacheMiss.contains(index))
+ cacheMiss.append(index);
+ requestPageContaining(index);
+ return QJsonObject();
+ }
+
+ QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
+ const QString &uuid = (begin+index).value();
+ if (!objectCache.hasValueAtPage(page, uuid)) {
+ // The value is missing, refresh page
+ int startIndex = 0; int count = 0;
+ objectCache.dropPage(page, startIndex, count);
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug() << "getItem Refresh Page: "<<page<<"Start: "<<startIndex<< "Count:"<<count<< "State :"<<state;
+#endif
+ if (state == QJsonDbQueryModel::Ready) {
+ // Start the request
+ createObjectRequests(startIndex, count);
+ } else {
+ requestQueue.append(QPair<int, int>(startIndex, count));
+ }
+ if (!cacheMiss.contains(index))
+ cacheMiss.append(index);
+ return QJsonObject();
+ }
+ if (state == QJsonDbQueryModel::Ready) // Pre-fetch only, if in Ready state
+ prefetchNearbyPages(index);
+ QJsonObject ret = objectCache.valueAtPage(page, uuid);
+ lastQueriedIndex = index;
+ lastQueriedObject = ret;
+ return ret;
+}
+
+QVariant QJsonDbQueryModelPrivate::getItem(int index)
+{
+ return QVariant(getJsonObject(index).toVariantMap());
+}
+
+QVariant QJsonDbQueryModelPrivate::getItem(int index, int role)
+{
+ QJsonObject obj = getJsonObject(index);
+ return lookupJsonProperty(obj, properties[role]).toVariant();
+}
+
+QString QJsonDbQueryModelPrivate::getItemPartition(int index)
+{
+ if (index < 0 || index >= objectUuids.size())
+ return QString();
+ QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
+ int partitionIndex = (begin+index).key().partitionIndex();
+ if (partitionIndex <= partitionObjects.count())
+ return partitionObjects[partitionIndex];
+ return QString();
+}
+
+int QJsonDbQueryModelPrivate::indexOf(const QString &uuid) const
+{
+ if (!objectSortValues.contains(uuid))
+ return -1;
+ const SortingKey &key = objectSortValues.value(uuid);
+ QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
+ QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
+ QMap<SortingKey, QString>::const_iterator i = objectUuids.find(key);
+ return iterator_position(begin, end, i);
+}
+
+void QJsonDbQueryModelPrivate::sendNotification(int partitionIndex, const QJsonObject &object, QJsonDbWatcher::Action action)
+{
+ if (action == QJsonDbWatcher::Created) {
+ addItem(object, partitionIndex);
+ } else if (action == QJsonDbWatcher::Removed)
+ deleteItem(object, partitionIndex);
+ else if (action == QJsonDbWatcher::Updated) {
+ updateItem(object, partitionIndex);
+ }
+}
+
+void QJsonDbQueryModelPrivate::onPartitionStateChanged()
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+#ifdef JSONDB_LISTMODEL_DEBUG
+ qDebug() << Q_FUNC_INFO;
+#endif
+ if (componentComplete && !query.isEmpty() && partitionsReady()) {
+ createOrUpdateNotifications();
+ fetchModel();
+ }
+
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+bool QJsonDbQueryModelPrivate::partitionsReady()
+{
+ for (int i = 0; i < partitionObjects.count(); i++) {
+ if (availablePartitions[i].state == PartitionStateNone || availablePartitions[i].state == PartitionStateError)
+ return false;
+ }
+ return true;
+}
+
+void QJsonDbQueryModelPrivate::_q_keyResponse(int index, const QList<QJsonObject> &v, const QString &sortKey)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_UNUSED(sortKey)
+ fillKeys(v, index);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_valueResponse(int index, const QList<QJsonObject> &v)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ fillData(v, index);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_indexResponse(int index, const QList<QJsonObject> &v)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ verifyIndexSpec(v, index);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode code, const QString & message)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_Q(QJsonDbQueryModel);
+ qWarning() << QString(QStringLiteral("JsonDb error: %1 %2")).arg(code).arg(message);
+ if (code != QtJsonDb::QJsonDbRequest::PartitionUnavailable) {
+ int oldErrorCode = errorCode;
+ errorCode = code;
+ errorString = message;
+ if (oldErrorCode != errorCode)
+ emit q->errorChanged(q->error());
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_notificationsAvailable()
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_Q(QJsonDbQueryModel);
+ QJsonDbWatcher *watcher = qobject_cast<QJsonDbWatcher *>(q->sender());
+ int partitionIndex = indexOfWatcher(watcher);
+ if (!watcher || partitionIndex == -1)
+ return;
+ QList<QJsonDbNotification> list = watcher->takeNotifications();
+ for (int i = 0; i < list.count(); i++) {
+ const QJsonDbNotification & notification = list[i];
+ QJsonObject object = notification.object();
+ QJsonDbWatcher::Action action = notification.action();
+ if (state == QJsonDbQueryModel::Querying) {
+ NotificationItem pending;
+ pending.partitionIndex = partitionIndex;
+ pending.item = object;
+ pending.action = action;
+ pendingNotifications.append(pending);
+ } else {
+ foreach (NotificationItem pending, pendingNotifications)
+ sendNotification(pending.partitionIndex, pending.item, pending.action);
+ pendingNotifications.clear();
+ sendNotification(partitionIndex, object, action);
+ }
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_partitionWatcherNotificationsAvailable()
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_Q(QJsonDbQueryModel);
+ QJsonDbWatcher *watcher = qobject_cast<QJsonDbWatcher *>(q->sender());
+ int partitionIndex = indexOfPartitionObjectWatcher(watcher);
+ if (!watcher || partitionIndex == -1)
+ return;
+ QList<QJsonDbNotification> list = watcher->takeNotifications();
+ for (int i = 0; i < list.count(); i++) {
+ const QJsonDbNotification & notification = list[i];
+ QJsonObject object = notification.object();
+ QJsonDbWatcher::Action action = notification.action();
+ QJsonDbQueryModelPrivate::JsonDbPartitionState previousState = availablePartitions[partitionIndex].state;
+ if (action == QJsonDbWatcher::Removed) {
+ availablePartitions[partitionIndex].state = PartitionStateOffline;
+ } else {
+ availablePartitions[partitionIndex].state = object.value(QStringLiteral("available"))
+ .toBool() ? PartitionStateOnline : PartitionStateOffline;
+ }
+ if (previousState != availablePartitions[partitionIndex].state)
+ this->onPartitionStateChanged();
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode code, const QString &message)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_Q(QJsonDbQueryModel);
+ if (code != QtJsonDb::QJsonDbRequest::PartitionUnavailable) {
+ int oldErrorCode = errorCode;
+ errorCode = code;
+ errorString = message;
+ if (oldErrorCode != errorCode)
+ emit q->errorChanged(q->error());
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_partitionObjectQueryFinished()
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_Q(QJsonDbQueryModel);
+ QJsonDbReadRequest *request = qobject_cast<QJsonDbReadRequest *>(q->sender());
+ if (request) {
+ QList<QJsonObject> objects = request->takeResults();
+ int count = objects.count();
+ if (count) {
+ QString name = objects[0].value(QStringLiteral("name")).toString();
+ // Skip this if name has been changed already
+ int partitionIndex = partitionObjects.indexOf(name);
+ if (partitionIndex == -1)
+ return;
+ JsonDbPartitionState state = objects[0].value(QStringLiteral("available"))
+ .toBool() ? PartitionStateOnline : PartitionStateOffline;
+ availablePartitions[partitionIndex].state = state;
+ onPartitionStateChanged();
+ }
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_partitionObjectQueryError()
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_Q(QJsonDbQueryModel);
+ QJsonDbReadRequest *request = qobject_cast<QJsonDbReadRequest *>(q->sender());
+ //TODO: NEED A MAPPING BETWEEN REQUEST AND PARTITION NAME / INDEX
+ if (request) {
+ qWarning() << "Partition query error";
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::_q_partitionWatcherNotificationError(QtJsonDb::QJsonDbWatcher::ErrorCode code, const QString &message)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_Q(QJsonDbQueryModel);
+ QJsonDbWatcher *watcher = qobject_cast<QJsonDbWatcher *>(q->sender());
+ int partitionIndex = indexOfPartitionObjectWatcher(watcher);
+ qWarning() << QStringLiteral("QJsonDbQueryModel PartitionObjectNotification error: %1 %2").arg(code).arg(message);
+ availablePartitions[partitionIndex].state = PartitionStateError;
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+
+
+void QJsonDbQueryModelPrivate::_q_verifyDefaultIndexType(int index)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ SortIndexSpec &indexSpec = partitionIndexDetails[index].spec;
+ partitionIndexDetails[index].valid = true;
+ if (!indexName.compare(QLatin1String("_uuid"))) {
+ indexSpec.name = QLatin1String("_uuid");
+ indexSpec.type = SortIndexSpec::UUID;
+ indexSpec.caseSensitive = false;
+ } else if (!indexName.compare(QLatin1String("_type"))) {
+ indexSpec.name = QLatin1String("_type");
+ indexSpec.type = SortIndexSpec::String;
+ indexSpec.caseSensitive = true;
+ }
+ //Check if all index specs are supported.
+ bool checkedAll = true;
+ for (int i = 0; i < partitionIndexDetails.count(); i++) {
+ if (!partitionIndexDetails[i].valid) {
+ checkedAll = false;
+ break;
+ }
+ }
+ if (checkedAll) {
+ //Start fetching the keys.
+ setQueryForSortKeys();
+ for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
+ fetchPartitionKeys(i);
+ }
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+void QJsonDbQueryModelPrivate::appendPartition(const QString& partitionName)
+{
+ Q_Q(QJsonDbQueryModel);
+ partitionObjects.append(partitionName);
+
+ partitionObjectDetails.append(RequestInfo());
+ startWatchingPartitionObject(partitionName);
+ ModelRequest *valueRequest = new ModelRequest();
+ QObject::connect(valueRequest, SIGNAL(finished(int,QList<QJsonObject>,QString)),
+ q, SLOT(_q_valueResponse(int,QList<QJsonObject>)));
+ QObject::connect(valueRequest, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
+ q, SLOT(_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode,QString)));
+ valueRequests.append(valueRequest);
+
+ partitionKeyRequestDetails.append(RequestInfo());
+ ModelRequest *keyRequest = new ModelRequest();
+ QObject::connect(keyRequest, SIGNAL(finished(int,QList<QJsonObject>,QString)),
+ q, SLOT(_q_keyResponse(int,QList<QJsonObject>,QString)));
+ QObject::connect(keyRequest, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
+ q, SLOT(_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode,QString)));
+ keyRequests.append(keyRequest);
+
+ partitionObjectUuids.append(JsonDbModelIndexType());
+
+ partitionIndexDetails.append(IndexInfo());
+ ModelRequest *indexRequest = new ModelRequest();
+ QObject::connect(indexRequest, SIGNAL(finished(int,QList<QJsonObject>,QString)),
+ q, SLOT(_q_indexResponse(int,QList<QJsonObject>)));
+ QObject::connect(indexRequest, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
+ q, SLOT(_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode,QString)));
+ indexRequests.append(indexRequest);
+
+ if (componentComplete && !query.isEmpty()) {
+ parseSortOrder();
+ createOrUpdateNotification(partitionObjects.count()-1);
+ if (state == QJsonDbQueryModel::None)
+ resetModel = true;
+ fetchIndexSpec(partitionObjects.count()-1);
+ }
+}
+
+void QJsonDbQueryModelPrivate::startWatchingPartitionObject(const QString &partitionName)
+{
+ Q_Q(QJsonDbQueryModel);
+ availablePartitions.append(JsonDbAvailablePartitionsInfo());
+
+ QString query;
+ if (partitionName.isEmpty())
+ query= QLatin1String("[?_type=\"Partition\"][?default=true]");
+ else
+ query = QStringLiteral("[?_type=\"Partition\"][?name=\"%1\"]").arg(partitionName);
+
+ // Create a watcher to watch changes in partition state
+ QJsonDbWatcher *partitionWatcher = new QJsonDbWatcher();
+ partitionWatcher->setQuery(query);
+ partitionWatcher->setWatchedActions(QJsonDbWatcher::Created | QJsonDbWatcher::Removed | QJsonDbWatcher::Updated);
+ partitionWatcher->setPartition(QStringLiteral("Ephemeral"));
+ QObject::connect(partitionWatcher, SIGNAL(notificationsAvailable(int)),
+ q, SLOT(_q_partitionWatcherNotificationsAvailable()));
+ QObject::connect(partitionWatcher, SIGNAL(error(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)),
+ q, SLOT(_q_partitionWatcherNotificationError(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)));
+ mConnection->addWatcher(partitionWatcher);
+
+ // Create a query to ephemeral partition to find out the state (&name) of the partition
+ QJsonDbReadRequest *request = new QJsonDbReadRequest;
+ request->setQuery(query);
+ request->setPartition(QStringLiteral("Ephemeral"));
+ QObject::connect(request, SIGNAL(finished()), q, SLOT(_q_partitionObjectQueryFinished()));
+ QObject::connect(request, SIGNAL(finished()), request, SLOT(deleteLater()));
+ QObject::connect(request, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
+ q, SLOT(_q_partitionObjectQueryError()));
+ QObject::connect(request, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
+ request, SLOT(deleteLater()));
+ mConnection->send(request);
+}
+
+void QJsonDbQueryModelPrivate::clearPartitions()
+{
+ partitionObjects.clear();
+ partitionObjectDetails.clear();
+ partitionKeyRequestDetails.clear();
+ partitionObjectUuids.clear();
+ partitionIndexDetails.clear();
+ while (!keyRequests.isEmpty()) {
+ delete keyRequests[0];
+ keyRequests.removeFirst();
+ }
+ while (!indexRequests.isEmpty()) {
+ delete indexRequests[0];
+ indexRequests.removeFirst();
+ }
+ while (!valueRequests.isEmpty()) {
+ delete valueRequests[0];
+ valueRequests.removeFirst();
+ }
+ reset();
+}
+
+/*!
+ \class QJsonDbQueryModel
+ \inmodule QtJsonDb
+
+ The QJsonDbQueryModel provides a read-only QAbstractListModel usable with views such as
+ ListView or GridView displaying data items matching a query. The sorting is done using
+ an index set on the JsonDb server. If it doesn't find a matching index for the sortkey,
+ the model goes into Error state. Maximum number of items in the model cache can be set
+ by cacheSize property.
+
+ When an item is not present in the internal cache, the model can return an 'undefined'
+ object from data() method. It will be queued for retrieval and the model will notify its
+ presence using the dataChanged() signal.
+
+ The model is initialized by retrieving the result in chunks. After receiving the first
+ chunk, the model is reset with items from it. The state will be "Querying" during
+ fetching data and will be changed to "Ready".
+
+ \code
+ QtJsonDb::QJsonDbQueryModel *model = new QtJsonDb::QJsonDbQueryModel(connection);
+ model->setQueryRoleNames(roleMap);
+ model->appendPartition(QStringLiteral("User"));
+ model->setQuery(QStringLiteral("[?_type=\"MyType\"][/orderKey]");
+ model->populate();
+ \endcode
+
+ \sa setQueryRoleNames(), appendPartition(), setQuery(), populate()
+*/
+/*!
+ \enum QJsonDbQueryModel::state
+
+ This enum describes current model state.
+
+ \value None Query returned zero items.
+ \value Querying Model has issued the query but not received a response.
+ \value Ready Model has received items.
+ \value Error Model received an error response from the partitions.
+*/
+
+QJsonDbQueryModel::QJsonDbQueryModel(QJsonDbConnection *dbConnection, QObject *parent)
+ : QAbstractListModel(parent)
+ , d_ptr(new QJsonDbQueryModelPrivate(this))
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+ d->init(dbConnection);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+QJsonDbQueryModel::~QJsonDbQueryModel()
+{
+}
+
+/*!
+ Loads objects matching the specified query from the specified
+ partitions into the model cache.
+*/
+void QJsonDbQueryModel::populate()
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+ d->componentComplete = true;
+ if (!d->query.isEmpty() && d->partitionObjects.count() && d->partitionsReady()) {
+ d->createOrUpdateNotifications();
+ d->fetchModel();
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+/*!
+ \property QJsonDbQueryModel::rowCount
+ Indicates how many items match the query. Value is zero unless state is Ready.
+*/
+int QJsonDbQueryModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ Q_D(const QJsonDbQueryModel);
+ return d->objectUuids.count();
+}
+
+/*!
+ Returns the \a role of the object at \a modelIndex.
+
+ \sa setQueryRoleNames()
+ */
+QVariant QJsonDbQueryModel::data(const QModelIndex &modelIndex, int role) const
+{
+ QVariant ret;
+ int index = modelIndex.row();
+ return data (index, role);
+}
+
+QVariant QJsonDbQueryModel::data(int index, int role) const
+{
+ Q_D(const QJsonDbQueryModel);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ QVariant ret;
+ if (index < 0 || index >= d->objectUuids.size())
+ ret = QVariant();
+ // Special synchronous handling for _uuid and _indexValue
+ else if (d->properties[role].at(0) == QLatin1String("_uuid")) {
+ JsonDbModelIndexType::const_iterator itr = d->objectUuids.constBegin() + index;
+ ret = itr.value();
+ }
+ else if (d->properties[role].at(0) == QLatin1String("_indexValue")) {
+ JsonDbModelIndexType::const_iterator itr = d->objectUuids.constBegin() + index;
+ ret = itr.key().value();
+ }
+ else
+ ret = const_cast<QJsonDbQueryModel*>(this)->d_func()->getItem(index, role);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+ return ret;
+}
+
+QHash<int, QByteArray> QJsonDbQueryModel::roleNames() const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->roleNames;
+}
+
+/*!
+ \property QJsonDbQueryModel::queryRoleNames
+
+ Controls which properties to expose from the objects matching the query.
+
+ Setting \a queryRoleNames to a list of strings causes the model to expose
+ corresponding object values as roles to the delegate for each item viewed.
+
+ \code
+ TBD
+ \endcode
+
+ Setting \a queryRoleNames to a dictionary remaps properties in the object
+ to the specified roles in the model.
+
+ In the following example, role \a a would yield the value of
+ property \a aLongName in the objects. Role \a liftedProperty would
+ yield the value of \a o.nested.property for each matching object \a
+ o in the database.
+
+ \code
+ TBD
+ \endcode
+
+ */
+
+QVariant QJsonDbQueryModel::queryRoleNames() const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->roleMap;
+}
+
+void QJsonDbQueryModel::setQueryRoleNames(const QVariant &vroles)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+ d->properties.clear();
+ d->roleNames.clear();
+ if (vroles.type() == QVariant::Map) {
+ QVariantMap roles = vroles.toMap();
+ d->roleMap = roles;
+ int i = 0;
+ for (QVariantMap::const_iterator it = roles.begin(); it != roles.end(); ++it) {
+ d->roleNames.insert(i, it.key().toLatin1());
+ d->properties.insert(i, removeArrayOperator(it.value().toString()).split('.'));
+ i++;
+ }
+ } else {
+ QVariantList roleList = vroles.toList();
+ d->roleMap.clear();
+ for (int i = 0; i < roleList.size(); i++) {
+ QString role = roleList[i].toString();
+ d->roleMap[role] = role;
+ d->roleNames.insert(i, role.toLatin1());
+ d->properties.insert(i, removeArrayOperator(role).split('.'));
+ }
+ }
+ QAbstractItemModel::setRoleNames(d->roleNames);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+/*!
+ \property QJsonDbQueryModel::query
+
+ The query string in JsonQuery format used by the model to fetch
+ items from the database. Setting an empty query clears all the elements
+
+ In the following example, the JsonDbCachingListModel would contain all
+ the objects with \a _type "CONTACT" from partition called "com.nokia.shared"
+
+ \sa QtJsonDb::QJsonDbQueryModel::bindings
+ */
+QString QJsonDbQueryModel::query() const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->query;
+}
+
+void QJsonDbQueryModel::setQuery(const QString &newQuery)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+
+ const QString oldQuery = d->query;
+ if (oldQuery == newQuery)
+ return;
+
+ d->query = newQuery;
+ if (rowCount() && d->query.isEmpty()) {
+ d->reset();
+ }
+
+ if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
+ return;
+ d->createOrUpdateNotifications();
+ d->fetchModel();
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+/*!
+ \property QJsonDbQueryModel::bindings()
+
+ Holds the bindings for the placeholders used in the query string. Note that
+ the placeholder marker '%' should not be included as part of the keys.
+
+ \sa QtJsonDb::QJsonDbQueryModel::query()
+ */
+QVariantMap QJsonDbQueryModel::bindings() const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->queryBindings;
+}
+
+void QJsonDbQueryModel::setBindings(const QVariantMap &newBindings)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+ d->queryBindings = newBindings;
+
+ if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
+ return;
+ d->createOrUpdateNotifications();
+ d->fetchModel();
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+
+/*!
+ \property QJsonDbQueryModel::cacheSize
+ Holds the maximum number of objects hold in memory by the model.
+*/
+int QJsonDbQueryModel::cacheSize() const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->cacheSize;
+}
+
+void QJsonDbQueryModel::setCacheSize(int newCacheSize)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+ if (newCacheSize == d->cacheSize)
+ return;
+
+ d->cacheSize = newCacheSize;
+ d->setCacheParams(d->cacheSize);
+ if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
+ return;
+
+ d->fetchModel();
+#ifdef JSONDB_LISTMODEL_DEBUG
+ d->objectCache.dumpCacheDetails();
+#endif
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+/*!
+ \property QJsonDbQueryModel::sortOrder
+
+ The order used by the model to sort the items. Make sure that there
+ is a matching Index in the database for this sortOrder. This has to be
+ specified in the JsonQuery format.
+
+ In the following example, the QJsonDbQueryModel would contain all
+ the objects of type \a "Contact" sorted by their \a firstName field
+
+ \qml
+ JsonDb.JsonDbCachingListModel {
+ id: listModel
+ query: "[?_type=\"Contact\"]"
+ partitions:[ JsonDb.Partition {
+ name:"com.nokia.shared"
+ }]
+ sortOrder: "[/firstName]"
+ }
+ \endqml
+
+ \sa QtJsonDb::QJsonDbQueryModel::bindings
+*/
+QString QJsonDbQueryModel::sortOrder() const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->sortOrder;
+}
+
+void QJsonDbQueryModel::setSortOrder(const QString &newSortOrder)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+
+ const QString oldSortOrder = d->sortOrder;
+ d->sortOrder = newSortOrder;
+ if (oldSortOrder != newSortOrder) {
+ d->parseSortOrder();
+ if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
+ return;
+ d->createOrUpdateNotifications();
+ d->fetchModel();
+ }
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+/*!
+ \property QJsonDbQueryModel::state
+ The current state of the model.
+ \list
+ \li QJsonDbQueryModel::None - The model is not initialized
+ \li QJsonDbQueryModel::Querying - It is querying the results from server
+ \li QJsonDbQueryModel::Ready - Results are ready
+ \li QJsonDbQueryModel::Error - Cannot find a matching index on the server
+ \endlist
+
+*/
+QJsonDbQueryModel::State QJsonDbQueryModel::state() const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->state;
+}
+
+/*!
+ Returns the index of the object with \a uuid from the model.
+
+ Becaues the model caches all uuids the index can be returned
+ immediately.
+
+ \sa QJsonDbQueryModel::uuid()
+*/
+int QJsonDbQueryModel::indexOf(const QString &uuid) const
+{
+ Q_D(const QJsonDbQueryModel);
+ return d->indexOf(uuid);
+}
+
+/*!
+ Fetches the object at position \a index in the model.
+
+ Becaues the model caches objects, it may not have a copy of the
+ object in memory. In that case, it queries the appropriate
+ partition to fetch the object.
+
+ The model emits signal objectAvailable() with \a index, the
+ object, and the name of the partition containing the object.
+
+ \sa QJsonDbQueryModel::objectAvailable()
+ \sa QJsonDbQueryModel::uuid()
+*/
+void QJsonDbQueryModel::fetchObject(int index)
+{
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ QElapsedTimer elt;
+ elt.start();
+#endif
+ Q_D(QJsonDbQueryModel);
+ if (index < 0 || index >= d->objectUuids.size())
+ return;
+ int page = d->objectCache.findPage(index);
+ if (page == -1) {
+ d->requestPageContaining(index);
+ return;
+ }
+ QJsonObject result = d->getJsonObject(index);
+ QString partitionName = d->getItemPartition(index);
+ emit objectAvailable(index, result, partitionName);
+#ifdef JSONDB_LISTMODEL_BENCHMARK
+ qint64 elap = elt.elapsed();
+ if (elap > 3)
+ qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
+#endif
+}
+
+
+/*!
+ Returns the name of partition at position \a index in the list of partition names used by the model.
+*/
+QString QJsonDbQueryModel::partitionName(int index) const
+{
+ QJsonDbQueryModel *pThis = const_cast<QJsonDbQueryModel *>(this);
+ return pThis->d_func()->getItemPartition(index);
+}
+
+/*!
+ Returns the list of partition names used by the model.
+*/
+QStringList QJsonDbQueryModel::partitionNames() const
+{
+ QJsonDbQueryModel *pThis = const_cast<QJsonDbQueryModel *>(this);
+ return QStringList(pThis->d_func()->partitionObjects);
+}
+
+/*!
+ Sets the list of partition names used by the model to \a partitionNames.
+*/
+void QJsonDbQueryModel::setPartitionNames(const QStringList &partitionNames)
+{
+ QJsonDbQueryModel *pThis = const_cast<QJsonDbQueryModel *>(this);
+ pThis->d_func()->clearPartitions();
+ foreach (const QString &partitionName, partitionNames) {
+ pThis->d_func()->appendPartition(partitionName);
+ }
+}
+
+/*!
+ Add a partition named \a partitionName to the list of partition names used by the model.
+*/
+void QJsonDbQueryModel::appendPartitionName(const QString &partitionName)
+{
+ Q_D(QJsonDbQueryModel);
+ d->appendPartition(partitionName);
+}
+
+/*!
+ \property QJsonDbQueryModel::error
+*/
+QVariantMap QJsonDbQueryModel::error() const
+{
+ Q_D(const QJsonDbQueryModel);
+ QVariantMap errorMap;
+ errorMap.insert(QLatin1String("code"), d->errorCode);
+ errorMap.insert(QLatin1String("message"), d->errorString);
+ return errorMap;
+}
+
+#include "moc_qjsondbquerymodel_p.cpp"
+QT_END_NAMESPACE_JSONDB
diff --git a/src/client/qjsondbquerymodel_p.h b/src/client/qjsondbquerymodel_p.h
new file mode 100644
index 0000000..b864783
--- /dev/null
+++ b/src/client/qjsondbquerymodel_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QJSONDBQUERYMODEL_P_H
+#define QJSONDBQUERYMODEL_P_H
+
+#include <QAbstractListModel>
+#include <QHash>
+#include <QMultiMap>
+#include <QSet>
+#include <QSharedDataPointer>
+#include <QStringList>
+#include <QScopedPointer>
+#include <QJsonObject>
+#include <QtJsonDb/qjsondbglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB
+
+class QJsonDbConnection;
+
+class QJsonDbQueryModelPrivate;
+
+class Q_JSONDB_EXPORT QJsonDbQueryModel: public QAbstractListModel
+{
+ Q_OBJECT
+ Q_ENUMS(State)
+public:
+ enum State { None, Querying, Ready, Error };
+
+ Q_PROPERTY(QVariantMap bindings READ bindings WRITE setBindings)
+ Q_PROPERTY(int cacheSize READ cacheSize WRITE setCacheSize)
+ Q_PROPERTY(QVariantMap error READ error NOTIFY errorChanged)
+ Q_PROPERTY(QString query READ query WRITE setQuery)
+ Q_PROPERTY(QVariant roleNames READ queryRoleNames WRITE setQueryRoleNames)
+ Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged)
+ Q_PROPERTY(QString sortOrder READ sortOrder WRITE setSortOrder)
+ Q_PROPERTY(State state READ state NOTIFY stateChanged)
+
+ QJsonDbQueryModel(QJsonDbConnection *dbConnection, QObject *parent = 0);
+ virtual ~QJsonDbQueryModel();
+
+ //From QAbstractItemModel
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ virtual QVariant data(int index, int role = Qt::DisplayRole) const;
+ virtual QHash<int,QByteArray> roleNames() const;
+
+ QVariantMap error() const;
+
+ QVariantMap bindings() const;
+ void setBindings(const QVariantMap &newBindings);
+ int cacheSize() const;
+ void setCacheSize(int newCacheSize);
+
+ int indexOf(const QString &uuid) const;
+ // fetchObject is async (see objectAvailable -signal) as the object at
+ // specifiec index might not be in model cache
+ void fetchObject(int index);
+
+ QString query() const;
+ void setQuery(const QString &newQuery);
+
+ QVariant queryRoleNames() const;
+ void setQueryRoleNames(const QVariant &roles);
+
+ QString sortOrder() const;
+ void setSortOrder(const QString &newSortOrder);
+
+ State state() const;
+
+ void populate();
+
+ //Partition handling
+ QString partitionName(int index) const;
+ QStringList partitionNames() const;
+ void setPartitionNames(const QStringList &partitions);
+ void appendPartitionName(const QString &partitionName);
+
+Q_SIGNALS:
+ void stateChanged(State state) const;
+ void rowCountChanged(int newCount) const;
+ void errorChanged(QVariantMap);
+ void objectAvailable(int index, QJsonObject availableObject, QString objectPartition);
+
+private:
+ Q_DISABLE_COPY(QJsonDbQueryModel)
+ Q_DECLARE_PRIVATE(QJsonDbQueryModel)
+ QScopedPointer<QJsonDbQueryModelPrivate> d_ptr;
+
+ Q_PRIVATE_SLOT(d_func(), void _q_verifyDefaultIndexType(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_notificationsAvailable())
+ Q_PRIVATE_SLOT(d_func(), void _q_partitionWatcherNotificationsAvailable())
+ Q_PRIVATE_SLOT(d_func(), void _q_notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode, QString))
+ Q_PRIVATE_SLOT(d_func(), void _q_partitionWatcherNotificationError(QtJsonDb::QJsonDbWatcher::ErrorCode, QString))
+ Q_PRIVATE_SLOT(d_func(), void _q_partitionObjectQueryFinished())
+ Q_PRIVATE_SLOT(d_func(), void _q_partitionObjectQueryError())
+ Q_PRIVATE_SLOT(d_func(), void _q_keyResponse(int, QList<QJsonObject>, QString))
+ Q_PRIVATE_SLOT(d_func(), void _q_valueResponse(int, QList<QJsonObject>))
+ Q_PRIVATE_SLOT(d_func(), void _q_indexResponse(int, QList<QJsonObject>))
+ Q_PRIVATE_SLOT(d_func(), void _q_readError(QtJsonDb::QJsonDbRequest::ErrorCode, QString))
+};
+
+QT_END_HEADER
+
+QT_END_NAMESPACE_JSONDB
+
+#endif // QJSONDBQUERYMODEL_P_H
diff --git a/src/imports/jsondb/jsondbcachinglistmodel_p.h b/src/client/qjsondbquerymodel_p_p.h
index cf9d707..f5959a3 100644
--- a/src/imports/jsondb/jsondbcachinglistmodel_p.h
+++ b/src/client/qjsondbquerymodel_p_p.h
@@ -40,8 +40,8 @@
****************************************************************************/
-#ifndef JSONDBCACHINGLISTMODEL_P_H
-#define JSONDBCACHINGLISTMODEL_P_H
+#ifndef QJSONDBQUERYMODEL_P_P_H
+#define QJSONDBQUERYMODEL_P_P_H
#include <QHash>
#include <QMultiMap>
@@ -52,10 +52,11 @@
#include <QUuid>
#include <QJsonObject>
-#include "jsondatabase.h"
-#include "jsondbcachinglistmodel.h"
-#include "jsondbmodelutils.h"
-#include "jsondbmodelcache.h"
+#include "qjsondbquerymodel_p.h"
+#include "qjsondbmodelutils_p.h"
+#include "qjsondbmodelcache_p.h"
+
+QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE_JSONDB
@@ -72,14 +73,23 @@ struct JsonDbModelIndexNSize
}
};
-class JsonDbCachingListModelPrivate
+struct JsonDbAvailablePartitionsInfo;
+
+class QJsonDbQueryModelPrivate
{
- Q_DECLARE_PUBLIC(JsonDbCachingListModel)
+ Q_DECLARE_PUBLIC(QJsonDbQueryModel)
+
public:
- JsonDbCachingListModel *q_ptr;
+ enum JsonDbPartitionState { PartitionStateNone,
+ PartitionStateOnline,
+ PartitionStateOffline,
+ PartitionStateError };
+ QJsonDbQueryModel *q_ptr;
+ QJsonDbConnection *mConnection;
QList<RequestInfo> partitionObjectDetails;
- QList<QPointer<JsonDbPartition> >partitionObjects;
+ QList<QString> partitionObjects;
+ QList<JsonDbAvailablePartitionsInfo> availablePartitions;
bool componentComplete;
bool resetModel;
@@ -112,14 +122,14 @@ public:
QHash<int, QStringList> properties;
QList<NotificationItem> pendingNotifications;
+ QList<NotificationItem> pendingPartitionObjectNotifications;
QList<int> cacheMiss;
- QMap<int, QJSValue> getCallbacks;
QList< QPair<int,int> > requestQueue;
QList< QPointer<ModelRequest> >keyRequests;
QList< QPointer<ModelRequest> >indexRequests;
QList< QPointer<ModelRequest> >valueRequests;
- JsonDbCachingListModel::State state;
+ QJsonDbQueryModel::State state;
QModelIndex parent;
int errorCode;
QString errorString;
@@ -132,10 +142,9 @@ public:
QJsonObject lastQueriedObject;
public:
- JsonDbCachingListModelPrivate(JsonDbCachingListModel *q);
- ~JsonDbCachingListModelPrivate();
- void init();
- bool partitionsReady();
+ QJsonDbQueryModelPrivate(QJsonDbQueryModel *q);
+ ~QJsonDbQueryModelPrivate();
+ void init(QJsonDbConnection *dbConnection);
void setCacheParams(int maxItems);
void createObjectRequests(int startIndex, int maxItems);
@@ -170,39 +179,51 @@ public:
void verifyIndexSpec(const QList<QJsonObject> &items, int partitionIndex);
int indexOfWatcher(QJsonDbWatcher *watcher);
+ int indexOfPartitionObjectWatcher(QJsonDbWatcher *watcher);
- void initPartition(JsonDbPartition *v);
- void appendPartition(JsonDbPartition *v);
+ void appendPartition(const QString& partitionName);
void clearPartitions();
+ void startWatchingPartitionObject(const QString& partitionName);
+ void onPartitionStateChanged();
+ bool partitionsReady();
QJsonObject getJsonObject(int index);
QVariant getItem(int index);
QVariant getItem(int index, int role);
- void queueGetCallback(int index, const QJSValue &callback);
- void callGetCallback(int index, QJSValue callback);
- JsonDbPartition* getItemPartition(int index);
+ QString getItemPartition(int index);
int indexOf(const QString &uuid) const;
- void set(int index, const QJSValue& valuemap,
- const QJSValue &successCallback,
- const QJSValue &errorCallback);
void sendNotification(int partitionIndex, const QJsonObject &object, QJsonDbWatcher::Action action);
// private slots
void _q_verifyDefaultIndexType(int index);
void _q_notificationsAvailable();
+ void _q_partitionWatcherNotificationsAvailable();
+ void _q_partitionWatcherNotificationError(QtJsonDb::QJsonDbWatcher::ErrorCode code, const QString &message);
void _q_notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode code, const QString &message);
+ void _q_partitionObjectQueryFinished();
+ void _q_partitionObjectQueryError();
void _q_readError(QtJsonDb::QJsonDbRequest::ErrorCode code, const QString & message);
void _q_keyResponse(int , const QList<QJsonObject>&, const QString&);
void _q_valueResponse(int , const QList<QJsonObject>&);
void _q_indexResponse(int , const QList<QJsonObject>&);
- void _q_partitionStateChanged(JsonDbPartition::State state);
-
- static void partitions_append(QQmlListProperty<JsonDbPartition> *p, JsonDbPartition *v);
- static int partitions_count(QQmlListProperty<JsonDbPartition> *p);
- static JsonDbPartition* partitions_at(QQmlListProperty<JsonDbPartition> *p, int idx);
- static void partitions_clear(QQmlListProperty<JsonDbPartition> *p);
+};
+struct JsonDbAvailablePartitionsInfo
+{
+ QPointer<QJsonDbWatcher> watcher;
+ QJsonDbQueryModelPrivate::JsonDbPartitionState state;
+ JsonDbAvailablePartitionsInfo() { clear();}
+ void clear()
+ {
+ state = QJsonDbQueryModelPrivate::PartitionStateNone;
+ if (watcher) {
+ delete watcher;
+ watcher = 0;
+ }
+ }
};
QT_END_NAMESPACE_JSONDB
-#endif // JSONDBCACHINGLISTMODEL_P_H
+QT_END_HEADER
+
+#endif // QJSONDBQUERYMODEL_P_P_H
diff --git a/src/imports/jsondb/jsondb.pro b/src/imports/jsondb/jsondb.pro
index 580c5d9..586aa1b 100644
--- a/src/imports/jsondb/jsondb.pro
+++ b/src/imports/jsondb/jsondb.pro
@@ -34,13 +34,10 @@ HEADERS += \
plugin.h \
jsondatabase.h \
jsondbqueryobject.h \
- jsondbmodelutils.h \
- jsondbmodelcache.h \
jsondblistmodel.h \
jsondblistmodel_p.h \
jsondbsortinglistmodel_p.h \
jsondbsortinglistmodel.h \
- jsondbcachinglistmodel_p.h \
jsondbcachinglistmodel.h
SOURCES += \
@@ -49,8 +46,6 @@ SOURCES += \
plugin.cpp \
jsondatabase.cpp \
jsondbqueryobject.cpp \
- jsondbmodelutils.cpp \
- jsondbmodelcache.cpp \
jsondblistmodel.cpp \
jsondbsortinglistmodel.cpp \
jsondbcachinglistmodel.cpp
diff --git a/src/imports/jsondb/jsondbcachinglistmodel.cpp b/src/imports/jsondb/jsondbcachinglistmodel.cpp
index ae87edd..e2a338c 100644
--- a/src/imports/jsondb/jsondbcachinglistmodel.cpp
+++ b/src/imports/jsondb/jsondbcachinglistmodel.cpp
@@ -42,9 +42,8 @@
//#define JSONDB_LISTMODEL_DEBUG
//#define JSONDB_LISTMODEL_BENCHMARK
-#include "jsondbcachinglistmodel.h"
-#include "jsondbcachinglistmodel_p.h"
#include "plugin.h"
+#include "jsondbcachinglistmodel.h"
#include <QJSEngine>
#include <QJSValueIterator>
@@ -60,1310 +59,189 @@
QT_BEGIN_NAMESPACE_JSONDB
-JsonDbCachingListModelPrivate::JsonDbCachingListModelPrivate(JsonDbCachingListModel *q)
- : q_ptr(q)
- , componentComplete(false)
- , resetModel(true)
- , cacheSize(-1)
- , state(JsonDbCachingListModel::None)
- , errorCode(0)
- , lastQueriedIndex(-1)
-{
- setCacheParams(INT_MAX/10);
-}
-
-void JsonDbCachingListModelPrivate::init()
-{
-}
-
-void JsonDbCachingListModelPrivate::setCacheParams(int maxItems)
-{
- objectCache.setPageSize(maxItems);
- chunkSize = objectCache.chunkSize();
- lowWaterMark = objectCache.chunkSize()/4;
-}
-
-JsonDbCachingListModelPrivate::~JsonDbCachingListModelPrivate()
-{
- clearNotifications();
- while (!keyRequests.isEmpty()) {
- delete keyRequests[0];
- keyRequests.removeFirst();
- }
- while (!indexRequests.isEmpty()) {
- delete indexRequests[0];
- indexRequests.removeFirst();
- }
- while (!valueRequests.isEmpty()) {
- delete valueRequests[0];
- valueRequests.removeFirst();
- }
-
-}
-
-bool JsonDbCachingListModelPrivate::partitionsReady()
-{
- for (int i = 0; i < partitionObjects.count(); i++) {
- if (partitionObjects[i]->state() == JsonDbPartition::None || partitionObjects[i]->state() == JsonDbPartition::Error)
- return false;
- }
- return true;
-}
-
-// insert item notification handler
-// + add items, for chunked read
-void JsonDbCachingListModelPrivate::addItem(const QJsonObject &item, int partitionIndex)
-{
- Q_Q(JsonDbCachingListModel);
- const QString &uuid = item.value(QLatin1String("_uuid")).toString();
- // ignore duplicates.
- if (objectSortValues.contains(uuid))
- return;
-
- QVariantList vl;
- vl.append(uuid);
- vl.append(item.value(QLatin1String("_indexValue")).toVariant());
- SortingKey key(partitionIndex, vl, QList<bool>() << ascendingOrder, partitionIndexDetails[partitionIndex].spec);
- QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
- QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
- QMap<SortingKey, QString>::const_iterator i = objectUuids.upperBound(key);
- int index = iterator_position(begin, end, i);
- if (index <= lastQueriedIndex)
- lastQueriedIndex++;
-
- q->beginInsertRows(parent, index, index);
- objectUuids.insert(key, uuid);
- objectCache.insert(index, uuid, item, objectUuids);
- partitionObjectUuids[partitionIndex].insert(key, uuid);
- objectSortValues.insert(uuid, key);
- q->endInsertRows();
- emit q->rowCountChanged(objectSortValues.count());
-}
-
-
-// deleteitem notification handler
-void JsonDbCachingListModelPrivate::deleteItem(const QJsonObject &item, int partitionIndex)
-{
- Q_Q(JsonDbCachingListModel);
- QString uuid = item.value(QLatin1String("_uuid")).toString();
- QMap<QString, SortingKey>::const_iterator keyIndex = objectSortValues.constFind(uuid);
- if (keyIndex != objectSortValues.constEnd()) {
- SortingKey key = keyIndex.value();
- QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
- QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
- QMap<SortingKey, QString>::const_iterator i = objectUuids.constFind(key);
- if (i != end) {
- int index = iterator_position(begin, end, i);
- q->beginRemoveRows(parent, index, index);
- objectCache.remove(index, uuid);
- partitionObjectUuids[partitionIndex].remove(key);
- objectUuids.remove(key);
- objectSortValues.remove(uuid);
- if (index == lastQueriedIndex)
- lastQueriedIndex = -1;
- else if (index < lastQueriedIndex)
- lastQueriedIndex--;
- q->endRemoveRows();
- emit q->rowCountChanged(objectUuids.count());
- }
- }
-}
-
-// updateitem notification handler
-void JsonDbCachingListModelPrivate::updateItem(const QJsonObject &item, int partitionIndex)
-{
- Q_Q(JsonDbCachingListModel);
- QString uuid = item.value(QLatin1String("_uuid")).toString();
- QMap<QString, SortingKey>::const_iterator keyIndex = objectSortValues.constFind(uuid);
- if (keyIndex != objectSortValues.constEnd()) {
- SortingKey key = keyIndex.value();
- QVariantList vl;
- vl.append(uuid);
- vl.append(item.value(QLatin1String("_indexValue")).toVariant());
- SortingKey newKey(partitionIndex, vl, QList<bool>() << ascendingOrder, partitionIndexDetails[partitionIndex].spec);
- QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
- QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
- QMap<SortingKey, QString>::const_iterator oldPos = objectUuids.constFind(key);
- int oldIndex = iterator_position(begin, end, oldPos);
- if (oldIndex == lastQueriedIndex) // Cached object has changed
- lastQueriedIndex = -1;
- // keys are same, modify the object
- if (key == newKey) {
- objectCache.update(uuid, item);
- QModelIndex modelIndex = q->createIndex(oldIndex, 0);
- emit q->dataChanged(modelIndex, modelIndex);
- return;
- }
- // keys are different
- QMap<SortingKey, QString>::const_iterator newPos = objectUuids.upperBound(newKey);
- int newIndex = iterator_position(begin, end, newPos);
- if ((newIndex != oldIndex) && (newIndex != oldIndex+1)) {
- if (oldIndex < lastQueriedIndex && newIndex > lastQueriedIndex)
- lastQueriedIndex--;
- else if (oldIndex > lastQueriedIndex && newIndex <= lastQueriedIndex)
- lastQueriedIndex++;
- q->beginMoveRows(parent, oldIndex, oldIndex, parent, newIndex);
- objectUuids.remove(key);
- partitionObjectUuids[partitionIndex].remove(key);
- objectCache.remove(oldIndex, uuid);
-
- objectUuids.insert(newKey, uuid);
- partitionObjectUuids[partitionIndex].insert(newKey, uuid);
-
- objectSortValues.remove(uuid);
- objectSortValues.insert(uuid, newKey);
-
- // recompute the new position
- newPos = objectUuids.constFind(newKey);
- begin = objectUuids.constBegin();
- end = objectUuids.constEnd();
- newIndex = iterator_position(begin, end, newPos);
- objectCache.insert(newIndex, uuid, item, objectUuids);
- q->endMoveRows();
- // send data changed and return
- QModelIndex modelIndex = q->createIndex(newIndex, 0);
- emit q->dataChanged(modelIndex, modelIndex);
- } else {
- // same position, update the object
- objectCache.update(uuid, item);
- objectUuids.remove(key);
- objectUuids.insert(newKey, uuid);
- partitionObjectUuids[partitionIndex].remove(key);
- partitionObjectUuids[partitionIndex].insert(newKey, uuid);
- objectSortValues.remove(uuid);
- objectSortValues.insert(uuid, newKey);
- newPos = objectUuids.constFind(newKey);
- begin = objectUuids.constBegin();
- end = objectUuids.constEnd();
- newIndex = iterator_position(begin, end, newPos);
- QModelIndex modelIndex = q->createIndex(newIndex, 0);
- emit q->dataChanged(modelIndex, modelIndex);
- }
- } else {
- addItem(item, partitionIndex);
- }
-}
-
-int findIndexOf(const JsonDbModelIndexType::const_iterator &begin, const SortingKey &key, int low, int high)
-{
- if (high < low)
- return -1;
- int mid = (low + high) / 2;
- JsonDbModelIndexType::const_iterator midItr = begin + mid;
- if (midItr.key() == key) // ==
- return mid;
- else if (midItr.key() < key) // <
- return findIndexOf(begin, key, mid+1, high);
- else // >
- return findIndexOf(begin, key, low, mid-1);
-}
-
-inline void setQueryBindings(QJsonDbReadRequest *request, const QVariantMap &bindings)
-{
- QVariantMap::ConstIterator i = bindings.constBegin();
- while (i != bindings.constEnd()) {
- request->bindValue(i.key(), QJsonValue::fromVariant(i.value()));
- ++i;
- }
-}
-
-void JsonDbCachingListModelPrivate::createObjectRequests(int startIndex, int maxItems)
-{
- Q_Q(JsonDbCachingListModel);
-
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<Q_FUNC_INFO<<startIndex<<maxItems<<objectUuids.count();
-#endif
- Q_ASSERT(startIndex>=0);
-
- if (startIndex >= objectUuids.size())
- return;
- if ((startIndex + maxItems) > objectUuids.size())
- maxItems = objectUuids.size() - startIndex;
-
- if (state == JsonDbCachingListModel::Querying &&
- currentCacheRequest.index == startIndex &&
- currentCacheRequest.count == maxItems) {
- // we are fetching the same set, skip this
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<"Skip this request";
-#endif
- return;
- }
- currentCacheRequest.index = startIndex;
- currentCacheRequest.count = maxItems;
-
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<"startIndex"<<startIndex<<" maxItems "<<maxItems;
-#endif
- JsonDbModelIndexNSize *indexNSizes = new JsonDbModelIndexNSize[partitionObjects.count()] ;
- JsonDbModelIndexType::const_iterator itr = objectUuids.constBegin()+startIndex;
- for (int i = startIndex; i < startIndex+maxItems; i++, itr++) {
- const SortingKey &key = itr.key();
- int index = key.partitionIndex();
- if (indexNSizes[index].index == -1) {
- indexNSizes[index].index = findIndexOf(partitionObjectUuids[index].constBegin(),
- key, 0, partitionObjectUuids[index].count()-1);
- Q_ASSERT(indexNSizes[index].index != -1);
- }
- indexNSizes[index].count++;
- }
- for (int i = 0; i < partitionObjects.count(); i++) {
- RequestInfo &r = partitionObjectDetails[i];
- if (indexNSizes[i].count) {
- if (state != JsonDbCachingListModel::Querying) {
- state = JsonDbCachingListModel::Querying;
- emit q->stateChanged(state);
- }
-
- r.lastOffset = indexNSizes[i].index;
- r.lastSize = -1;
- r.requestCount = indexNSizes[i].count;
- QJsonDbReadRequest *request = valueRequests[i]->newRequest(i);
- request->setQuery(query+sortOrder);
- request->setProperty("queryOffset", indexNSizes[i].index);
- request->setQueryLimit(qMin(r.requestCount, chunkSize));
- request->setPartition(partitionObjects[i]->name());
- setQueryBindings(request, queryBindings);
- JsonDatabase::sharedConnection().send(request);
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<"Query"<<query+sortOrder<<partitionObjects[i]->name();
- qDebug()<<"Request "<<request->property("requestId") <<
- "Total Count "<<r.requestCount <<
- "Offset"<<r.lastOffset<<
- "Count "<<qMin(r.requestCount,chunkSize);
-#endif
- } else {
- r.lastSize = 0;
- r.requestCount = 0;
- }
- }
- delete [] indexNSizes;
-}
-
-void JsonDbCachingListModelPrivate::verifyIndexSpec(const QList<QJsonObject> &items, int partitionIndex)
-{
- Q_Q(JsonDbCachingListModel);
- SortIndexSpec &indexSpec = partitionIndexDetails[partitionIndex].spec;
- bool validIndex = false;
- if (items.count()) {
- for (int i = 0; i < items.length() && !validIndex; i++) {
- QJsonObject spec = items[i];
- indexSpec.propertyName = QLatin1String("_indexValue");
- QString propertyType = spec.value(QLatin1String("propertyType")).toString();
- indexSpec.name = spec.value(QLatin1String("name")).toString();
- if (indexSpec.name.isEmpty())
- indexSpec.name = spec.value(QLatin1String("propertyName")).toString();
- indexSpec.caseSensitive = true;
- if (!indexName.isEmpty()) {
- if (indexSpec.name == indexName) {
- if (!propertyType.compare(QLatin1String("string"), Qt::CaseInsensitive)) {
- indexSpec.type = SortIndexSpec::String;
- if (spec.value(QLatin1String("caseSensitive")).isBool())
- indexSpec.caseSensitive = spec.value(QLatin1String("caseSensitive")).toBool();
- validIndex = true;
- } else if (!propertyType.compare(QLatin1String("number"), Qt::CaseInsensitive)) {
- indexSpec.type = SortIndexSpec::Number;
- validIndex = true;
- } else if (!propertyType.compare(QLatin1String("UUID"), Qt::CaseInsensitive)) {
- indexSpec.type = SortIndexSpec::UUID;
- indexSpec.caseSensitive = false;
- validIndex = true;
- }
- }
- }
- }
- }
- if (!validIndex) {
- qWarning() << "Error JsonDbCachingListModel requires a supported Index for "<<indexName << partitionObjects[partitionIndex]->name();
- reset();
- state = JsonDbCachingListModel::Error;
- emit q->stateChanged(state);
- } else {
- partitionIndexDetails[partitionIndex].valid = true;
- //Check if all index specs are supported.
- bool checkedAll = true;
- for (int i = 0; i < partitionIndexDetails.count(); i++) {
- if (partitionObjects[i]->state() != JsonDbPartition::Online)
- continue;
- if (!partitionIndexDetails[i].valid) {
- checkedAll = false;
- break;
- }
- }
- if (checkedAll) {
- //Start fetching the keys.
- setQueryForSortKeys();
- for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
- fetchPartitionKeys(i);
- }
- }
- }
-}
-
-void JsonDbCachingListModelPrivate::fillKeys(const QList<QJsonObject> &items, int partitionIndex)
-{
- RequestInfo &r = partitionKeyRequestDetails[partitionIndex];
- r.lastSize = items.size();
- for (int i = 0; i < r.lastSize; i++) {
- const QJsonObject &item = items.at(i);
- QString uuidStr = item.value(QLatin1String("_uuid")).toString();
- QByteArray uuid = QUuid(uuidStr).toRfc4122();
- SortingKey key(partitionIndex, uuid, item.value(QLatin1String("_indexValue")).toVariant(), ascendingOrder, partitionIndexDetails[partitionIndex].spec);
- objectUuids.insert(key, uuidStr);
- partitionObjectUuids[partitionIndex].insert(key, uuidStr);
- objectSortValues.insert(uuidStr, key);
-
- }
- // Check if requests from different partitions returned
- // all the results
- bool allRequestsFinished = true;
- for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
- if (partitionObjects[i]->state() != JsonDbPartition::Online)
- continue;
- if (partitionKeyRequestDetails[i].lastSize >= chunkSize || partitionKeyRequestDetails[i].lastSize == -1) {
- allRequestsFinished = false;
- break;
- }
- }
- if (allRequestsFinished) {
- // retrieve the first chunk of data
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<"All Keys Received "<<objectUuids.count();
-#endif
- if (!objectUuids.count()) {
- for (int i = 0; i<partitionObjectDetails.count(); i++) {
- fillData(QList<QJsonObject>(), i);
- }
- return;
- }
- createObjectRequests(0, qMin(objectCache.maxItems(), objectUuids.count()));
- } else if (r.lastSize >= chunkSize){
- // more items, fetch next chunk of keys
- fetchNextKeyChunk(partitionIndex);
- }
-}
-
-void JsonDbCachingListModelPrivate::emitDataChanged(int from, int to)
-{
- Q_Q(JsonDbCachingListModel);
- QModelIndex modelIndexFrom = q->createIndex(from, 0);
- QModelIndex modelIndexTo = q->createIndex(to, 0);
- emit q->dataChanged(modelIndexFrom, modelIndexTo);
-}
-
-void JsonDbCachingListModelPrivate::fillData(const QList<QJsonObject> &items, int partitionIndex)
-{
- Q_Q(JsonDbCachingListModel);
- RequestInfo &r = partitionObjectDetails[partitionIndex];
- r.lastSize = items.size();
- r.requestCount -= r.lastSize;
- r.lastOffset += r.lastSize;
- for (int i = 0; i < r.lastSize; i++) {
- const QJsonObject &item = items.at(i);
- const QString &uuid = item.value(QLatin1String("_uuid")).toString();
- tmpObjects.insert(uuid, item);
- }
+/*!
+ \qmlclass JsonDbCachingListModel JsonDbCachingListModel
+ \inqmlmodule QtJsonDb
+ \since 1.0
- // Check if requests from different partitions returned
- // all the results
- bool allRequestsFinished = true;
- for (int i = 0; i < partitionObjectDetails.count(); i++) {
- if (partitionObjects[i]->state() != JsonDbPartition::Online)
- continue;
- if (partitionObjectDetails[i].lastSize >= chunkSize || partitionObjectDetails[i].lastSize == -1) {
- allRequestsFinished = false;
- break;
- }
- }
- if (allRequestsFinished) {
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<"Finished Req For:"<<currentCacheRequest.index<<currentCacheRequest.count;
- qDebug()<<"Finished Req recieved count: "<<tmpObjects.count()<<" Total Items:"<<objectUuids.count();
-#endif
- objectCache.addObjects(currentCacheRequest.index, objectUuids, tmpObjects);
- tmpObjects.clear();
- JsonDbModelIndexNSize req = currentCacheRequest;
- currentCacheRequest.clear();
- // send the update for missed items
- QList<int> pendingCacheMiss;
- int changedFrom = -1, changedTo = -2;
- for (int i = 0; i < cacheMiss.size(); i++) {
- if (cacheMiss[i] >= req.index &&
- cacheMiss[i] < req.index + req.count) {
- if (changedFrom >= 0 && cacheMiss[i] != changedTo+1) {
- emitDataChanged(changedFrom, changedTo);
- changedFrom = -1;
- }
- if (changedFrom < 0) changedFrom = cacheMiss[i];
- changedTo = cacheMiss[i];
- } else {
- pendingCacheMiss.append(cacheMiss[i]);
- }
- }
- if (changedFrom >= 0)
- emitDataChanged(changedFrom, changedTo);
- cacheMiss.clear();
- cacheMiss = pendingCacheMiss;
- // call the get callback
- QMap<int, QJSValue> pendingGetCallbacks;
- QMapIterator<int, QJSValue> itr(getCallbacks);
- while (itr.hasNext()) {
- itr.next();
- int index = itr.key();
- if (index >= req.index && index < req.index + req.count) {
- callGetCallback(index, itr.value());
- } else {
- pendingGetCallbacks.insert(index, itr.value());
- }
- }
- getCallbacks.clear();
- getCallbacks = pendingGetCallbacks;
- if (resetModel) {
- q->beginResetModel();
- q->endResetModel();
- emit q->rowCountChanged(objectUuids.count());
- resetModel = false;
- }
- // retrieved all elements
- state = JsonDbCachingListModel::Ready;
- emit q->stateChanged(state);
- if (!pendingNotifications.isEmpty()) {
- foreach (NotificationItem pending, pendingNotifications)
- sendNotification(pending.partitionIndex, pending.item, pending.action);
- pendingNotifications.clear();
- }
- if (requestQueue.count()) {
- QPair<int, int> req = requestQueue.takeFirst();
- createObjectRequests(req.first, req.second);
- }
- } else if (r.lastSize >= chunkSize){
- // more items, fetch next chunk
- fetchNextChunk(partitionIndex);
- }
-}
+ The JsonDbCachingListModel provides a read-only ListModel usable with views such as
+ ListView or GridView displaying data items matching a query. The sorting is done using
+ an index set on the JsonDb server. If it doesn't find a matching index for the sortkey,
+ the model goes into Error status. Maximum number of items in the model cache can be set
+ by cacheSize property.
+ When an item is not present in the internal cache, the model can return an 'undefined'
+ object from data() method. It will be queued for retrieval and the model will notify its
+ presence using the dataChanged() signal.
-//Clears all the state information.
-void JsonDbCachingListModelPrivate::reset()
-{
- Q_Q(JsonDbCachingListModel);
- lastQueriedIndex = -1;
- q->beginResetModel();
- clearNotifications();
- for (int i = 0; i < partitionObjectDetails.count(); i++) {
- partitionObjectDetails[i].clear();
- }
- for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
- partitionKeyRequestDetails[i].clear();
- }
- for (int i = 0; i < partitionIndexDetails.count(); i++) {
- partitionIndexDetails[i].clear();
- }
- for (int i = 0; i < partitionObjectUuids.count(); i++) {
- partitionObjectUuids[i].clear();
- }
+ The model is initialized by retrieving the result in chunks. After receiving the first
+ chunk, the model is reset with items from it. The status will be "Querying" during
+ fetching data and will be changed to "Ready".
- objectCache.clear();
- objectUuids.clear();
- objectSortValues.clear();
- currentCacheRequest.clear();
- cacheMiss.clear();
- getCallbacks.clear();
- requestQueue.clear();
- q->endResetModel();
- emit q->rowCountChanged(0);
- state = JsonDbCachingListModel::None;
- emit q->stateChanged(state);
-}
+ \note This is still a work in progress, so expect minor changes.
-bool JsonDbCachingListModelPrivate::checkForDefaultIndexTypes(int index)
-{
- Q_Q(JsonDbCachingListModel);
- bool defaultType = false;
- if (!indexName.compare(QLatin1String("_uuid")) || !indexName.compare(QLatin1String("_type"))) {
- defaultType = true;
- QMetaObject::invokeMethod(q, "_q_verifyDefaultIndexType", Qt::QueuedConnection,
- QGenericReturnArgument(),
- Q_ARG(int, index));
+ \code
+ JsonDb.JsonDbCachingListModel {
+ id: listModel
+ query: "[?_type=\"CONTACT\"]"
+ cacheSize: 100
+ partitions: [JsonDb.Partition {
+ name:"com.nokia.shared"
+ }]
}
- return defaultType;
-}
+ \endcode
+*/
-void JsonDbCachingListModelPrivate::fetchIndexSpec(int index)
+JsonDbCachingListModel::JsonDbCachingListModel(QObject *parent):
+ QJsonDbQueryModel(QJsonDbConnection::defaultConnection(), parent)
{
- Q_Q(JsonDbCachingListModel);
- if (index >= partitionObjects.count())
- return;
- if (checkForDefaultIndexTypes(index))
- return;
- if (state != JsonDbCachingListModel::Querying) {
- state = JsonDbCachingListModel::Querying;
- emit q->stateChanged(state);
- }
- QPointer<JsonDbPartition> p = partitionObjects[index];
- if (p && p->state() == JsonDbPartition::Online) {
- QJsonDbReadRequest *request = indexRequests[index]->newRequest(index);
- request->setQuery(queryForIndexSpec);
- request->setPartition(p->name());
- JsonDatabase::sharedConnection().send(request);
- }
+ QJsonDbConnection::defaultConnection()->connectToServer();
}
-void JsonDbCachingListModelPrivate::fetchPartitionKeys(int index)
+JsonDbCachingListModel::~JsonDbCachingListModel()
{
- Q_Q(JsonDbCachingListModel);
- if (index >= partitionObjects.count())
- return;
-
- if (state != JsonDbCachingListModel::Querying) {
- state = JsonDbCachingListModel::Querying;
- emit q->stateChanged(state);
- }
- RequestInfo &r = partitionKeyRequestDetails[index];
- QPointer<JsonDbPartition> p = partitionObjects[index];
- if (p && p->state() == JsonDbPartition::Online) {
- r.lastSize = -1;
- r.lastOffset = 0;
- QJsonDbReadRequest *request = keyRequests[index]->newRequest(index);
- request->setQuery(queryForSortKeys);
- request->setQueryLimit(chunkSize);
- request->setPartition(p->name());
- setQueryBindings(request, queryBindings);
- JsonDatabase::sharedConnection().send(request);
- }
}
-void JsonDbCachingListModelPrivate::initializeModel(bool reset)
-{
- resetModel = reset;
- if (resetModel) {
- objectCache.clear();
- objectUuids.clear();
- objectSortValues.clear();
- for (int i = 0; i < partitionObjectUuids.count(); i++) {
- partitionObjectUuids[i].clear();
- }
- for (int i = 0; i < partitionIndexDetails.count(); i++) {
- partitionIndexDetails[i].clear();
- }
- }
- for (int i = 0; i < partitionObjects.count(); i++) {
- fetchIndexSpec(i);
- }
-}
+//---------------- METHODS------------------------------
-void JsonDbCachingListModelPrivate::fetchModel(bool reset)
+void JsonDbCachingListModel::classBegin()
{
- parseSortOrder();
- initializeModel(reset);
}
-void JsonDbCachingListModelPrivate::fetchNextKeyChunk(int partitionIndex)
+void JsonDbCachingListModel::componentComplete()
{
- RequestInfo &r = partitionKeyRequestDetails[partitionIndex];
- r.lastOffset += chunkSize;
- QJsonDbReadRequest *request = keyRequests[partitionIndex]->newRequest(partitionIndex);
- request->setQuery(queryForSortKeys);
- request->setProperty("queryOffset", r.lastOffset);
- request->setQueryLimit(chunkSize);
- request->setPartition(partitionObjects[partitionIndex]->name());
- setQueryBindings(request, queryBindings);
- JsonDatabase::sharedConnection().send(request);
-
+ populate();
+ connect (this, SIGNAL(objectAvailable(int,QJsonObject,QString)), this, SLOT(onObjectAvailable(int,QJsonObject,QString)));
}
-void JsonDbCachingListModelPrivate::fetchNextChunk(int partitionIndex)
-{
- RequestInfo &r = partitionObjectDetails[partitionIndex];
- QJsonDbReadRequest *request = valueRequests[partitionIndex]->newRequest(partitionIndex);
- request->setQuery(query+sortOrder);
- request->setProperty("queryOffset", r.lastOffset);
- request->setQueryLimit(qMin(r.requestCount, chunkSize));
- request->setPartition(partitionObjects[partitionIndex]->name());
- setQueryBindings(request, queryBindings);
- JsonDatabase::sharedConnection().send(request);
-
-}
+/*!
+ \qmlmethod QtJsonDb::JsonDbCachingListModel::get(int index, function callback)
+ Calls the callback with object at the specified \a index in the model. The result.object property
+ contains the object in its raw form as returned by the query, the rolenames
+ are not applied. The object.partition is the partition for the returned.
+ If the index is out of range it returns an object with empty partition & object properties.
-void JsonDbCachingListModelPrivate::prefetchNearbyPages(int index)
-{
- int pos = objectCache.findPrefetchIndex(index, lowWaterMark);
- if (pos != -1 && index <= objectUuids.count()) {
- createObjectRequests(pos, objectCache.findChunkSize(pos));
- }
-}
-void JsonDbCachingListModelPrivate::addIndexToQueue(int index)
-{
- int maxItems = 0;
- int start = objectCache.findIndexNSize(index, maxItems);
- QPair<int, int> req;
- foreach (req, requestQueue) {
- if (start == req.first && maxItems == req.second) {
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<"Allready in Queue "<<start<<maxItems;
-#endif
- return;
+ \code
+ function updateCallback(error, response) {
+ if (error) {
+ console.log("Update Error :"+JSON.stringify(error));
+ return,
}
- }
- requestQueue.append(QPair<int, int>(start,maxItems));
-}
-
-void JsonDbCachingListModelPrivate::requestPageContaining(int index)
-{
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<Q_FUNC_INFO<<index;
-#endif
- if (state == JsonDbCachingListModel::Querying) {
- if (index >= currentCacheRequest.index &&
- index < currentCacheRequest.index+currentCacheRequest.count) {
- // Check if we are querying for this range already
- if (!cacheMiss.contains(index))
- cacheMiss.append(index);
- return;
- } else {
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug()<<"Add new Request to Queue" << index <<" currentCacheRequest = " <<
- currentCacheRequest.index << ", " << currentCacheRequest.count;
-#endif
- addIndexToQueue(index);
- return;
+ console.log("Response from Update");
+ console.log("response.id = "+response.id +" count = "+response.items.length);
+ for (var i = 0; i < response.items.length; i++) {
+ console.log("_uuid = "+response.items[i]._uuid +" ._version = "+response.items[i]._version);
}
}
- int maxItems = 0;
- int start = objectCache.findIndexNSize(index, maxItems);
- createObjectRequests(start, maxItems);
-
-}
-
-void JsonDbCachingListModelPrivate::clearNotification(int index)
-{
- if (index >= partitionObjects.count())
- return;
-
- RequestInfo &r = partitionObjectDetails[index];
- if (r.watcher) {
- JsonDatabase::sharedConnection().removeWatcher(r.watcher);
- }
- r.clear();
-}
-
-void JsonDbCachingListModelPrivate::clearNotifications()
-{
- for (int i = 0; i < partitionObjects.count(); i++)
- clearNotification(i);
-}
-
-void JsonDbCachingListModelPrivate::createOrUpdateNotification(int index)
-{
- Q_Q(JsonDbCachingListModel);
- if (index >= partitionObjects.count())
- return;
- clearNotification(index);
- if (partitionObjects[index]->state() != JsonDbPartition::Online)
- return;
- QJsonDbWatcher *watcher = new QJsonDbWatcher();
- watcher->setQuery(query+sortOrder);
- watcher->setWatchedActions(QJsonDbWatcher::Created | QJsonDbWatcher::Updated |QJsonDbWatcher::Removed);
- watcher->setPartition(partitionObjects[index]->name());
- QVariantMap::ConstIterator i = queryBindings.constBegin();
- while (i != queryBindings.constEnd()) {
- watcher->bindValue(i.key(), QJsonValue::fromVariant(i.value()));
- ++i;
- }
- QObject::connect(watcher, SIGNAL(notificationsAvailable(int)),
- q, SLOT(_q_notificationsAvailable()));
- QObject::connect(watcher, SIGNAL(error(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)),
- q, SLOT(_q_notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)));
- JsonDatabase::sharedConnection().addWatcher(watcher);
- partitionObjectDetails[index].watcher = watcher;
-}
-
-void JsonDbCachingListModelPrivate::createOrUpdateNotifications()
-{
- for (int i = 0; i < partitionObjects.count(); i++) {
- createOrUpdateNotification(i);
- }
-}
-
-void JsonDbCachingListModelPrivate::parseSortOrder()
-{
- Q_Q(JsonDbCachingListModel);
- QRegExp orderMatch("\\[([/\\\\[\\]])[ ]*([^\\[\\]]+)[ ]*\\]");
- if (orderMatch.indexIn(sortOrder, 0) >= 0) {
- ascendingOrder = false;
- if (!orderMatch.cap(1).compare(QLatin1String("/")))
- ascendingOrder = true;
- indexName = orderMatch.cap(2);
- }
- if (!indexName.isEmpty()) {
- queryForIndexSpec = QString(QLatin1String("[?_type=\"Index\"][?name=\"%1\" | propertyName=\"%1\"]")).arg(indexName);
- } else {
- // Set default sort order (by _uuid)
- q->setSortOrder(QLatin1String("[/_uuid]"));
- }
-}
-
-void JsonDbCachingListModelPrivate::setQueryForSortKeys()
-{
- // Query to retrieve the sortKeys
- // TODO remove the "[= {}]" from query
- queryForSortKeys = query + QLatin1String("[= { _uuid: _uuid, _indexValue: _indexValue }]");
- queryForSortKeys += sortOrder;
-}
-
-int JsonDbCachingListModelPrivate::indexOfWatcher(QJsonDbWatcher *watcher)
-{
- for (int i = 0; i < partitionObjectDetails.count(); i++) {
- if (watcher == partitionObjectDetails[i].watcher)
- return i;
- }
- return -1;
-}
-
-QJsonObject JsonDbCachingListModelPrivate::getJsonObject(int index)
-{
- if (index == lastQueriedIndex)
- return lastQueriedObject;
- if (index < 0 || index >= objectUuids.size()) {
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug() << "getItem" << index << "size " << objectUuids.size();
-#endif
- return QJsonObject();
- }
- int page = objectCache.findPage(index);
- if (page == -1) {
- if (!cacheMiss.contains(index))
- cacheMiss.append(index);
- requestPageContaining(index);
- return QJsonObject();
- }
-
- QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
- const QString &uuid = (begin+index).value();
- if (!objectCache.hasValueAtPage(page, uuid)) {
- // The value is missing, refresh page
- int startIndex = 0; int count = 0;
- objectCache.dropPage(page, startIndex, count);
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug() << "getItem Refresh Page: "<<page<<"Start: "<<startIndex<< "Count:"<<count<< "State :"<<state;
-#endif
- if (state == JsonDbCachingListModel::Ready) {
- // Start the request
- createObjectRequests(startIndex, count);
- } else {
- requestQueue.append(QPair<int, int>(startIndex, count));
+ function updateItemCallback(index, response) {
+ if (response) {
+ response.object.firstName = response.object.firstName+ "*";
+ response.partition.update(response.object, updateCallback);
}
- if (!cacheMiss.contains(index))
- cacheMiss.append(index);
- return QJsonObject();
- }
- if (state == JsonDbCachingListModel::Ready) // Pre-fetch only, if in Ready state
- prefetchNearbyPages(index);
- QJsonObject ret = objectCache.valueAtPage(page, uuid);
- lastQueriedIndex = index;
- lastQueriedObject = ret;
- return ret;
-}
-
-QVariant JsonDbCachingListModelPrivate::getItem(int index)
-{
- return QVariant(getJsonObject(index).toVariantMap());
-}
-
-QVariant JsonDbCachingListModelPrivate::getItem(int index, int role)
-{
- QJsonObject obj = getJsonObject(index);
- return lookupJsonProperty(obj, properties[role]).toVariant();
-}
-
-void JsonDbCachingListModelPrivate::queueGetCallback(int index, const QJSValue &callback)
-{
- if (index < 0 || index >= objectUuids.size())
- return;
- int page = objectCache.findPage(index);
- if (page == -1) {
- requestPageContaining(index);
- getCallbacks.insert(index, callback);
- return;
}
- callGetCallback(index, callback);
-}
-
-void JsonDbCachingListModelPrivate::callGetCallback(int index, QJSValue callback)
-{
- if (index < 0 || index >= objectUuids.size())
- return;
- int page = objectCache.findPage(index);
- if (page == -1) {
- return;
- }
- QVariant object = getItem(index);
- JsonDbPartition *partition = getItemPartition(index);
- QJSValue result = g_declEngine->newObject();
- result.setProperty(QLatin1String("object"), g_declEngine->toScriptValue(object));
- result.setProperty(QLatin1String("partition"), g_declEngine->newQObject(partition));
- if (callback.isCallable()) {
- QJSValueList args;
- args << QJSValue(index) << result;
- callback.call(args);
- getCallbacks.remove(index);
- }
-}
-
-JsonDbPartition* JsonDbCachingListModelPrivate::getItemPartition(int index)
-{
- if (index < 0 || index >= objectUuids.size())
- return 0;
- QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
- int partitionIndex = (begin+index).key().partitionIndex();
- if (partitionIndex <= partitionObjects.count())
- return partitionObjects[partitionIndex];
- return 0;
-}
-
-int JsonDbCachingListModelPrivate::indexOf(const QString &uuid) const
-{
- if (!objectSortValues.contains(uuid))
- return -1;
- const SortingKey &key = objectSortValues.value(uuid);
- QMap<SortingKey, QString>::const_iterator begin = objectUuids.constBegin();
- QMap<SortingKey, QString>::const_iterator end = objectUuids.constEnd();
- QMap<SortingKey, QString>::const_iterator i = objectUuids.find(key);
- return iterator_position(begin, end, i);
-}
-
-void JsonDbCachingListModelPrivate::sendNotification(int partitionIndex, const QJsonObject &object, QJsonDbWatcher::Action action)
-{
- if (action == QJsonDbWatcher::Created) {
- addItem(object, partitionIndex);
- } else if (action == QJsonDbWatcher::Removed)
- deleteItem(object, partitionIndex);
- else if (action == QJsonDbWatcher::Updated) {
- updateItem(object, partitionIndex);
+ onClicked: {
+ contacts.get(listView.currentIndex, updateItemCallback);
}
-}
-
-void JsonDbCachingListModelPrivate::_q_keyResponse(int index, const QList<QJsonObject> &v, const QString &sortKey)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_UNUSED(sortKey)
- fillKeys(v, index);
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
-
-void JsonDbCachingListModelPrivate::_q_valueResponse(int index, const QList<QJsonObject> &v)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- fillData(v, index);
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
-
-void JsonDbCachingListModelPrivate::_q_indexResponse(int index, const QList<QJsonObject> &v)
+ \endcode
+*/
+void JsonDbCachingListModel::get(int index, const QJSValue &callback)
{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- verifyIndexSpec(v, index);
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
+ getCallbacks.insertMulti(index, callback);
+ fetchObject(index);
}
-void JsonDbCachingListModelPrivate::_q_partitionStateChanged(JsonDbPartition::State state)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_UNUSED(state)
-#ifdef JSONDB_LISTMODEL_DEBUG
- qDebug() << Q_FUNC_INFO;
-#endif
- if (componentComplete && !query.isEmpty() && partitionsReady()) {
- createOrUpdateNotifications();
- fetchModel();
- }
-
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
+/*!
+ \qmlmethod int QtJsonDb::JsonDbCachingListModel::indexOf(string uuid)
-void JsonDbCachingListModelPrivate::_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode code, const QString & message)
+ Returns the index of the object with the \a uuid in the model. If the object is
+ not found it returns -1
+*/
+int JsonDbCachingListModel::indexOf(const QString &uuid) const
{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_Q(JsonDbCachingListModel);
- qWarning() << QString("JsonDb error: %1 %2").arg(code).arg(message);
- if (code != QtJsonDb::QJsonDbRequest::PartitionUnavailable) {
- int oldErrorCode = errorCode;
- errorCode = code;
- errorString = message;
- if (oldErrorCode != errorCode)
- emit q->errorChanged(q->error());
- }
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
+ return QJsonDbQueryModel::indexOf(uuid);
}
-void JsonDbCachingListModelPrivate::_q_notificationsAvailable()
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_Q(JsonDbCachingListModel);
- QJsonDbWatcher *watcher = qobject_cast<QJsonDbWatcher *>(q->sender());
- int partitionIndex = indexOfWatcher(watcher);
- if (!watcher || partitionIndex == -1)
- return;
- QList<QJsonDbNotification> list = watcher->takeNotifications();
- for (int i = 0; i < list.count(); i++) {
- const QJsonDbNotification & notification = list[i];
- QJsonObject object = notification.object();
- QJsonDbWatcher::Action action = notification.action();
- if (state == JsonDbCachingListModel::Querying) {
- NotificationItem pending;
- pending.partitionIndex = partitionIndex;
- pending.item = object;
- pending.action = action;
- pendingNotifications.append(pending);
- } else {
- foreach (NotificationItem pending, pendingNotifications)
- sendNotification(pending.partitionIndex, pending.item, pending.action);
- pendingNotifications.clear();
- sendNotification(partitionIndex, object, action);
- }
- }
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
+/*!
+ \qmlmethod object QtJsonDb::JsonDbCachingListModel::getPartition(int index)
-void JsonDbCachingListModelPrivate::_q_notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode code, const QString &message)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_Q(JsonDbCachingListModel);
- if (code != static_cast<QtJsonDb::QJsonDbWatcher::ErrorCode>(QtJsonDb::QJsonDbRequest::PartitionUnavailable)) {
- int oldErrorCode = errorCode;
- errorCode = code;
- errorString = message;
- if (oldErrorCode != errorCode)
- emit q->errorChanged(q->error());
- }
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
+ Returns the partition object at the specified \a index in the model. If
+ the index is out of range it returns an empty object.
+*/
-void JsonDbCachingListModelPrivate::_q_verifyDefaultIndexType(int index)
+JsonDbPartition* JsonDbCachingListModel::getPartition(int index)
{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- SortIndexSpec &indexSpec = partitionIndexDetails[index].spec;
- partitionIndexDetails[index].valid = true;
- if (!indexName.compare(QLatin1String("_uuid"))) {
- indexSpec.name = QLatin1String("_uuid");
- indexSpec.type = SortIndexSpec::UUID;
- indexSpec.caseSensitive = false;
- } else if (!indexName.compare(QLatin1String("_type"))) {
- indexSpec.name = QLatin1String("_type");
- indexSpec.type = SortIndexSpec::String;
- indexSpec.caseSensitive = true;
+ const QString partitionName = this->partitionName(index);
+ if (!this->nameToJsonDbPartitionMap.contains(partitionName)) {
+ this->nameToJsonDbPartitionMap.insert(partitionName,
+ new JsonDbPartition(partitionName));
}
- //Check if all index specs are supported.
- bool checkedAll = true;
- for (int i = 0; i < partitionIndexDetails.count(); i++) {
- if (!partitionIndexDetails[i].valid) {
- checkedAll = false;
- break;
- }
- }
- if (checkedAll) {
- //Start fetching the keys.
- setQueryForSortKeys();
- for (int i = 0; i < partitionKeyRequestDetails.count(); i++) {
- fetchPartitionKeys(i);
- }
- }
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
+ return this->nameToJsonDbPartitionMap.value(partitionName);
}
-void JsonDbCachingListModelPrivate::initPartition(JsonDbPartition *v)
-{
- Q_Q(JsonDbCachingListModel);
- partitionObjects.append(QPointer<JsonDbPartition>(v));
- partitionObjectDetails.append(RequestInfo());
- ModelRequest *valueRequest = new ModelRequest();
- QObject::connect(valueRequest, SIGNAL(finished(int,QList<QJsonObject>,QString)),
- q, SLOT(_q_valueResponse(int,QList<QJsonObject>)));
- QObject::connect(valueRequest, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
- q, SLOT(_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode,QString)));
- valueRequests.append(valueRequest);
-
- partitionKeyRequestDetails.append(RequestInfo());
- ModelRequest *keyRequest = new ModelRequest();
- QObject::connect(keyRequest, SIGNAL(finished(int,QList<QJsonObject>,QString)),
- q, SLOT(_q_keyResponse(int,QList<QJsonObject>,QString)));
- QObject::connect(keyRequest, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
- q, SLOT(_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode,QString)));
- keyRequests.append(keyRequest);
-
- partitionObjectUuids.append(JsonDbModelIndexType());
-
- partitionIndexDetails.append(IndexInfo());
- ModelRequest *indexRequest = new ModelRequest();
- QObject::connect(indexRequest, SIGNAL(finished(int,QList<QJsonObject>,QString)),
- q, SLOT(_q_indexResponse(int,QList<QJsonObject>)));
- QObject::connect(indexRequest, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)),
- q, SLOT(_q_readError(QtJsonDb::QJsonDbRequest::ErrorCode,QString)));
- indexRequests.append(indexRequest);
-
- if (componentComplete && !query.isEmpty()) {
- parseSortOrder();
- createOrUpdateNotification(partitionObjects.count()-1);
- if (state == JsonDbCachingListModel::None)
- resetModel = true;
- fetchIndexSpec(partitionObjects.count()-1);
- }
-}
+//---------------- PROPERTIES------------------------------
-void JsonDbCachingListModelPrivate::appendPartition(JsonDbPartition *v)
-{
- Q_Q(JsonDbCachingListModel);
- QObject::connect (v, SIGNAL(stateChanged(JsonDbPartition::State)), q, SLOT(_q_partitionStateChanged(JsonDbPartition::State)));
- initPartition (v);
-}
-
-void JsonDbCachingListModelPrivate::partitions_append(QQmlListProperty<JsonDbPartition> *p, JsonDbPartition *v)
-{
- JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
- JsonDbCachingListModelPrivate *pThis = (q) ? q->d_func() : 0;
- if (!v) {
- qWarning("Invalid partition object");
- return;
- }
-
- if (pThis) {
- pThis->appendPartition(v);
- }
-}
-
-int JsonDbCachingListModelPrivate::partitions_count(QQmlListProperty<JsonDbPartition> *p)
-{
- JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
- JsonDbCachingListModelPrivate *pThis = (q) ? q->d_func() : 0;
- if (pThis) {
- return pThis->partitionObjects.count();
- }
- return 0;
-}
+/*!
+ \qmlproperty object QtJsonDb::JsonDbCachingListModel::bindings
+ Holds the bindings for the placeholders used in the query string. Note that
+ the placeholder marker '%' should not be included as part of the keys.
-JsonDbPartition* JsonDbCachingListModelPrivate::partitions_at(QQmlListProperty<JsonDbPartition> *p, int idx)
-{
- JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
- JsonDbCachingListModelPrivate *pThis = (q) ? q->d_func() : 0;
- if (pThis && idx < pThis->partitionObjects.count()) {
- return pThis->partitionObjects.at(idx);
+ \qml
+ JsonDb.JsonDbCachingListModel {
+ id: listModel
+ query: '[?_type="Contact"][?name=%firstName]'
+ bindings :{'firstName':'Book'}
+ partitions:[ JsonDb.Partition {
+ name:"com.nokia.shared"
+ }]
}
- return 0;
-}
+ \endqml
-void JsonDbCachingListModelPrivate::clearPartitions()
-{
- partitionObjects.clear();
- partitionObjectDetails.clear();
- partitionKeyRequestDetails.clear();
- partitionObjectUuids.clear();
- partitionIndexDetails.clear();
- while (!keyRequests.isEmpty()) {
- delete keyRequests[0];
- keyRequests.removeFirst();
- }
- while (!indexRequests.isEmpty()) {
- delete indexRequests[0];
- indexRequests.removeFirst();
- }
- while (!valueRequests.isEmpty()) {
- delete valueRequests[0];
- valueRequests.removeFirst();
- }
- reset();
-}
+ \sa QtJsonDb::JsonDbCachingListModel::query
-void JsonDbCachingListModelPrivate::partitions_clear(QQmlListProperty<JsonDbPartition> *p)
-{
- JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
- JsonDbCachingListModelPrivate *pThis = (q) ? q->d_func() : 0;
- if (pThis) {
- pThis->clearPartitions();
- }
-}
+*/
/*!
- \qmlclass JsonDbCachingListModel JsonDbCachingListModel
- \inqmlmodule QtJsonDb 1.0
- \since 1.0
+ \qmlproperty int QtJsonDb::JsonDbCachingListModel::cacheSize
+ Holds the maximum number of items cached by the model.
+*/
- The JsonDbCachingListModel provides a read-only ListModel usable with views such as
- ListView or GridView displaying data items matching a query. The sorting is done using
- an index set on the JsonDb server. If it doesn't find a matching index for the sortkey,
- the model goes into Error state. Maximum number of items in the model cache can be set
- by cacheSize property.
+/*!
+ \qmlproperty object QtJsonDb::JsonDbCachingListModel::error
+ \readonly
- When an item is not present in the internal cache, the model can return an 'undefined'
- object from data() method. It will be queued for retrieval and the model will notify its
- presence using the dataChanged() signal.
+ This property holds the current error information for the object. It contains:
+ \list
+ \li error.code - code for the current error.
+ \li error.message - detailed explanation of the error
+ \endlist
+*/
- The model is initialized by retrieving the result in chunks. After receiving the first
- chunk, the model is reset with items from it. The state will be "Querying" during
- fetching data and will be changed to "Ready".
+/*!
+ \qmlproperty string QtJsonDb::JsonDbCachingListModel::query
- \note This is still a work in progress, so expect minor changes.
+ The query string in JsonQuery format used by the model to fetch
+ items from the database. Setting an empty query clears all the elements
- \code
- import QtJsonDb 1.0 as JsonDb
+ In the following example, the JsonDbCachingListModel would contain all
+ the objects with \a _type "CONTACT" from partition called "com.nokia.shared"
+ \qml
JsonDb.JsonDbCachingListModel {
- id: contactsModel
- query: '[?_type="Contact"]'
- cacheSize: 100
- partitions: [JsonDb.Partition {
+ id: listModel
+ query: "[?_type=\"CONTACT\"]"
+ partitions:[ JsonDb.Partition {
name:"com.nokia.shared"
}]
- sortOrder: "[/firstName]"
- roleNames: ["firstName", "lastName", "phoneNumber"]
- }
- ListView {
- model: contactsModel
- Row {
- spacing: 10
- Text {
- text: firstName + " " + lastName
- }
- Text {
- text: phoneNumber
- }
- }
}
- \endcode
-*/
-
-JsonDbCachingListModel::JsonDbCachingListModel(QObject *parent)
- : QAbstractListModel(parent)
- , d_ptr(new JsonDbCachingListModelPrivate(this))
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
- d->init();
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
-
-JsonDbCachingListModel::~JsonDbCachingListModel()
-{
-}
-
-void JsonDbCachingListModel::classBegin()
-{
-}
+ \endqml
-void JsonDbCachingListModel::componentComplete()
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
- d->componentComplete = true;
- if (!d->query.isEmpty() && d->partitionObjects.count() && d->partitionsReady()) {
- d->createOrUpdateNotifications();
- d->fetchModel();
- }
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
+*/
/*!
\qmlproperty int QtJsonDb1::JsonDbCachingListModel::rowCount
The number of items in the model.
*/
-int JsonDbCachingListModel::rowCount(const QModelIndex &parent) const
-{
- Q_UNUSED(parent);
- Q_D(const JsonDbCachingListModel);
- return d->objectUuids.count();
-}
-
-QVariant JsonDbCachingListModel::data(const QModelIndex &modelIndex, int role) const
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- JsonDbCachingListModel *pThis = const_cast<JsonDbCachingListModel *>(this);
- QVariant ret = pThis->d_func()->getItem(modelIndex.row(), role);
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
- return ret;
-}
-
-QHash<int, QByteArray> JsonDbCachingListModel::roleNames() const
-{
- Q_D(const JsonDbCachingListModel);
- return d->roleNames;
-}
/*!
\qmlproperty ListOrObject QtJsonDb1::JsonDbCachingListModel::roleNames
@@ -1418,55 +296,15 @@ QHash<int, QByteArray> JsonDbCachingListModel::roleNames() const
\endcode
*/
-QVariant JsonDbCachingListModel::scriptableRoleNames() const
-{
- Q_D(const JsonDbCachingListModel);
- return d->roleMap;
-}
-
-void JsonDbCachingListModel::setScriptableRoleNames(const QVariant &vroles)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
- d->properties.clear();
- d->roleNames.clear();
- if (vroles.type() == QVariant::Map) {
- QVariantMap roles = vroles.toMap();
- d->roleMap = roles;
- int i = 0;
- for (QVariantMap::const_iterator it = roles.begin(); it != roles.end(); ++it) {
- d->roleNames.insert(i, it.key().toLatin1());
- d->properties.insert(i, removeArrayOperator(it.value().toString()).split('.'));
- i++;
- }
- } else {
- QVariantList roleList = vroles.toList();
- d->roleMap.clear();
- for (int i = 0; i < roleList.size(); i++) {
- QString role = roleList[i].toString();
- d->roleMap[role] = role;
- d->roleNames.insert(i, role.toLatin1());
- d->properties.insert(i, removeArrayOperator(role).split('.'));
- }
- }
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
-
/*!
- \qmlproperty string QtJsonDb1::JsonDbCachingListModel::query
+ \qmlproperty string QtJsonDb::JsonDbCachingListModel::sortOrder
- The query string in JsonQuery format used by the model to fetch
- items from the database. Setting an empty query clears all the elements
+ The order used by the model to sort the items. Make sure that there
+ is a matching Index in the database for this sortOrder. This has to be
+ specified in the JsonQuery format.
In the following example, the JsonDbCachingListModel would contain all
- the objects with \a _type "CONTACT" from partition called "com.nokia.shared"
+ the objects of type \a "CONTACT" sorted by their \a firstName field
\qml
JsonDb.JsonDbCachingListModel {
@@ -1475,162 +313,25 @@ void JsonDbCachingListModel::setScriptableRoleNames(const QVariant &vroles)
partitions:[ JsonDb.Partition {
name:"com.nokia.shared"
}]
+ sortOrder: "[/firstName]"
}
\endqml
\sa QtJsonDb1::JsonDbCachingListModel::bindings
*/
-QString JsonDbCachingListModel::query() const
-{
- Q_D(const JsonDbCachingListModel);
- return d->query;
-}
-
-void JsonDbCachingListModel::setQuery(const QString &newQuery)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
-
- const QString oldQuery = d->query;
- if (oldQuery == newQuery)
- return;
-
- d->query = newQuery;
- if (rowCount() && d->query.isEmpty()) {
- d->reset();
- }
-
- if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
- return;
- d->createOrUpdateNotifications();
- d->fetchModel();
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
-
-/*!
- \qmlproperty object QtJsonDb1::JsonDbCachingListModel::bindings
- Holds the bindings for the placeholders used in the query string. Note that
- the placeholder marker '%' should not be included as part of the keys.
-
- \qml
- JsonDb.JsonDbCachingListModel {
- id: listModel
- query: '[?_type="Contact"][?name=%firstName]'
- bindings :{'firstName':'Book'}
- partitions:[ JsonDb.Partition {
- name:"com.nokia.shared"
- }]
- }
- \endqml
-
- \sa QtJsonDb1::JsonDbCachingListModel::query
-
-*/
-
-QVariantMap JsonDbCachingListModel::bindings() const
-{
- Q_D(const JsonDbCachingListModel);
- return d->queryBindings;
-}
-
-void JsonDbCachingListModel::setBindings(const QVariantMap &newBindings)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
- d->queryBindings = newBindings;
-
- if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
- return;
- d->createOrUpdateNotifications();
- d->fetchModel();
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
/*!
- \qmlproperty int QtJsonDb1::JsonDbCachingListModel::cacheSize
- Holds the maximum number of items cached by the model.
-
- \code
- JsonDb.JsonDbCachingListModel {
- id: listModel
- query: "[?_type=\"CONTACT\"]"
- cacheSize: 100
- partitions: [JsonDb.Partition {
- name:"com.nokia.shared"
- }]
- }
- \endcode
-
+ \qmlproperty Status QtJsonDb::JsonDbCachingListModel::status
+ The current status of the model.
+ \list
+ \li Status.None - The model is not initialized
+ \li Status.Querying - It is querying the results from server
+ \li Status.Ready - Results are ready
+ \li Status.Error - Cannot find a matching index on the server
+ \endlist
*/
-int JsonDbCachingListModel::cacheSize() const
-{
- Q_D(const JsonDbCachingListModel);
- return d->cacheSize;
-}
-
-void JsonDbCachingListModel::setCacheSize(int newCacheSize)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
- if (newCacheSize == d->cacheSize)
- return;
-
- d->cacheSize = newCacheSize;
- d->setCacheParams(d->cacheSize);
- if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
- return;
-
- d->fetchModel();
-#ifdef JSONDB_LISTMODEL_DEBUG
- d->objectCache.dumpCacheDetails();
-#endif
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
-
-void JsonDbCachingListModel::partitionNameChanged(const QString &partitionName)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_UNUSED(partitionName);
- Q_D(JsonDbCachingListModel);
-
- if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
- return;
-
- d->createOrUpdateNotifications();
- d->fetchModel();
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
-}
-
/*!
\qmlproperty list QtJsonDb1::JsonDbCachingListModel::partitions
Holds the list of partition objects for the model.
@@ -1646,175 +347,84 @@ void JsonDbCachingListModel::partitionNameChanged(const QString &partitionName)
\endcode
*/
-
QQmlListProperty<JsonDbPartition> JsonDbCachingListModel::partitions()
{
return QQmlListProperty<JsonDbPartition>(this, 0
- , &JsonDbCachingListModelPrivate::partitions_append
- , &JsonDbCachingListModelPrivate::partitions_count
- , &JsonDbCachingListModelPrivate::partitions_at
- , &JsonDbCachingListModelPrivate::partitions_clear);
+ , &partitions_append
+ , &partitions_count
+ , &partitions_at
+ , &partitions_clear);
}
-/*!
- \qmlproperty string QtJsonDb1::JsonDbCachingListModel::sortOrder
-
- The order used by the model to sort the items. Make sure that there
- is a matching Index in the database for this sortOrder. This has to be
- specified in the JsonQuery format.
-
- In the following example, the JsonDbCachingListModel would contain all
- the objects of type \a "CONTACT" sorted by their \a firstName field
-
- \qml
- JsonDb.JsonDbCachingListModel {
- id: listModel
- query: "[?_type=\"CONTACT\"]"
- partitions: [ JsonDb.Partition {
- name:"com.nokia.shared"
- }]
- sortOrder: "[/firstName]"
- }
- \endqml
-
-*/
-
-QString JsonDbCachingListModel::sortOrder() const
+void JsonDbCachingListModel::partitions_append(QQmlListProperty<JsonDbPartition> *p, JsonDbPartition *v)
{
- Q_D(const JsonDbCachingListModel);
- return d->sortOrder;
+ if (!v) {
+ qWarning("Invalid partition object");
+ return;
+ }
+ JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
+ if (q) {
+ const QString partitionName = v->name();
+ q->appendPartitionName(partitionName);
+ if (!q->nameToJsonDbPartitionMap.contains(partitionName)) {
+ q->nameToJsonDbPartitionMap.insert(partitionName,
+ new JsonDbPartition(partitionName));
+ }
+ }
}
-void JsonDbCachingListModel::setSortOrder(const QString &newSortOrder)
+int JsonDbCachingListModel::partitions_count(QQmlListProperty<JsonDbPartition> *p)
{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
-
- const QString oldSortOrder = d->sortOrder;
- d->sortOrder = newSortOrder;
- if (oldSortOrder != newSortOrder) {
- d->parseSortOrder();
- if (!d->componentComplete || d->query.isEmpty() || !d->partitionObjects.count() || !d->partitionsReady())
- return;
- d->createOrUpdateNotifications();
- d->fetchModel();
+ JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
+ if (q) {
+ return q->partitionNames().size();
}
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
+ return 0;
}
-/*!
- \qmlproperty State QtJsonDb1::JsonDbCachingListModel::state
- The current state of the model.
- \list
- \li State.None - The model is not initialized
- \li State.Querying - It is querying the results from server
- \li State.Ready - Results are ready
- \li State.Error - Cannot find a matching index on the server
- \endlist
-*/
-
-JsonDbCachingListModel::State JsonDbCachingListModel::state() const
+JsonDbPartition* JsonDbCachingListModel::partitions_at(QQmlListProperty<JsonDbPartition> *p, int idx)
{
- Q_D(const JsonDbCachingListModel);
- return d->state;
+ JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
+ if (q && idx < q->partitionNames().size()) {
+ QString partitionName = q->partitionName(idx);
+ if (q->nameToJsonDbPartitionMap.contains(partitionName))
+ return q->nameToJsonDbPartitionMap.value(partitionName);
+ }
+ return 0;
}
-/*!
- \qmlmethod int QtJsonDb1::JsonDbCachingListModel::indexOf(string uuid)
-
- Returns the index of the object with the \a uuid in the model. If the object is
- not found it returns -1
-*/
-int JsonDbCachingListModel::indexOf(const QString &uuid) const
+void JsonDbCachingListModel::partitions_clear(QQmlListProperty<JsonDbPartition> *p)
{
- Q_D(const JsonDbCachingListModel);
- return d->indexOf(uuid);
-}
-
-/*!
- \qmlmethod QtJsonDb1::JsonDbCachingListModel::get(int index, function callback)
-
- Calls the callback with object at the specified \a index in the model. The result.object property
- contains the object in its raw form as returned by the query, the rolenames
- are not applied. The object.partition is the partition for the returned.
- If the index is out of range it returns an object with empty partition & object properties.
-
- \code
- function updateCallback(error, response) {
- if (error) {
- console.log("Update Error :"+JSON.stringify(error));
- return,
- }
- console.log("Response from Update");
- console.log("response.id = "+response.id +" count = "+response.items.length);
- for (var i = 0; i < response.items.length; i++) {
- console.log("_uuid = "+response.items[i]._uuid +" ._version = "+response.items[i]._version);
- }
- }
- function updateItemCallback(index, response) {
- if (response) {
- response.object.firstName = response.object.firstName+ "*";
- response.partition.update(response.object, updateCallback);
+ JsonDbCachingListModel *q = qobject_cast<JsonDbCachingListModel *>(p->object);
+ if (q) {
+ q->setPartitionNames(QStringList());
+ foreach (const QString &partitionName, q->nameToJsonDbPartitionMap.keys()) {
+ q->nameToJsonDbPartitionMap.take(partitionName)->deleteLater();
}
}
- onClicked: {
- contacts.get(listView.currentIndex, updateItemCallback);
- }
- \endcode
-*/
-void JsonDbCachingListModel::get(int index, const QJSValue &callback)
-{
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- QElapsedTimer elt;
- elt.start();
-#endif
- Q_D(JsonDbCachingListModel);
- d->queueGetCallback(index, callback);
-#ifdef JSONDB_LISTMODEL_BENCHMARK
- qint64 elap = elt.elapsed();
- if (elap > 3)
- qDebug() << Q_FUNC_INFO << "took more than 3 ms (" << elap << "ms )";
-#endif
}
-/*!
- \qmlmethod object QtJsonDb1::JsonDbCachingListModel::getPartition(int index)
-
- Returns the partition object at the specified \a index in the model. If
- the index is out of range it returns an empty object.
-*/
-
-JsonDbPartition* JsonDbCachingListModel::getPartition(int index) const
+void JsonDbCachingListModel::onObjectAvailable(int index, QJsonObject availableObject, QString partitionName)
{
- JsonDbCachingListModel *pThis = const_cast<JsonDbCachingListModel *>(this);
- return pThis->d_func()->getItemPartition(index);
-}
+ if (getCallbacks.contains(index)) {
+ QVariant object = QVariant (availableObject.toVariantMap());
+ JsonDbPartition *partition = new JsonDbPartition(partitionName);
+ QJSValue result = g_declEngine->newObject();
+ result.setProperty(QLatin1String("object"), g_declEngine->toScriptValue(object));
+ result.setProperty(QLatin1String("partition"), g_declEngine->newQObject(partition));
-/*!
- \qmlproperty object QtJsonDb1::JsonDbCachingListModel::error
- \readonly
+ QMap<int, QJSValue>::iterator callbacksIter = getCallbacks.find(index);
- This property holds the current error information for the object. It contains:
- \list
- \li error.code - code for the current error.
- \li error.message - detailed explanation of the error
- \endlist
-*/
-
-QVariantMap JsonDbCachingListModel::error() const
-{
- Q_D(const JsonDbCachingListModel);
- QVariantMap errorMap;
- errorMap.insert(QLatin1String("code"), d->errorCode);
- errorMap.insert(QLatin1String("message"), d->errorString);
- return errorMap;
+ while ((callbacksIter != getCallbacks.end()) && (callbacksIter.key() == index)) {
+ if (callbacksIter.value().isCallable()) {
+ QJSValueList args;
+ args << QJSValue(index) << result;
+ callbacksIter.value().call(args);
+ }
+ callbacksIter ++;
+ }
+ getCallbacks.remove(index);
+ }
}
#include "moc_jsondbcachinglistmodel.cpp"
diff --git a/src/imports/jsondb/jsondbcachinglistmodel.h b/src/imports/jsondb/jsondbcachinglistmodel.h
index 68546c8..4173f17 100644
--- a/src/imports/jsondb/jsondbcachinglistmodel.h
+++ b/src/imports/jsondb/jsondbcachinglistmodel.h
@@ -39,103 +39,53 @@
**
****************************************************************************/
-#ifndef JSONDBCACHINGLISTMODEL_H
-#define JSONDBCACHINGLISTMODEL_H
-
-#include <QAbstractListModel>
-#include <QHash>
-#include <QMultiMap>
-#include <QSet>
-#include <QSharedDataPointer>
-#include <QStringList>
+#ifndef JSONDBCACHINGLISTMODEL_QML_H
+#define JSONDBCACHINGLISTMODEL_QML_H
+
#include <QQmlParserStatus>
#include <QQmlListProperty>
#include <QJSValue>
-#include <QScopedPointer>
#include "jsondbpartition.h"
+#include <private/qjsondbquerymodel_p.h>
QT_BEGIN_NAMESPACE_JSONDB
-class JsonDbCachingListModelPrivate;
-class JsonDbPartition;
-class JsonDbCachingListModel : public QAbstractListModel, public QQmlParserStatus
+class JsonDbCachingListModel : public QJsonDbQueryModel, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
- Q_ENUMS(State)
+
public:
- enum State { None, Querying, Ready, Error };
JsonDbCachingListModel(QObject *parent = 0);
virtual ~JsonDbCachingListModel();
- Q_PROPERTY(State state READ state NOTIFY stateChanged)
- Q_PROPERTY(QString query READ query WRITE setQuery)
- Q_PROPERTY(QVariantMap bindings READ bindings WRITE setBindings)
- Q_PROPERTY(QString sortOrder READ sortOrder WRITE setSortOrder)
- Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged)
- Q_PROPERTY(QVariant roleNames READ scriptableRoleNames WRITE setScriptableRoleNames)
- Q_PROPERTY(int cacheSize READ cacheSize WRITE setCacheSize)
Q_PROPERTY(QQmlListProperty<JsonDbPartition> partitions READ partitions)
- Q_PROPERTY(QVariantMap error READ error NOTIFY errorChanged)
virtual void classBegin();
virtual void componentComplete();
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- virtual QHash<int,QByteArray> roleNames() const;
-
- QVariant scriptableRoleNames() const;
- void setScriptableRoleNames(const QVariant &roles);
-
- QString query() const;
- void setQuery(const QString &newQuery);
-
- QVariantMap bindings() const;
- void setBindings(const QVariantMap &newBindings);
-
QQmlListProperty<JsonDbPartition> partitions();
- int cacheSize() const;
- void setCacheSize(int newCacheSize);
-
- QString sortOrder() const;
- void setSortOrder(const QString &newSortOrder);
-
-
- JsonDbCachingListModel::State state() const;
-
- Q_INVOKABLE int indexOf(const QString &uuid) const;
Q_INVOKABLE void get(int index, const QJSValue &callback);
- Q_INVOKABLE JsonDbPartition* getPartition(int index) const;
- QVariantMap error() const;
-
-signals:
- void stateChanged(State state) const;
- void rowCountChanged(int newCount) const;
- void errorChanged(QVariantMap newError);
-
-private Q_SLOTS:
- void partitionNameChanged(const QString &partitionName);
+ Q_INVOKABLE JsonDbPartition* getPartition(int index);
+ Q_INVOKABLE int indexOf(const QString &uuid) const;
+ static void partitions_append(QQmlListProperty<JsonDbPartition> *p, JsonDbPartition *v);
+ static int partitions_count(QQmlListProperty<JsonDbPartition> *p);
+ static JsonDbPartition* partitions_at(QQmlListProperty<JsonDbPartition> *p, int idx);
+ static void partitions_clear(QQmlListProperty<JsonDbPartition> *p);
private:
Q_DISABLE_COPY(JsonDbCachingListModel)
- Q_DECLARE_PRIVATE(JsonDbCachingListModel)
- QScopedPointer<JsonDbCachingListModelPrivate> d_ptr;
-
- Q_PRIVATE_SLOT(d_func(), void _q_verifyDefaultIndexType(int))
- Q_PRIVATE_SLOT(d_func(), void _q_notificationsAvailable())
- Q_PRIVATE_SLOT(d_func(), void _q_notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode, QString))
- Q_PRIVATE_SLOT(d_func(), void _q_keyResponse(int, QList<QJsonObject>, QString))
- Q_PRIVATE_SLOT(d_func(), void _q_valueResponse(int, QList<QJsonObject>))
- Q_PRIVATE_SLOT(d_func(), void _q_indexResponse(int, QList<QJsonObject>))
- Q_PRIVATE_SLOT(d_func(), void _q_readError(QtJsonDb::QJsonDbRequest::ErrorCode, QString))
- Q_PRIVATE_SLOT(d_func(), void _q_partitionStateChanged(JsonDbPartition::State))
+
+ QMap <int, QJSValue> getCallbacks;
+ QMap <QString, JsonDbPartition *> nameToJsonDbPartitionMap;
+private slots:
+ void onObjectAvailable(int index, QJsonObject availableObject, QString partitionName);
};
QT_END_NAMESPACE_JSONDB
-#endif // JSONDBCACHINGLISTMODEL_H
+#endif // JSONDBCACHINGLISTMODEL_QML_H
diff --git a/src/imports/jsondb/jsondblistmodel.cpp b/src/imports/jsondb/jsondblistmodel.cpp
index 9ed0ed4..1bdce01 100644
--- a/src/imports/jsondb/jsondblistmodel.cpp
+++ b/src/imports/jsondb/jsondblistmodel.cpp
@@ -49,7 +49,6 @@
#include <QString>
#include <qdebug.h>
-
#undef DEBUG_LIST_MODEL
#ifdef DEBUG_LIST_MODEL
diff --git a/src/imports/jsondb/jsondblistmodel_p.h b/src/imports/jsondb/jsondblistmodel_p.h
index 7852fa4..616d4b4 100644
--- a/src/imports/jsondb/jsondblistmodel_p.h
+++ b/src/imports/jsondb/jsondblistmodel_p.h
@@ -53,10 +53,16 @@
#include <QJsonDbWatcher>
#include <QJsonDbReadRequest>
#include <QJsonDbUpdateRequest>
-#include "jsondbmodelutils.h"
+#include <private/qjsondbmodelutils_p.h>
QT_BEGIN_NAMESPACE_JSONDB
+struct CallbackInfo {
+ int index;
+ QJSValue successCallback;
+ QJSValue errorCallback;
+};
+
class JsonDbListModelPrivate
{
Q_DECLARE_PUBLIC(JsonDbListModel)
diff --git a/src/imports/jsondb/jsondbpartition.cpp b/src/imports/jsondb/jsondbpartition.cpp
index 79b9c03..7ff1ad1 100644
--- a/src/imports/jsondb/jsondbpartition.cpp
+++ b/src/imports/jsondb/jsondbpartition.cpp
@@ -38,6 +38,9 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+#include <private/qjsondbmodelutils_p.h>
+#include <QJsonDbCreateRequest>
+
#include "jsondbpartition.h"
#include "jsondatabase.h"
#include "jsondbnotification.h"
@@ -49,7 +52,6 @@
#include <qdebug.h>
QT_BEGIN_NAMESPACE_JSONDB
-
/*!
\qmlclass Partition JsonDbPartition
\inqmlmodule QtJsonDb 1.0
@@ -164,26 +166,6 @@ static QVariant qjsvalue_to_qvariant(const QJSValue &value)
}
}
-QList<QJsonObject> qvariantlist_to_qjsonobject_list(const QVariantList &list)
-{
- QList<QJsonObject> objects;
- int count = list.count();
- for (int i = 0; i < count; i++) {
- objects.append(QJsonObject::fromVariantMap(list[i].toMap()));
- }
- return objects;
-}
-
-QVariantList qjsonobject_list_to_qvariantlist(const QList<QJsonObject> &list)
-{
- QVariantList objects;
- int count = list.count();
- for (int i = 0; i < count; i++) {
- objects.append(list[i].toVariantMap());
- }
- return objects;
-}
-
QJSValue qjsonobject_list_to_qjsvalue(const QList<QJsonObject> &list)
{
int count = list.count();
diff --git a/src/imports/jsondb/jsondbpartition.h b/src/imports/jsondb/jsondbpartition.h
index 8aeca06..e732fea 100644
--- a/src/imports/jsondb/jsondbpartition.h
+++ b/src/imports/jsondb/jsondbpartition.h
@@ -54,8 +54,6 @@
#include <QJsonDbWatcher>
QT_BEGIN_NAMESPACE_JSONDB
-
-class JsonDatabase;
class JsonDbNotify;
class JsonDbPartitionPrivate;
class JsonDbQueryObject;
@@ -141,11 +139,12 @@ private Q_SLOTS:
void notificationsAvailable();
void notificationError(QtJsonDb::QJsonDbWatcher::ErrorCode code, const QString &message);
- friend class JsonDatabase;
friend class JsonDbNotify;
friend class JsonDbQueryObject;
};
+QJSValue qjsonobject_list_to_qjsvalue(const QList<QJsonObject> &list);
+
QT_END_NAMESPACE_JSONDB
#endif
diff --git a/src/imports/jsondb/jsondbqueryobject.cpp b/src/imports/jsondb/jsondbqueryobject.cpp
index ac50a49..02d0133 100644
--- a/src/imports/jsondb/jsondbqueryobject.cpp
+++ b/src/imports/jsondb/jsondbqueryobject.cpp
@@ -43,7 +43,7 @@
#include "jsondatabase.h"
#include "plugin.h"
#include <private/qjsondbstrings_p.h>
-#include <jsondbmodelutils.h>
+#include <private/qjsondbmodelutils_p.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE_JSONDB
diff --git a/src/imports/jsondb/jsondbsortinglistmodel_p.h b/src/imports/jsondb/jsondbsortinglistmodel_p.h
index b109bc5..345baa4 100644
--- a/src/imports/jsondb/jsondbsortinglistmodel_p.h
+++ b/src/imports/jsondb/jsondbsortinglistmodel_p.h
@@ -51,8 +51,7 @@
#include <QUuid>
#include <QJSEngine>
#include <QJSValueIterator>
-
-#include "jsondbmodelutils.h"
+#include <private/qjsondbmodelutils_p.h>
QT_BEGIN_NAMESPACE_JSONDB
diff --git a/tests/auto/jsondbcachinglistmodel/jsondbcachinglistmodel.pro b/tests/auto/jsondbcachinglistmodel/jsondbcachinglistmodel.pro
index c054dd4..fd7fbb1 100644
--- a/tests/auto/jsondbcachinglistmodel/jsondbcachinglistmodel.pro
+++ b/tests/auto/jsondbcachinglistmodel/jsondbcachinglistmodel.pro
@@ -3,7 +3,7 @@ TARGET = tst_jsondbcachinglistmodel
DEPENDPATH += .
INCLUDEPATH += . ../../shared/
-QT = core network testlib gui qml jsondb
+QT = core network testlib gui qml jsondb-private
CONFIG -= app_bundle
CONFIG += testcase
diff --git a/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.cpp b/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.cpp
index 5a50c93..fe2f829 100644
--- a/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.cpp
+++ b/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.cpp
@@ -42,6 +42,7 @@
#include <QtTest/QtTest>
#include <QJSEngine>
#include "testjsondbcachinglistmodel.h"
+#include "private/qjsondbquerymodel_p.h"
#include "../../shared/util.h"
#include <QQmlListReference>
@@ -1570,6 +1571,44 @@ void TestJsonDbCachingListModel::getItemNotInCache()
deleteModel(listModel);
}
+void TestJsonDbCachingListModel::useDataToGetUuidAndIndexValue()
+{
+ resetWaitFlags();
+
+ createIndex("anotherNumber", "number");
+
+ for (int i=0; i < 1000; i++) {
+ QVariantMap item;
+ item.insert("_type", __FUNCTION__);
+ item.insert("anotherNumber", i);
+ int id = i%2 ? create(item, "com.nokia.shared.1") : create(item, "com.nokia.shared.2");
+ waitForResponse1(id);
+ }
+
+ QJsonDbQueryModel *queryModel = new QJsonDbQueryModel(QJsonDbConnection::defaultConnection(), this);
+ queryModel->setSortOrder(QString("[/anotherNumber]"));
+ queryModel->setQuery(QString("[?_type=\"%1\"]").arg(__FUNCTION__));
+ queryModel->setPartitionNames(QStringList() << "com.nokia.shared.1" << "com.nokia.shared.2");
+ queryModel->setQueryRoleNames(QStringList() << "_type" << "_uuid" << "_indexValue" << "anotherNumber");
+ connectListModel(queryModel);
+
+ queryModel->populate();
+
+ mWaitingForReset = true;
+ waitForExitOrTimeout();
+ QCOMPARE(mWaitingForReset, false);
+
+ QCOMPARE(queryModel->rowCount(), 1000);
+
+ for (int i=0; i < 1000; i++) {
+ QVariant iv = queryModel->data (i, 1);
+ iv = queryModel->data (i, 2);
+ QCOMPARE(iv.toInt(), i);
+ }
+
+ delete queryModel;
+}
+
QStringList TestJsonDbCachingListModel::getOrderValues(QAbstractListModel *listModel)
{
QStringList vals;
diff --git a/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.h b/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.h
index c835b8c..f0e39a1 100644
--- a/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.h
+++ b/tests/auto/jsondbcachinglistmodel/testjsondbcachinglistmodel.h
@@ -102,6 +102,7 @@ private slots:
void indexOfUuid();
void roleNames();
void getItemNotInCache();
+ void useDataToGetUuidAndIndexValue();
public:
void timeout();
private: