summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJamey Hicks <jamey.hicks@nokia.com>2012-03-22 09:57:37 -0400
committerQt by Nokia <qt-info@nokia.com>2012-03-26 19:19:22 +0200
commit148586f1ae531c0ecda68ee5d1bf80f123738c14 (patch)
tree0890ee1565ccff62ec20eb5ae3f04c257b7cd48b
parenta3f84f262af945ac44e95902d8256662575bbef2 (diff)
Updated Reduce
* Added sourceKeyFunction for determining source key as alternative to sourceKeyName * If targetValueName is null, use whole object returned from add/subtract as result of reduce. This will be the only behavior once all Reduces are converted. * Combined some redundant code in JsonDbReduceDefinition. * Added basic Reduce documentation. Change-Id: I258d9e24bb5fa620634b1168832ee4bbc1d58aca Reviewed-by: Kevin Simons <kevin.simons@nokia.com>
-rw-r--r--doc/src/index.qdoc2
-rw-r--r--doc/src/reduce-views.qdoc136
-rw-r--r--src/daemon/jsondbreducedefinition.cpp151
-rw-r--r--src/daemon/jsondbreducedefinition.h18
-rw-r--r--tests/auto/daemon/json/reduce.json4
-rw-r--r--tests/auto/daemon/testjsondb.cpp111
6 files changed, 345 insertions, 77 deletions
diff --git a/doc/src/index.qdoc b/doc/src/index.qdoc
index 4026e8c..6e8842a 100644
--- a/doc/src/index.qdoc
+++ b/doc/src/index.qdoc
@@ -110,6 +110,7 @@ sets.
\list
\li \l {Map and Join Views}
+\li \l {Reduce Views}
\endlist
\section2 Notifications
@@ -134,6 +135,7 @@ See also \l{QJsonDbWatcher}
\li \l{declarative/desktop-inspector}{Desktop Database Inspector Example}
\li \l{declarative/map-phone}{Creating a Map View}
\li \l{declarative/joinview}{Creating a Join View}
+\li \l{declarative/reduce-phone}{Creating a Reduce View}
\li \l{client/chat}{Creating a chat client using the C++ API}
\endlist
*/
diff --git a/doc/src/reduce-views.qdoc b/doc/src/reduce-views.qdoc
new file mode 100644
index 0000000..d16c5f9
--- /dev/null
+++ b/doc/src/reduce-views.qdoc
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file.
+**
+** 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$
+**
+****************************************************************************/
+
+/*!
+\page reduce-views.html
+\title Reduce Views
+
+\target reduce
+
+\section1 Performing a Reduce
+
+A reduce is defined by creating a \l Reduce object in the database. A
+Reduce operates on objects of a single source type, producing values
+of a single target type. A Reduce incrementally combines source
+objects with matching keys using JavaScript "add" and "subtract"
+functions.
+
+\table
+\row
+\li _type
+\li Reduce
+
+\row
+\li targetType
+\li The output type of this reduction. The target type must extend \l View.
+
+\row
+\li targetKeyName
+\li The name of the key property in the target objects. Defaults to "key" if unspecified.
+
+\row
+\li targetValueName (should be "null")
+
+\li The name of the value property in the target objects. Defaults to
+"value" if unspecified. If this is defined to be "null", then the
+object returned from the "add" and "subtract" functions is used as the
+whole target object. The old behavior is deprecated and will be
+removed.
+
+\row
+\li sourceType
+\li The type of the source objects used in this reduction
+
+\row
+\li sourceKeyName
+
+\li The name of the key property in the source objects. Defaults to
+"key" if unspecified. Exactly one of sourceKeyName and
+sourceKeyFunction must be specified.
+
+\row
+\li sourceKeyFunction
+
+\li A string that represents a JavaScript function that takes an
+object and returns the key value for that object. Exactly one of
+sourceKeyName and sourceKeyFunction must be specified.
+
+\row
+\li add
+\li A string that evaluates to a Javascript function taking three arguments: keyValue, targetObject, sourceObject..
+
+\row
+\li subtract
+\li A string that evaluates to a Javascript function taking three arguments: keyValue, targetObject, sourceObject.
+\endtable
+
+
+\section2 Deterministic Uuids
+
+View objects created by Reduce are assigned a deterministic Uuid. If
+the "add" or "subtract" functions assign _uuid to the object they
+return, then that value is used as the uuid for the object. Otherwise,
+the view engine constructs an identifier string as follows:
+\code
+ var identifier = targetType + ":" + reduceDefinitionUuid + ":" + keyValue;
+ var uuid = jsondb.createUuidFromString(identifier);
+\endcode
+
+\section2 Theory of Operation
+
+When the view is updated, it operates on all of the source objects
+that were changed since the last time the view was updated.
+
+If a source object is created, the key is extracted from the object
+using either sourceKeyName or sourceKeyFunction. The "add" function is
+applied to three arguments: the key value, the previous target object
+for the key value or undefined, and the source object.
+
+If a source object is removed, the key is extracted from the object
+that was removed using either sourceKeyName or sourceKeyFunction. The
+"remove" function is applied to three arguments: the key value, the
+previous target object for the key value or undefined, and the source
+object.
+
+If a source object is updated, it is treated as a remove of the
+previous value and a creation of the new value, so there will be a
+call to the "subtract" function and then the "add" function.
+
+All of the changes are staged together during an update of the view to
+minimize the number of changes of target objects visible via
+notifications.
+
+\section2 Reduce Proxy
+
+When the map functions run, they have access to a jsondb proxy object with one method:
+
+\table
+\row
+\li \l {jsondb.createUuidFromString}{jsondb.createUuidFromString}(identifier)
+\endtable
+
+*/
diff --git a/src/daemon/jsondbreducedefinition.cpp b/src/daemon/jsondbreducedefinition.cpp
index f591fd5..1021413 100644
--- a/src/daemon/jsondbreducedefinition.cpp
+++ b/src/daemon/jsondbreducedefinition.cpp
@@ -73,11 +73,20 @@ JsonDbReduceDefinition::JsonDbReduceDefinition(const JsonDbOwner *owner, JsonDbP
, mTargetType(mDefinition.value("targetType").toString())
, mTargetTable(mPartition->findObjectTable(mTargetType))
, mSourceType(mDefinition.value("sourceType").toString())
- , mTargetKeyName(mDefinition.contains("targetKeyName") ? mDefinition.value("targetKeyName").toString() : QString("key"))
- , mTargetValueName(mDefinition.contains("targetValueName") ? mDefinition.value("targetValueName").toString() : QString("value"))
- , mSourceKeyName(mDefinition.contains("sourceKeyName") ? mDefinition.value("sourceKeyName").toString() : QString("key"))
- , mSourceKeyNameList(mSourceKeyName.split("."))
{
+ if (mDefinition.contains("targetKeyName"))
+ mTargetKeyName = mDefinition.value("targetKeyName").toString();
+ else
+ mTargetKeyName = QLatin1String("key");
+ if (mDefinition.contains("sourceKeyName"))
+ mSourceKeyName = mDefinition.value("sourceKeyName").toString();
+ mSourceKeyNameList = mSourceKeyName.split(".");
+ if (mDefinition.contains("targetValueName")) {
+ if (mDefinition.value("targetValueName").isString())
+ mTargetValueName = mDefinition.value("targetValueName").toString();
+ } else
+ mTargetValueName = QLatin1String("value");
+
}
void JsonDbReduceDefinition::definitionCreated()
@@ -115,7 +124,7 @@ void JsonDbReduceDefinition::initScriptEngine()
mScriptEngine = new QJSEngine(this);
QString message;
- bool status = compileFunctions(mScriptEngine, mDefinition, mAddFunction, mSubtractFunction, message);
+ bool status = compileFunctions(mScriptEngine, mDefinition, mFunctions, message);
if (!status)
setError(message);
@@ -128,14 +137,19 @@ void JsonDbReduceDefinition::initScriptEngine()
void JsonDbReduceDefinition::releaseScriptEngine()
{
- mAddFunction = QJSValue();
- mSubtractFunction = QJSValue();
+ mFunctions.clear();
delete mScriptEngine;
mScriptEngine = 0;
}
void JsonDbReduceDefinition::initIndexes()
{
+ // TODO: this index should not be automatic
+ if (!mSourceKeyName.isEmpty()) {
+ JsonDbObjectTable *sourceTable = mPartition->findObjectTable(mSourceType);
+ sourceTable->addIndexOnProperty(mSourceKeyName, QLatin1String("string"));
+ }
+ // TODO: this index should not be automatic
mTargetTable->addIndexOnProperty(mTargetKeyName, QLatin1String("string"), mTargetType);
mTargetTable->addIndexOnProperty(QLatin1String("_reduceUuid"), QLatin1String("string"), mTargetType);
}
@@ -143,14 +157,9 @@ void JsonDbReduceDefinition::initIndexes()
void JsonDbReduceDefinition::updateObject(JsonDbObject before, JsonDbObject after)
{
initScriptEngine();
- Q_ASSERT(mAddFunction.isCallable());
- QJsonValue beforeKeyValue = mSourceKeyName.contains(".")
- ? before.propertyLookup(mSourceKeyNameList)
- : before.value(mSourceKeyName);
- QJsonValue afterKeyValue = mSourceKeyName.contains(".")
- ? after.propertyLookup(mSourceKeyNameList)
- : after.value(mSourceKeyName);
+ QJsonValue beforeKeyValue = sourceKeyValue(before);
+ QJsonValue afterKeyValue = sourceKeyValue(after);
if (!after.isEmpty() && !before.isEmpty() && (beforeKeyValue != afterKeyValue)) {
// do a subtract only on the before key
@@ -184,9 +193,9 @@ void JsonDbReduceDefinition::updateObject(JsonDbObject before, JsonDbObject afte
QJsonValue value = previousValue;
if (!before.isEmpty())
- value = subtractObject(keyValue, value, before);
+ value = addObject(JsonDbReduceDefinition::Subtract, keyValue, value, before);
if (!after.isEmpty())
- value = addObject(keyValue, value, after);
+ value = addObject(JsonDbReduceDefinition::Add, keyValue, value, after);
JsonDbWriteResult res;
// if we had a previous object to reduce
@@ -222,21 +231,28 @@ void JsonDbReduceDefinition::updateObject(JsonDbObject before, JsonDbObject afte
setError(QString("Error executing add function: %1").arg(res.message));
}
-QJsonValue JsonDbReduceDefinition::addObject(const QJsonValue &keyValue, const QJsonValue &previousValue, JsonDbObject object)
+QJsonValue JsonDbReduceDefinition::addObject(JsonDbReduceDefinition::FunctionNumber functionNumber,
+ const QJsonValue &keyValue, QJsonValue previousValue, JsonDbObject object)
{
initScriptEngine();
QJSValue svKeyValue = JsonDbObject::toJSValue(keyValue, mScriptEngine);
- QJSValue svPreviousValue = JsonDbObject::toJSValue(previousValue.toObject().value(mTargetValueName), mScriptEngine);
+ if (!mTargetValueName.isEmpty())
+ previousValue = previousValue.toObject().value(mTargetValueName);
+ QJSValue svPreviousValue = JsonDbObject::toJSValue(previousValue, mScriptEngine);
QJSValue svObject = JsonDbObject::toJSValue(object, mScriptEngine);
QJSValueList reduceArgs;
reduceArgs << svKeyValue << svPreviousValue << svObject;
- QJSValue reduced = mAddFunction.call(reduceArgs);
+ QJSValue reduced = mFunctions[functionNumber].call(reduceArgs);
if (!reduced.isUndefined() && !reduced.isError()) {
- QJsonObject jsonReduced;
- jsonReduced.insert(mTargetValueName, JsonDbObject::fromJSValue(reduced));
- return jsonReduced;
+ QJsonValue jsonReduced = JsonDbObject::fromJSValue(reduced);
+ QJsonObject jsonReducedObject;
+ if (!mTargetValueName.isEmpty())
+ jsonReducedObject.insert(mTargetValueName, jsonReduced);
+ else
+ jsonReducedObject = jsonReduced.toObject();
+ return jsonReducedObject;
} else {
if (reduced.isError())
@@ -246,31 +262,6 @@ QJsonValue JsonDbReduceDefinition::addObject(const QJsonValue &keyValue, const Q
}
}
-QJsonValue JsonDbReduceDefinition::subtractObject(const QJsonValue &keyValue, const QJsonValue &previousValue, JsonDbObject object)
-{
- initScriptEngine();
- Q_ASSERT(mSubtractFunction.isCallable());
-
- QJSValue svKeyValue = JsonDbObject::toJSValue(keyValue, mScriptEngine);
- QJSValue svPreviousValue = JsonDbObject::toJSValue(previousValue.toObject().value(mTargetValueName),
- mScriptEngine);
- QJSValue sv = JsonDbObject::toJSValue(object, mScriptEngine);
-
- QJSValueList reduceArgs;
- reduceArgs << svKeyValue << svPreviousValue << sv;
- QJSValue reduced = mSubtractFunction.call(reduceArgs);
-
- if (!reduced.isUndefined() && !reduced.isError()) {
- QJsonObject jsonReduced;
- jsonReduced.insert(mTargetValueName, JsonDbObject::fromJSValue(reduced));
- return jsonReduced;
- } else {
- if (reduced.isError())
- setError("Error executing subtract function: " + reduced.toString());
- return QJsonValue(QJsonValue::Undefined);
- }
-}
-
bool JsonDbReduceDefinition::isActive() const
{
return !mDefinition.contains(JsonDbString::kActiveStr) || mDefinition.value(JsonDbString::kActiveStr).toBool();
@@ -302,45 +293,67 @@ bool JsonDbReduceDefinition::validateDefinition(const JsonDbObject &reduce, Json
&& view->mReduceDefinitionsBySource.value(sourceType)->uuid() != uuid)
message = QString("duplicate Reduce definition on source %1 and target %2")
.arg(sourceType).arg(targetType);
- else if (reduce.value("sourceKeyName").toString().isEmpty())
- message = QLatin1Literal("sourceKeyName property for Reduce not specified");
+ else if (reduce.value("sourceKeyName").toString().isEmpty() && reduce.value("sourceKeyFunction").toString().isEmpty())
+ message = QLatin1Literal("sourceKeyName or sourceKeyFunction must be provided for Reduce");
+ else if (!reduce.value("sourceKeyName").toString().isEmpty() && !reduce.value("sourceKeyFunction").toString().isEmpty())
+ message = QLatin1Literal("Only one of sourceKeyName and sourceKeyFunction may be provided for Reduce");
else if (reduce.value("add").toString().isEmpty())
message = QLatin1Literal("add function for Reduce not specified");
else if (reduce.value("subtract").toString().isEmpty())
message = QLatin1Literal("subtract function for Reduce not specified");
+ else if (reduce.contains("targetValueName")
+ && !(reduce.value("targetValueName").isString() || reduce.value("targetValueName").isNull()))
+ message = QLatin1Literal("targetValueName for Reduce must be a string or null");
else {
// FIXME: This is static because otherwise we leak memory per QJSEngine instance
static QJSEngine *scriptEngine = new QJSEngine;
- QJSValue addFunction, subtractFunction;
+ QVector<QJSValue> functions;
// check for script errors
- compileFunctions(scriptEngine, reduce, addFunction, subtractFunction, message);
+ compileFunctions(scriptEngine, reduce, functions, message);
scriptEngine->collectGarbage();
}
return message.isEmpty();
}
-bool JsonDbReduceDefinition::compileFunctions(QJSEngine *scriptEngine, QJsonObject definition, QJSValue &addFunction, QJSValue &subtractFunction, QString &message)
+bool JsonDbReduceDefinition::compileFunctions(QJSEngine *scriptEngine, QJsonObject definition,
+ QVector<QJSValue> &functions, QString &message)
{
- Q_ASSERT(!definition.value("add").toString().isEmpty());
- Q_ASSERT(!definition.value("subtract").toString().isEmpty());
- QString script = definition.value("add").toString();
- QJSValue result = scriptEngine->evaluate(QString("var %1 = (%2); %1;").arg("add").arg(script));
-
- if (result.isError() || !result.isCallable()) {
- message = QString("Unable to parse add function: %1").arg(result.toString());
- return false;
+ bool status = true;
+ QStringList functionNames = (QStringList()
+ << QLatin1String("add")
+ << QLatin1String("subtract")
+ << QLatin1String("sourceKeyFunction"));
+ int i = 0;
+ functions.resize(3);
+ foreach (const QString &functionName, functionNames) {
+ int functionNumber = i++;
+ if (!definition.contains(functionName))
+ continue;
+ QString script = definition.value(functionName).toString();
+ QJSValue result = scriptEngine->evaluate(QString("(%1)").arg(script));
+
+ if (result.isError() || !result.isCallable()) {
+ message = QString("Unable to parse add function: %1").arg(result.toString());
+ status = false;
+ continue;
+ }
+ functions[functionNumber] = result;
}
- addFunction = result;
+ return status;
+}
- script = definition.value("subtract").toString();
- result = scriptEngine->evaluate(QString("var %1 = (%2); %1;").arg("subtract").arg(script));
+QJsonValue JsonDbReduceDefinition::sourceKeyValue(const QJsonObject &object)
+{
+ if (object.isEmpty()) {
+ return QJsonValue(QJsonValue::Undefined);
+ } else if (mFunctions[JsonDbReduceDefinition::SourceKeyValue].isCallable()) {
+ QJSValueList args;
+ args << JsonDbObject::toJSValue(object, mScriptEngine);
+ QJsonValue keyValue = JsonDbObject::fromJSValue(mFunctions[JsonDbReduceDefinition::SourceKeyValue].call(args));
+ return keyValue;
+ } else
+ return mSourceKeyName.contains(".") ? JsonDbObject(object).propertyLookup(mSourceKeyNameList) : object.value(mSourceKeyName);
- if (result.isError() || !result.isCallable()) {
- message = QString("Unable to parse subtract function: %1").arg(result.toString());
- return false;
- }
- subtractFunction = result;
- return true;
}
QT_END_NAMESPACE_JSONDB
diff --git a/src/daemon/jsondbreducedefinition.h b/src/daemon/jsondbreducedefinition.h
index 6530a82..214153e 100644
--- a/src/daemon/jsondbreducedefinition.h
+++ b/src/daemon/jsondbreducedefinition.h
@@ -77,8 +77,6 @@ public:
QStringList sourceKeyNameList() const { return mSourceKeyNameList; }
bool isActive() const;
QJsonObject definition() const { return mDefinition; }
- const QJSValue &addFunction() const { return mAddFunction; }
- const QJSValue &subtractFunction() const { return mSubtractFunction; }
const JsonDbOwner *owner() const { return mOwner; }
static void definitionRemoved(JsonDbPartition *partition, JsonDbObjectTable *table, const QString targetType, const QString &definitionUuid);
@@ -89,21 +87,27 @@ public:
void initIndexes();
void updateObject(JsonDbObject before, JsonDbObject after);
- QJsonValue addObject(const QJsonValue &keyValue, const QJsonValue &previousResult, JsonDbObject object);
- QJsonValue subtractObject(const QJsonValue &keyValue, const QJsonValue &previousResult, JsonDbObject object);
void setError(const QString &errorMsg);
static bool validateDefinition(const JsonDbObject &reduce, JsonDbPartition *partition, QString &message);
- static bool compileFunctions(QJSEngine *scriptEngine, QJsonObject definition, QJSValue &addFunction, QJSValue &subFunction, QString &message);
+
+private:
+ enum FunctionNumber {
+ Add = 0,
+ Subtract = 1,
+ SourceKeyValue = 2
+ };
+ static bool compileFunctions(QJSEngine *scriptEngine, QJsonObject definition, QVector<QJSValue> &mFunctions, QString &message);
+ QJsonValue sourceKeyValue(const QJsonObject &object);
+ QJsonValue addObject(FunctionNumber fn, const QJsonValue &keyValue, QJsonValue previousResult, JsonDbObject object);
private:
const JsonDbOwner *mOwner;
JsonDbPartition *mPartition;
QJsonObject mDefinition;
QJSEngine *mScriptEngine;
- QJSValue mAddFunction;
- QJSValue mSubtractFunction;
+ QVector<QJSValue> mFunctions;
QString mUuid;
QString mTargetType;
JsonDbObjectTable *mTargetTable;
diff --git a/tests/auto/daemon/json/reduce.json b/tests/auto/daemon/json/reduce.json
index f95d4f1..5939fb7 100644
--- a/tests/auto/daemon/json/reduce.json
+++ b/tests/auto/daemon/json/reduce.json
@@ -20,6 +20,8 @@
"targetKeyName": "firstName",
"targetValueName": "count",
"add": "function add (k, z, c) { if (!z) {z = 0}; z += 1; if (z) return z;}",
- "subtract": "function subtract (k, z, c) { if (!z) {z = 0}; z -= 1; if (z) return z;}"
+ "subtract": "function subtract (k, z, c) { if (!z) {z = 0}; z -= 1; if (z) return z;}",
+ "addFlattened": "function add (k, z, c) { if (!z) {z = {count: 0}}; z.count += 1; if (z.count) return z;}",
+ "subtractFlattened": "function subtract (k, z, c) { if (!z) {z = {count: 0}}; z.count -= 1; if (z.count) return z;}"
}
]
diff --git a/tests/auto/daemon/testjsondb.cpp b/tests/auto/daemon/testjsondb.cpp
index 3532faa..ac37b35 100644
--- a/tests/auto/daemon/testjsondb.cpp
+++ b/tests/auto/daemon/testjsondb.cpp
@@ -175,6 +175,8 @@ private slots:
void mapSchemaViolation();
void mapArrayConversion();
void reduce();
+ void reduceFlattened();
+ void reduceSourceKeyFunction();
void reduceRemoval();
void reduceUpdate();
void reduceDuplicate();
@@ -1556,6 +1558,17 @@ void TestJsonDb::reduceDefinitionInvalid()
verifyErrorResult(res);
QVERIFY(res.message.contains("View"));
+ // fail because targetValue name is not a string or null
+ reduceDefinition = JsonDbObject();
+ reduceDefinition.insert(JsonDbString::kTypeStr, QLatin1String("Reduce"));
+ reduceDefinition.insert("targetType", QLatin1String("MyViewType"));
+ reduceDefinition.insert("sourceType", QLatin1String("Contact"));
+ reduceDefinition.insert("add", QLatin1String("function add (k, z, c) { }"));
+ reduceDefinition.insert("subtract", QLatin1String("function subtract (k, z, c) { }"));
+ reduceDefinition.insert("targetValueName", true);
+ res = create(mOwner, reduceDefinition);
+ verifyErrorResult(res);
+
//schemaRes.value("result").toObject()
verifyGoodResult(remove(mOwner, schema));
}
@@ -2182,6 +2195,104 @@ void TestJsonDb::reduce()
mJsonDbPartition->removeIndex("MyContactCount");
}
+void TestJsonDb::reduceFlattened()
+{
+ QJsonArray objects(readJsonFile(":/daemon/json/reduce-data.json").toArray());
+
+ JsonDbObjectList toDelete;
+ JsonDbObjectList reduces;
+
+ QHash<QString, int> firstNameCount;
+ for (int ii = 0; ii < objects.size(); ii++) {
+ JsonDbObject object(objects.at(ii).toObject());
+ JsonDbWriteResult result = create(mOwner, object);
+ verifyGoodResult(result);
+ firstNameCount[object.value("firstName").toString()]++;
+ toDelete.append(object);
+ }
+
+ objects = readJsonFile(":/daemon/json/reduce.json").toArray();
+ for (int ii = 0; ii < objects.size(); ii++) {
+ JsonDbObject object(objects.at(ii).toObject());
+ if (object.value(JsonDbString::kTypeStr).toString() == JsonDbString::kReduceTypeStr) {
+ object.insert(QLatin1String("add"), object.value(QLatin1String("addFlattened")));
+ object.insert(QLatin1String("subtract"), object.value(QLatin1String("subtractFlattened")));
+ // transitional behavior: null targetValueName indicates whole object is value of the Reduce
+ object.insert(QLatin1String("targetValueName"), QJsonValue(QJsonValue::Null));
+ }
+ JsonDbWriteResult result = create(mOwner, object);
+ verifyGoodResult(result);
+ if (object.value(JsonDbString::kTypeStr).toString() == "Reduce")
+ reduces.append(object);
+ else
+ toDelete.append(object);
+ }
+
+ JsonDbQueryResult queryResult = find(mOwner, QLatin1String("[?_type=\"MyContactCount\"]"));
+ verifyGoodQueryResult(queryResult);
+ QCOMPARE(queryResult.data.size(), firstNameCount.keys().count());
+
+ JsonDbObjectList data = queryResult.data;
+ for (int ii = 0; ii < data.size(); ii++) {
+ QCOMPARE((int)data.at(ii).value("count").toDouble(),
+ firstNameCount[data.at(ii).value("firstName").toString()]);
+ }
+ for (int ii = 0; ii < reduces.size(); ii++)
+ verifyGoodResult(remove(mOwner, reduces.at(ii)));
+ for (int ii = 0; ii < toDelete.size(); ii++)
+ verifyGoodResult(remove(mOwner, toDelete.at(ii)));
+ mJsonDbPartition->removeIndex("MyContactCount");
+}
+
+void TestJsonDb::reduceSourceKeyFunction()
+{
+ QJsonArray objects(readJsonFile(":/daemon/json/reduce-data.json").toArray());
+
+ JsonDbObjectList toDelete;
+ JsonDbObjectList reduces;
+
+ QHash<QString, int> firstNameCount;
+ for (int ii = 0; ii < objects.size(); ii++) {
+ JsonDbObject object(objects.at(ii).toObject());
+ JsonDbWriteResult result = create(mOwner, object);
+ verifyGoodResult(result);
+ firstNameCount[object.value("firstName").toString()]++;
+ toDelete.append(object);
+ }
+
+ objects = readJsonFile(":/daemon/json/reduce.json").toArray();
+ for (int ii = 0; ii < objects.size(); ii++) {
+ JsonDbObject object(objects.at(ii).toObject());
+ if (object.value(JsonDbString::kTypeStr).toString() == JsonDbString::kReduceTypeStr) {
+ QString sourceKeyName = object.value(QLatin1String("sourceKeyName")).toString();
+ object.remove(QLatin1String("sourceKeyName"));
+ object.insert(QLatin1String("sourceKeyFunction"),
+ QString("function (o) { return o.%1; }").arg(sourceKeyName));
+ }
+ JsonDbWriteResult result = create(mOwner, object);
+ verifyGoodResult(result);
+ if (object.value(JsonDbString::kTypeStr).toString() == "Reduce")
+ reduces.append(object);
+ else
+ toDelete.append(object);
+ }
+
+ JsonDbQueryResult queryResult = find(mOwner, QLatin1String("[?_type=\"MyContactCount\"]"));
+ verifyGoodQueryResult(queryResult);
+ QCOMPARE(queryResult.data.size(), firstNameCount.keys().count());
+
+ JsonDbObjectList data = queryResult.data;
+ for (int ii = 0; ii < data.size(); ii++) {
+ QCOMPARE((int)data.at(ii).value("count").toDouble(),
+ firstNameCount[data.at(ii).value("firstName").toString()]);
+ }
+ for (int ii = 0; ii < reduces.size(); ii++)
+ verifyGoodResult(remove(mOwner, reduces.at(ii)));
+ for (int ii = 0; ii < toDelete.size(); ii++)
+ verifyGoodResult(remove(mOwner, toDelete.at(ii)));
+ mJsonDbPartition->removeIndex("MyContactCount");
+}
+
void TestJsonDb::reduceRemoval()
{
QJsonArray objects(readJsonFile(":/daemon/json/reduce-data.json").toArray());