繁体   English   中英

使用 mongo c++ 驱动程序查询嵌套的 BSON 文档

[英]Query nested BSON documents with mongo c++ driver

我有一个bsoncxx::document::view bsonObjView和一个std::vector<std::string> path ,表示我们在 BSON 文档中搜索的值的键(第一个键是顶级,第二个键是深度 1 、第三键深度 2 等)。

我正在尝试编写一个给定路径的函数,它将搜索 bson 文档:

bsoncxx::document::element deepFieldAccess(bsoncxx::document::view bsonObj, const std::vector<std::string>& path) {

    assert (!path.empty());

    // for each key, find the corresponding value at the current depth, next keys will search the value (document) we found at the current depth
    for (auto const& currKey: path) {

        // get value for currKey
        bsonObj = bsonObj.find(currKey);

    }

    // for every key in the path we found a value at the appropriate level, we return the final value we found with the final key
    return bsonObj;
}

如何让功能发挥作用? bsonObj应该是什么类型以允许在循环中进行此类搜索? 另外,如何检查是否找到了currKey的值?

另外,是否有一些内置的bsoncxx方法来做到这一点?

这是一个示例 json 文档,后跟一些指向其中的值的路径。 当给定路径时,最终的解决方案应该返回相应的值:

{
  "shopper": {
    "Id": "4973860941232342",
    "Context": {
      "CollapseOrderItems": false,
      "IsTest": false
    }
  },
  "SelfIdentifiersData": {
    "SelfIdentifierData": [
      {
        "SelfIdentifierType": {
          "SelfIdentifierType": "111"
        }
      },
      {
        "SelfIdentifierType": {
          "SelfIdentifierType": "2222"
        }
      }
    ]
  }
}

示例路径:

路径[ shopper -> Id -> targetValue ]指向字符串"4973860941232342"

路径[ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> targetValue ]指向对象{ "SelfIdentifierType": { "SelfIdentifierType": "111" } }

路径[ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> targetValue ]指向对象{ "SelfIdentifierType": "111" }

路径[ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> SelfIdentifierType -> targetValue ]指向字符串"111"

请注意,路径的类型为std::vector<std::string> path 所以最终的解决方案应该返回路径指向的值。 它应该适用于任意深度,也适用于指向数组元素(第二个示例路径)和 THROUGH 数组元素(最后两个示例路径)的路径。 我们假设索引i处的数组元素的键是"i"

更新:目前,@acm 建议的方法对于具有数组索引的路径失败(没有数组索引的路径工作正常)。 这是重现问题的所有代码:

#include <iostream>

#include <bsoncxx/json.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>



std::string turnQueryResultIntoString3(bsoncxx::document::element queryResult) {

    // check if no result for this query was found
    if (!queryResult) {
        return "[NO QUERY RESULT]";
    }

    // hax
    bsoncxx::builder::basic::document basic_builder{};
    basic_builder.append(bsoncxx::builder::basic::kvp("Your Query Result is the following value ", queryResult.get_value()));

    std::string rawResult = bsoncxx::to_json(basic_builder.view());
    std::string frontPartRemoved = rawResult.substr(rawResult.find(":") + 2);
    std::string backPartRemoved = frontPartRemoved.substr(0, frontPartRemoved.size() - 2);

    return backPartRemoved;
}

// TODO this currently fails for paths with array indices
bsoncxx::document::element deepFieldAccess3(bsoncxx::document::view bsonObj, const std::vector<std::string>& path) {

    if (path.empty())
        return {};

    auto keysIter = path.begin();
    const auto keysEnd = path.end();

    std::string currKey = *keysIter;    // for debug purposes
    std::cout << "Current key: " << currKey;

    auto currElement = bsonObj[*(keysIter++)];

    std::string currElementAsString = turnQueryResultIntoString3(currElement);  // for debug purposes
    std::cout << "    Query result for this key: " << currElementAsString << std::endl;


    while (currElement && (keysIter != keysEnd)) {
        currKey = *keysIter;
        std::cout << "Current key: " << currKey;

        currElement = currElement[*(keysIter++)];

        currElementAsString = turnQueryResultIntoString3(currElement);
        std::cout << "    Query result for this key: " << currElementAsString << std::endl;
    }

    return currElement;
}

// execute this function to see that queries with array indices fail
void reproduceIssue() {

    std::string testJson = "{\n"
                           "  \"shopper\": {\n"
                           "    \"Id\": \"4973860941232342\",\n"
                           "    \"Context\": {\n"
                           "      \"CollapseOrderItems\": false,\n"
                           "      \"IsTest\": false\n"
                           "    }\n"
                           "  },\n"
                           "  \"SelfIdentifiersData\": {\n"
                           "    \"SelfIdentifierData\": [\n"
                           "      {\n"
                           "        \"SelfIdentifierType\": {\n"
                           "          \"SelfIdentifierType\": \"111\"\n"
                           "        }\n"
                           "      },\n"
                           "      {\n"
                           "        \"SelfIdentifierType\": {\n"
                           "          \"SelfIdentifierType\": \"2222\"\n"
                           "        }\n"
                           "      }\n"
                           "    ]\n"
                           "  }\n"
                           "}";

    // create bson object
    bsoncxx::document::value bsonObj = bsoncxx::from_json(testJson);
    bsoncxx::document::view bsonObjView = bsonObj.view();

    // example query which contains an array index, this fails. Expected query result is "111"
    std::vector<std::string> currQuery = {"SelfIdentifiersData", "SelfIdentifierData", "0", "SelfIdentifierType", "SelfIdentifierType"};

    // an example query without array indices, this works. Expected query result is "false"
    //std::vector<std::string> currQuery = {"shopper", "Context", "CollapseOrderItems"};

    bsoncxx::document::element queryResult = deepFieldAccess3(bsonObjView, currQuery);

    std::cout << "\n\nGiven query and its result: [ ";
    for (auto i: currQuery)
        std::cout << i << ' ';

    std::cout << "] -> " << turnQueryResultIntoString3(queryResult) << std::endl;
}

没有内置的方法可以做到这一点,因此您需要编写一个类似于上面概述的辅助函数。

我相信您遇到的问题是函数的参数是bsoncxx::document::view ,但view::find的返回值是bsoncxx::document::element 因此,您需要考虑循环中某处的类型更改。

我想我会这样写函数:

bsoncxx::document::element deepFieldAccess(bsoncxx::document::view bsonObj, const std::vector<std::string>& path) {

    if (path.empty())
       return {};

    auto keysIter = path.begin();
    const auto keysEnd = path.end();

    auto currElement = bsonObj[*(keysIter++)];
    while (currElement && (keysIter != keysEnd))
        currElement = currElement[*(keysIter++)];

    return currElement;
}

请注意,如果找不到路径的任何部分,或者如果路径尝试遍历实际上不是 BSON 文档或 BSON 数组的对象,这将返回无效的bsoncxx::document::element

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM