简体   繁体   English

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

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

I have a bsoncxx::document::view bsonObjView and a std::vector<std::string> path that represents keys to the value we are searching for in the BSON document (first key is top level, second key is depth 1, third key depth 2, etc).我有一个bsoncxx::document::view bsonObjView和一个std::vector<std::string> path ,表示我们在 BSON 文档中搜索的值的键(第一个键是顶级,第二个键是深度 1 、第三键深度 2 等)。

I'm trying to write a function that given a path will search the bson document:我正在尝试编写一个给定路径的函数,它将搜索 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;
}

How to make the function work?如何让功能发挥作用? What type should bsonObj be to allow for such searches within a loop? bsonObj应该是什么类型以允许在循环中进行此类搜索? Also, how to check if a value for currKey has been found?另外,如何检查是否找到了currKey的值?

Also, is there some bsoncxx built in way to do this?另外,是否有一些内置的bsoncxx方法来做到这一点?

Here is an example json document followed by some paths that point to values inside of it.这是一个示例 json 文档,后跟一些指向其中的值的路径。 The final solution should return the corresponding value when given the path:当给定路径时,最终的解决方案应该返回相应的值:

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

Example paths:示例路径:

The path [ shopper -> Id -> targetValue ] points to the string "4973860941232342" .路径[ shopper -> Id -> targetValue ]指向字符串"4973860941232342"

The path [ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> targetValue ] points to the object { "SelfIdentifierType": { "SelfIdentifierType": "111" } } .路径[ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> targetValue ]指向对象{ "SelfIdentifierType": { "SelfIdentifierType": "111" } }

The path [ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> targetValue ] points to the object { "SelfIdentifierType": "111" } .路径[ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> targetValue ]指向对象{ "SelfIdentifierType": "111" }

The path [ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> SelfIdentifierType -> targetValue ] points to the string "111" .路径[ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> SelfIdentifierType -> targetValue ]指向字符串"111"

Note that the paths are of the type std::vector<std::string> path .请注意,路径的类型为std::vector<std::string> path So the final solution should return the value that the path points to.所以最终的解决方案应该返回路径指向的值。 It should work for arbitrary depths, and also for paths that point TO array elements (second example path) and THROUGH array elements (last 2 example paths).它应该适用于任意深度,也适用于指向数组元素(第二个示例路径)和 THROUGH 数组元素(最后两个示例路径)的路径。 We assume that the key for an array element at index i is "i" .我们假设索引i处的数组元素的键是"i"

Update: Currently, the approach suggested by @acm fails for paths with array indices (paths without array indices work fine).更新:目前,@acm 建议的方法对于具有数组索引的路径失败(没有数组索引的路径工作正常)。 Here is all the code to reproduce the issue:这是重现问题的所有代码:

#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;
}

There is not a built-in way to to do this, so you will need to write a helper function like the one you outline above.没有内置的方法可以做到这一点,因此您需要编写一个类似于上面概述的辅助函数。

I believe the issue you are encountering is that the argument to the function is a bsoncxx::document::view , but the return value of view::find is a bsoncxx::document::element .我相信您遇到的问题是函数的参数是bsoncxx::document::view ,但view::find的返回值是bsoncxx::document::element So you need to account for the change of type somewhere in the loop.因此,您需要考虑循环中某处的类型更改。

I think I would write the function this way:我想我会这样写函数:

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;
}

Note that this will return an invalid bsoncxx::document::element if any part of the path is not found, or if the path attempts to traverse into an object that is not a actually a BSON document or BSON array.请注意,如果找不到路径的任何部分,或者如果路径尝试遍历实际上不是 BSON 文档或 BSON 数组的对象,这将返回无效的bsoncxx::document::element

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

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