MongoDB C++ Update an Element in an Array - c++

I am pretty new to MongoDB and C++.
My problem: I want to update a JSON-Document from my database collection:
{
"_id" : ObjectId("5ac4beacc0e2a512e6377d43"),
"docID" : "SL/UO4ZJgdUxcRLKxXDWMg==",
"docVersion" : "DA3EF8047AD0F[…]77C6F9286488CEE6a",
"userID" : "bob#nds-local",
"parts" : [
{
"partID" : "u2v[…]0KG7R",
"partVersion" : "",
"partKey" : "",
"docPosition" : 0,
"counter" : 0,
"users" : {
"everyone" : [ 1, 0, 0, 0, 0, 0, 0, 0, 0 ]
}
},
{
"partID" : "AZ3[…]1Odeku",
"partVersion" : "",
"partKey" : "",
"docPosition" : 0,
"counter" : 0,
"users" : {
}
}
]
}
First I want to get this document from my database. Therefore I am using this small code:
bsoncxx::stdx::optional<bsoncxx::document::value> resultDocument =
collection.find_one(document{} << "docID" << docID << finalize);
One way to manipulate an element is to use the update-function:
collection.update_one(document{} << "docID" << docID
<< "docVersion" << docVersion
<< finalize,
document{} << "$set" << open_document <<
"userID" << "oscar#nds-local" << close_document << finalize);
But I don't want to manipulate the userID!
I want to get access to the array with the key "parts" (depending on the "partID=u2v[…]0KG7R").
After that i want to update/replace the child-elements partVersion, partKey and docPosition of this array.
(1) How can I do that?
Furthermore I want to access the sub-array "users" and add keys, delete keys and manipulate the array of one special user.
(2) How could I realize this?
It would be great if someone can give me an example for my question (1). I hope that this answer of (1) will give me a clue of how to deal with question (2) ...

It is not required to retrieve the document from DB, you can update the document in one shot.
You need to add also the partID field in the query using the dot notation: parts.partID to find the one matching in the array.
To update the fields you requested (1) and to add a new key in users (2) you need to use the positional $ operator since it will allow accessing the element matched by the query.
It would look like this:
db["docs"].update_one(
make_document(
kvp("docID", docID),
kvp("parts.partID", partID)
),
make_document(
kvp("$set", make_document(
kvp("parts.$.partKey", newPartKey),
kvp("parts.$.partVersion", newPartVersion),
kvp("parts.$.docPosition", newDocPosition),
kvp("parts.$.users." + newUser, make_array(1,2,3,4,5))
)
)
)
);
The kvp("parts.$.users." + newUser, make_array(1,2,3,4,5)) will create a key inside users with the value of the newUser variable and it will contain the array [1,2,3,4,5]
Removing a user (2) can be done in a similar way using the $unset operator:
db["docs"].update_one(
make_document(
kvp("docID", docID),
kvp("parts.partID", partID)
),
make_document(
kvp("$unset", make_document(kvp("parts.$.users." + userToDelete, "")))
)
);
How to manipulate the array of one specific user (2) would depend on what/how exactly you plan to update it, but for that you can use the array update operators.
The following code snippet:
First, it updates the partKey, partVersion and
docPosition fields and adds a new user in the users field for the
partID:"AZ3[...]1Odeku" part
Second, it removes the previous added user
Please note that I've replaced the ellipsis … for three dots ... from your document data since it was causing that the mongocxx driver was never finding any match even if using also ellipsis in the query (it works fine in the mongo shell)
#include <iostream>
#include <bsoncxx/json.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/uri.hpp>
using bsoncxx::builder::basic::kvp;
using bsoncxx::builder::basic::make_array;
using bsoncxx::builder::basic::make_document;
void updatePartAndAddNewUser(const mongocxx::client& client,
const std::string& docID,
const std::string& partID,
const std::string& newPartKey,
const std::string& newPartVersion,
int newDocPosition,
const std::string& newUser)
{
mongocxx::database db = client["stack"];
db["docs"].update_one(
make_document(
kvp("docID", docID),
kvp("parts.partID", partID)
),
make_document(
kvp("$set", make_document(
kvp("parts.$.partKey", newPartKey),
kvp("parts.$.partVersion", newPartVersion),
kvp("parts.$.docPosition", newDocPosition),
kvp("parts.$.users." + newUser, make_array(1,2,3,4,5))
)
)
)
);
}
void removeUserFromPart(const mongocxx::client& client,
const std::string& docID,
const std::string& partID,
const std::string& userToDelete)
{
mongocxx::database db = client["stack"];
db["docs"].update_one(
make_document(
kvp("docID", docID),
kvp("parts.partID", partID)
),
make_document(
kvp("$unset", make_document(kvp("parts.$.users." + userToDelete, "")))
)
);
}
int main(int, char**)
{
std::cout << "Start program" << std::endl;
mongocxx::instance instance{};
mongocxx::client client{ mongocxx::uri{} };
updatePartAndAddNewUser(client,
"SL/UO4ZJgdUxcRLKxXDWMg==",
"AZ3[...]1Odeku",
"newPartKey",
"newPartVersion1",
1,
"nobody");
std::cout << "Part has been modified, press any key to remove a user...";
std::cin.ignore();
removeUserFromPart(client,
"SL/UO4ZJgdUxcRLKxXDWMg==",
"AZ3[...]1Odeku",
"nobody");
std::cout << "End program" << std::endl;
}
Generates this document after executing updatePartAndAddNewUser(...):
{
"_id" : ObjectId("5ac4beacc0e2a512e6377d43"),
"docID" : "SL/UO4ZJgdUxcRLKxXDWMg==",
"docVersion" : "DA3EF8047AD0F[...]77C6F9286488CEE6a",
"userID" : "bob#nds-local",
"parts" : [
{
"partID" : "u2v[...]0KG7R",
"partVersion" : "",
"partKey" : "",
"docPosition" : 0,
"counter" : 0,
"users" : {
"everyone" : [1,0,0,0,0,0,0,0,0]
}
},
{
"partID" : "AZ3[...]1Odeku",
"partVersion" : "newPartVersion1",
"partKey" : "newPartKey",
"docPosition" : 1,
"counter" : 0,
"users" : {
"nobody" : [1,2,3,4,5]
}
}
]
}
and after executing removeUserFromPart(...) the nobody user is gone:
{
"_id" : ObjectId("5ac4beacc0e2a512e6377d43"),
"docID" : "SL/UO4ZJgdUxcRLKxXDWMg==",
"docVersion" : "DA3EF8047AD0F[...]77C6F9286488CEE6a",
"userID" : "bob#nds-local",
"parts" : [
{
"partID" : "u2v[...]0KG7R",
"partVersion" : "",
"partKey" : "",
"docPosition" : 0,
"counter" : 0,
"users" : {
"everyone" : [1,0,0,0,0,0,0,0,0]
}
},
{
"partID" : "AZ3[...]1Odeku",
"partVersion" : "newPartVersion1",
"partKey" : "newPartKey",
"docPosition" : 1,
"counter" : 0,
"users" : {
}
}
]
}

Related

How to access array elements inside JSON in nlohman json in c++?

I have following JSON file
{
"list" :
{
"type" : "school",
"students" : [
{
"name" : "root",
"marks" : "100"
},
{
"name" : "joe",
"version" : "100"
}]
}
}
So in code I have parsed this JSON file and assigned this JSON object to JSON::student. So I am able to assign first element to string like this :
std::string type = student["list"]["type"];
and it works. SO similarly I want to do for "name" and "marks" in "students" array. How can I do that?
You can simply iterate over all the elements with iterators
std::string const str = "{ \"list\" : { \"type\" : \"school\", \"students\" : [ "
"{ \"name\" : \"root\", \"marks\" : \"100\"}, "
"{ \"name\" : \"joe\", \"version\" : \"100\" } "
"] } }";
nlohmann::json const js = nlohmann::json::parse(str);
nlohmann::json const& ja = js["list"]["students"];
for (auto it = ja.begin(); it != ja.end(); ++it) {
// For each individual element (root, joe, ...)
if (it->contains("name")) {
std::string const name = (*it)["name"].get<std::string>();
// Do something with the name
}
if (it->contains("marks")) {
std::string const marks = (*it)["marks"].get<std::string>();
// Do something with the marks
}
if (it->contains("version")) {
std::string const version = (*it)["version"].get<std::string>();
// Process version
}
// ...
}

update dynamodb list item

This seems like it should be really simple but I haven't found any examples or documentation. I've got a dynamodb table that looks like this:
record 1: {name, email, items[{product}, {item2}, {item3]}
record 2: (name, email, items[{product}, {item2}, {item3]}
I need to be able to update items elements, i.e., update item1 object in record 1. I can do this with the following code by hardcoding the list array element, but I can't figure out how to pass the item number into the update expression :
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"id" : { "S" : "${context.arguments.input.id}" }
},
"update" : {
"expression" : "SET #items[0].#product= :productVal",
"expressionNames" : {
"#product": "product",
},
"expressionValues" : {
":productVal": { "S" : "${context.arguments.input.product}" },
}
}
Have you tried something like:
"update" : {
"expression" : "SET #items[:idx].#product= :productVal",
"expressionNames" : {
"#product": "product",
},
"expressionValues" : {
":productVal": { "S" : "${context.arguments.input.product}" },
":idx": { "N" : 0 }
}
}

Converting a BSON document containing an array to JSON, removes the array from the converted JSON (C++)

I have a document that looks like this:
{
"_id" : ObjectId("5bd37e0128f41363c0006ac2"),
"source" : "Forge",
"data" : [
{
"symbol" : "EURUSD",
"bid" : 1.14021,
"ask" : 1.14024,
"price" : 1.14023,
"timestamp" : 1540587008
}
]
}
I want to get the data part out of the bson document, which I'm doing by this code:
auto dataDocument = view["data"].get_value().get_document();
auto textMessage = bsoncxx::to_json(dataDocument);
However the output when field data is an array as in the example is this:
{
"0": {
"symbol": "EURUSD",
"bid": 1.1405199999999999783,
"ask": 1.1405300000000000438,
"price": 1.1405300000000000438,
"timestamp": 1540580136
}
}
Instead of this (correct):
[{
"symbol": "EURUSD",
"bid": 1.14056,
"ask": 1.14057,
"price": 1.14057,
"timestamp": 1540580927
}
]
How come the brackets are removed and instead a "0" field is put there instead?
If I do a to_json on the whole document the array is kept, its only when I do to_json on the field data which is an array the error happens.
Any ideas?
Update, here's a working example which reproduce my issue:
#include <mongocxx/instance.hpp>
#include <bsoncxx/json.hpp>
#include <mongocxx/client.hpp>
int main()
{
mongocxx::instance inst{};
auto conn = mongocxx::client{ mongocxx::uri{} };
auto collection = conn["QuantBot"]["test"];
auto jsonDoc = R"(
{
"source" : "Forge",
"data" : [
{
"symbol" : "EURUSD",
"bid" : 1.13875,
"ask" : 1.13925,
"price" : 1.139,
"timestamp" : 1540758149
}
]
}
)";
auto bsonDocument = bsoncxx::from_json(jsonDoc);
collection.insert_one(bsonDocument.view());
auto cursor = std::make_unique<mongocxx::cursor>(collection.find({}));
auto cursorIt = std::make_unique<mongocxx::cursor::iterator>(cursor->begin());
auto view = bsoncxx::document::view(**cursorIt);
auto dataDocument = view["data"].get_value().get_document();
auto textMessage = bsoncxx::to_json(dataDocument);
}
I believe the problem is here: get_value().get_document(). Try making that say get_value().get_array() instead. Also note that you should be checking the type of the value returned by get_value before calling any methods on it. This is working more by good luck than by good design. The reason that you are seeing a literal zero there is because BSON arrays are represented as documents with integral keys. You have, effectively, cast an array to a document, so it is printing as a document with numeric keys, instead of an array as intended.

QT Reading a JSON file and storing and retriving values

I am trying to read a json file like shown below in QT. Can someone suggest a way to obtain values from json object and store them in separate containers or array such as test_cell2.CELLS[0] or some way so that nesting can also be taken care of and also I can access them easily after parsing the file
"test_cells2" : {
"CELLS" : {
"cell_0" : {
"prettyName" : "cell_1",
"CELLS" : {
"cell_1" : {
"prettyName" : "cell_1",
"type" : "default",
},
},
},
"cell_1" : {
"prettyName" : "cell_1",
"type" : "default",
},
"cell_2" : {
"type" : "text cell ko",
},
"cell_3" : {
"prettyName" : "cell_3",
"type" : "default",
},
"cell_4" : {
"data" : {
"settings" : {
"EXEC_PARAMETERS" : {
"defaultQueue" : "batch",
"environment" : {
"blabla" : "blabla2",
},
},
},
},
"type" : "parallel_test",
},
},
},
Take a look at this function
QJsonDocument::fromJson(QByteArray)
http://doc.qt.io/qt-5/qjsondocument.html#fromJson
Then use QJsonDocument::object() and you can use keys to get your values:
QJsonDocument doc = QJsonDocument::fromJson(QByteArray);
QJsonObject root = doc.object();
foreach(QJsonValue element, root["CELLS"].toArray()){
QJsonObject node = element.toObject();
node["whatEver"];
}
If you Qt Version > 5.5 check QJSonDocument, http://doc.qt.io/qt-5/qjsondocument.html.
An Example:
// Just read it from a file... or copy here
const QString yourJSON = "...";
const QJsonDocument jsonDoc = QJsonDocument::fromJson(yourJSON.toLocal8Bit());
const QVariantMap map = jsonDoc.toVariant().toMap();
To take care of nesting of CELLS inside CELLS, first load your CELLS array:
// Start read your parameter
const QVariant testCells2 = map["test_cells2"].toVariant();
const QVariantList CELLS = testCells2.toList();
// Now, we have a list available CELLS, check one be one
foreach (const QVariant& cell, CELLS) {
const QVariantMap wrapped = cell.toMap();
qDebug() << "Pretty Name: " << wrapped["prettyName"].toString();
qDebug() << "Type: " << wrapped["type"].toString();
const hasList = !wrapped["CELLS"].toList().isEmpty();
qDebug() << "Has child list? << hasList;
if (hasList) {
// Start another loop to check the child list.
}
}

Using $push operator for update MongoDB document

I have MongoDB document with structure like this:
{
"_id" : ObjectId("56ebce0d1e3c51fe6a5053f3"),
"timestamp" : 1458294285,
"values" : [
{
"offset" : 132,
"packets" : [
{
"type" : "type1",
"data" : "some_data1"
},
{
"type" : "type2",
"data" : "some_data2"
}
]
}
}
I use it for storage time series data.
I want to create the query which insert a packet in packets array.
I use MongoDB c++ legacy driver 1.1.0.
When I will execute twice code below with equal timestamp and offset:
mongo::BSONObj query = BSON("timestamp" << timestamp);
mongo::BSONObj mainObj =
BSON ("$push" << BSON("values" << BSON("offset" << timestampOffset <<
"packets" << BSON_ARRAY(obj))));
connection.update(collectionName, query, mainObj, true);
I have result like this:
{
"_id" : ObjectId("56f90c1a3b20b7c1f61dccf7"),
"timestamp" : 1459080000,
"values" : [
{
"offset" : 68457,
"packets" : [
{
"type" : "type1",
"data" : "some_data1"
}
]
},
{
"offset" : 68457,
"packets" : [
{
"type" : "type2",
"data" : "some_data2"
}
]
}
]
}
Can somebody help me create right query for insert data into the document with specific structure?