diff --git a/extensions/kDBDocuments/QueryExecutor.cpp b/extensions/kDBDocuments/QueryExecutor.cpp index b90299ef5b4c22808d085a72d265c0e01992c7c5..6998b0702376c574163d40ccb6ba0762fd20b456 100644 --- a/extensions/kDBDocuments/QueryExecutor.cpp +++ b/extensions/kDBDocuments/QueryExecutor.cpp @@ -28,11 +28,49 @@ struct QueryExecutor::Private QStringList include_directories; }; - knowCore::ReturnValue<QVariant> create_document(const Cyqlops::Yaml::Node _node, const Options& _options); - knowDBC::Result execute_one(const Cyqlops::Yaml::Node _node, const QString& _query, const Options& _options, const kDB::Repository::QueryConnectionInfo& _connectionInfo); + knowCore::ReturnValue<QVariant> create_document(const Cyqlops::Yaml::Node& _node, const Options& _options); + knowDBC::Result execute_one(const Cyqlops::Yaml::Node& _node, const QString& _query, const Options& _options, const kDB::Repository::QueryConnectionInfo& _connectionInfo); + + knowCore::ReturnValue<QList<DocumentRecord>> execute_matches(const Cyqlops::Yaml::Node& _matches_node, const CollectionRecord& _cr_source); + + QJsonValue update_rec(const QJsonValue& _value, const Cyqlops::Yaml::Node& _update); }; -knowCore::ReturnValue<QVariant> QueryExecutor::Private::create_document(const Cyqlops::Yaml::Node _node, const Options& _options) + +knowCore::ReturnValue<QList<DocumentRecord>> QueryExecutor::Private::execute_matches(const Cyqlops::Yaml::Node& _matches_node, const CollectionRecord& _cr_source) +{ + if(_matches_node.isUndefined() or (_matches_node.isMap() and _matches_node.childrenCount() == 0)) + { + return kCrvSuccess(DocumentRecord::all(_cr_source).exec()); + } else if(_matches_node.isMap()) { + DocumentSelectQuery sq; + for(const QString& key : _matches_node.keys()) + { + Cyqlops::Yaml::Node cn = _matches_node[key]; + sq = sq and DocumentRecord::by(_cr_source, "(%1 #>> %2) = %3", DocumentFields::content, QStringList() << key, cn.toVariant()); + } + return kCrvSuccess(sq.exec()); + } else { + return kCrvError("Expected matches to be a map."); + } +} + +QJsonValue QueryExecutor::Private::update_rec(const QJsonValue& _value, const Cyqlops::Yaml::Node& _update) +{ + if(_update.isMap()) + { + QJsonObject obj = _value.toObject(); + for(const QString& key : _update.keys()) + { + obj[key] = update_rec(obj[key], _update[key]); + } + return obj; + } else { + return QJsonValue::fromVariant(_update.toVariant()); + } +} + +knowCore::ReturnValue<QVariant> QueryExecutor::Private::create_document(const Cyqlops::Yaml::Node& _node, const Options& _options) { using ynType = Cyqlops::Yaml::Node::Type; switch(_node.type()) @@ -85,11 +123,13 @@ knowCore::ReturnValue<QVariant> QueryExecutor::Private::create_document(const Cy return kCrvSuccess(_node.toInteger()); case ynType::Double: return kCrvSuccess(_node.toDouble()); + case ynType::Boolean: + return kCrvSuccess(_node.toBoolean()); } return kCrvError("Unhandled YAML type."); } -knowDBC::Result QueryExecutor::Private::execute_one(const Cyqlops::Yaml::Node _node, const QString& _query, const Options& _options, const kDB::Repository::QueryConnectionInfo& _connectionInfo) +knowDBC::Result QueryExecutor::Private::execute_one(const Cyqlops::Yaml::Node& _node, const QString& _query, const Options& _options, const kDB::Repository::QueryConnectionInfo& _connectionInfo) { KNOWCORE_ASSERT(_node.isMap() and _node.childrenCount() == 1); QString key = _node.keys().first(); @@ -178,22 +218,15 @@ knowDBC::Result QueryExecutor::Private::execute_one(const Cyqlops::Yaml::Node _n cr_source = cr_query.first(); } - QList<DocumentRecord> documents; - Cyqlops::Yaml::Node matches_node = query_node["matches"]; - if(matches_node.isUndefined() or (matches_node.isMap() and matches_node.childrenCount() == 0)) + knowCore::ReturnValue<QList<DocumentRecord>> documents_rv = execute_matches(query_node["matches"], cr_source); + + if(not documents_rv.success()) { - documents = DocumentRecord::all(cr_source).exec(); - } else if(matches_node.isMap()) { - DocumentSelectQuery sq; - for(const QString& key : matches_node.keys()) - { - Cyqlops::Yaml::Node cn = matches_node[key]; - sq = sq and DocumentRecord::by(cr_source, "(%1 #>> %2) = %3", DocumentFields::content, QStringList() << key, cn.toVariant()); - } - documents = sq.exec(); - } else { - return knowDBC::Result::create(_query, "Expected matches to be a map."); + return knowDBC::Result::create(_query, documents_rv.message()); } + + QList<DocumentRecord> documents = documents_rv.value(); + QStringList columnNames; QList<knowCore::ValueList> results; if(what_node.isString() and what_node.toString() == "*") @@ -249,6 +282,41 @@ knowDBC::Result QueryExecutor::Private::execute_one(const Cyqlops::Yaml::Node _n } return knowDBC::Result::create(_query, columnNames, results); + } + /*************************** + * Execute set queries + */ + else if(key == "update") + { + Cyqlops::Yaml::Node from_node = query_node["from"]; + if(from_node.isUndefined()) + { + return knowDBC::Result::create(_query, "Missing 'from' node in retrieve statement."); + } + + QString source = from_node.toString(); + + CollectionRecord cr_source; + QList<CollectionRecord> cr_query = CollectionRecord::byName(_connectionInfo, source).exec(); + if(cr_query.isEmpty()) + { + return knowDBC::Result::create(_query, clog_qt::qformat("Unknown collection '{}'.", source)); + } else { + cr_source = cr_query.first(); + } + knowCore::ReturnValue<QList<DocumentRecord>> documents_rv = execute_matches(query_node["matches"], cr_source); + + if(not documents_rv.success()) + { + return knowDBC::Result::create(_query, documents_rv.message()); + } + for(DocumentRecord dr : documents_rv.value()) + { + Cyqlops::Yaml::Node values_node = query_node["values"]; + dr.setContent(update_rec(dr.content(), values_node)); + dr.record(); + } + return knowDBC::Result::create(_query, {}, {}); } else { return knowDBC::Result::create(_query, clog_qt::qformat("Unsupported type of query: {}", key)); } diff --git a/extensions/kDBDocuments/kDQLFileLoader.cpp b/extensions/kDBDocuments/kDQLFileLoader.cpp index 2265bfbfb5490337dd6266ba672262fe6b0adfbc..5f3854e204bb2013e57c43dc0f58bb833620829a 100644 --- a/extensions/kDBDocuments/kDQLFileLoader.cpp +++ b/extensions/kDBDocuments/kDQLFileLoader.cpp @@ -40,6 +40,7 @@ namespace case ynType::Undefined: case ynType::Integer: case ynType::Double: + case ynType::Boolean: return; case ynType::Sequence: for(std::size_t i = 0; i < _node.childrenCount(); ++i) diff --git a/extensions/kDBDocuments/tests/TestDocumentsQuery.cpp b/extensions/kDBDocuments/tests/TestDocumentsQuery.cpp index b6ecf44e99f9df29d35d65c8b4d860c57c560ea0..2f9e337d89ffcd98f91693e3416cfaffb2093e4a 100644 --- a/extensions/kDBDocuments/tests/TestDocumentsQuery.cpp +++ b/extensions/kDBDocuments/tests/TestDocumentsQuery.cpp @@ -44,6 +44,7 @@ create: - name: "a" age: 10 location: "world" + alive: false - name: "b" age: 20 location: "universe" @@ -70,6 +71,7 @@ retrieve: KNOWCORE_TEST_VERIFY_COMPARE(r_0.value("name").value<QString>(), "a"); KNOWCORE_TEST_VERIFY_COMPARE(r_0.value("age").value<int>(), 10); KNOWCORE_TEST_VERIFY_COMPARE(r_0.value("location").value<QString>(), "world"); + KNOWCORE_TEST_VERIFY_COMPARE(r_0.value("alive").value<bool>(), false); q.setQuery(R"( retrieve: @@ -114,6 +116,39 @@ retrieve: KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(0, 1), 30); KNOWCORE_TEST_VERIFY_COMPARE(r.value<QString>(0, "name"), "b"); KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(0, "age"), 30); + + // set + q.setQuery(R"( +update: + from: tests + matches: + name: "b" + values: + age: 25 + alive: true + dead: false +)"); + r = q.execute(); + VERIFY_QUERY_RESULT(r); + + q.setQuery(R"( +retrieve: + what: ["age", "alive", "dead"] + from: tests + matches: + name: "b" +)"); + r = q.execute(); + VERIFY_QUERY_RESULT(r); + QCOMPARE( r.fieldNames(), QStringList() << "age" << "alive" << "dead" ); + QCOMPARE( r.tuples(), 2 ); + KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(0, "age"), 25); + KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(0, "alive"), true); + KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(0, "dead"), false); + KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(1, 0), 25); + KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(1, 1), true); + KNOWCORE_TEST_VERIFY_COMPARE(r.value<int>(1, 2), false); + } QTEST_MAIN(TestDocumentsQuery)