Add support for allowing dot being used inside the rql query key path#224
Conversation
|
This would be great to solve, and requiring dots within names to be percent-encoded is a good idea. However, to implement this, I think we need to treat Consider We want it to mean |
garethsb
left a comment
There was a problem hiding this comment.
Initial suggested test case tweak...
| const auto test_value = web::json::value_of({ | ||
| { U("foo"), web::json::value_of({ | ||
| { U("bar"), web::json::value_of({ | ||
| { U("baz.qux"), U("quux")} |
There was a problem hiding this comment.
| { U("baz.qux"), U("quux")} | |
| { U("baz.qux"), U("quux")}, | |
| { U("baz%2Equx"), U("quuux")} |
| const utility::string_t query_rql = U("matches(foo.bar.baz%2Equx,quux)"); | ||
| const auto rql_query = rql::parse_query(query_rql); | ||
|
|
||
| const auto args = rql_query.at(U("args")); | ||
| const auto key_path = args.as_array().at(0).as_string(); | ||
| web::json::value results; | ||
|
|
||
| BST_REQUIRE_EQUAL(true, web::json::extract(test_value.as_object(), results, key_path)); | ||
| BST_REQUIRE_EQUAL(U("quux"), results.as_string()); |
There was a problem hiding this comment.
| const utility::string_t query_rql = U("matches(foo.bar.baz%2Equx,quux)"); | |
| const auto rql_query = rql::parse_query(query_rql); | |
| const auto args = rql_query.at(U("args")); | |
| const auto key_path = args.as_array().at(0).as_string(); | |
| web::json::value results; | |
| BST_REQUIRE_EQUAL(true, web::json::extract(test_value.as_object(), results, key_path)); | |
| BST_REQUIRE_EQUAL(U("quux"), results.as_string()); | |
| { | |
| const utility::string_t query_rql = U("matches(foo.bar.baz%2Equx,quux)"); | |
| const auto rql_query = rql::parse_query(query_rql); | |
| const auto args = rql_query.at(U("args")); | |
| const auto key_path = args.as_array().at(0).as_string(); | |
| web::json::value results; | |
| BST_REQUIRE_EQUAL(true, web::json::extract(test_value.as_object(), results, key_path)); | |
| BST_REQUIRE_EQUAL(U("quux"), results.as_string()); | |
| } | |
| { | |
| const utility::string_t query_rql = U("matches(foo.bar.baz%252Equx,quux)"); | |
| const auto rql_query = rql::parse_query(query_rql); | |
| const auto args = rql_query.at(U("args")); | |
| const auto key_path = args.as_array().at(0).as_string(); | |
| web::json::value results; | |
| BST_REQUIRE_EQUAL(true, web::json::extract(test_value.as_object(), results, key_path)); | |
| BST_REQUIRE_EQUAL(U("quuux"), results.as_string()); | |
| } |
|
I think an approach to satisfy the requirements could be something like this:
To maintain backward compatibility and flexibility, we would need to think harder about the last two bullet points, to ensure that type look-up and value equality works they way we want, e.g. is Maybe the answer is to pass a |
garethsb
left a comment
There was a problem hiding this comment.
This is heading in the right direction apart from one bug. However it doesn't address the issue I pointed out with value equality.
Consider the request URL:
/x-nmos/query/v1.3/nodes?query.rql=eq(label,f%6F%6F)
Does that match a Node with a label of foo or not?
Similarly what about a query like:
/x-nmos/query/v1.3/flows?query.rql=ge(bit_depth,%31%36)
Does that match audio Flows with bit_depth greater than or equal to 16?
| const utility::string_t::size_type key_last = key_path.find_first_of(_XPLATSTR("."), key_first); | ||
| const utility::string_t key = key_path.substr(key_first, details::count(key_first, key_last)); | ||
| if (utility::string_t::npos != key_last) | ||
| if (key != *(key_path.end() - 1)) |
There was a problem hiding this comment.
That checks that the value of the key isn't equal to the last one in the key_path... but what about paths like foo.bar.foo?
There was a problem hiding this comment.
Well-spotted will fix it
| bool extract(const web::json::object& object, web::json::value& results, const utility::string_t& key_path) | ||
| { | ||
| std::vector<utility::string_t> key_path_; | ||
| boost::algorithm::split(key_path_, key_path, [](utility::char_t c) { return U('.') == c; }); |
There was a problem hiding this comment.
I think we try to avoid U preprocessor macro in the library code? Use _XPLATSTR as below.
Hmm, no, that's not true... can't now remember what our rule was! 😄 Looks odd having U and _XPLATSTR in the same file though!
There was a problem hiding this comment.
Okay, will change to use _XPLATSTR instead of U.
FYI U is already used in the preprocess function.
utility::string_t preprocess(const utility::string_t& value)
{
// regex pattern matches JSON strings, or single or multi-line comments
// only strings are captured
static const utility::regex_t string_or_comment(U(R"-regex-(("[^"\\](?:\.[^"\\])")|(?://[^\r\n]+)|(?:/*[\s\S]?*/))-regex-"));
// format pattern uses the first capture group to copy strings into the output
// having inserted a single space to ensure tokens are not coalesced
return bst::regex_replace(value, string_or_comment, U(" $1"));
}
There was a problem hiding this comment.
FYI
Uis already used in the preprocess function.
You're right. I think the intention may have been that anything we might conceivably have wanted to push back to Microsoft/cpprestsdk should use _XPLATSTR so that the code could be built with U disabled. So that probably means anything in the Development/cpprest (or pplx) directories... However, there are obviously a few files where I failed to follow my own rule already, including the instance you pointed out! Moreover, since cpprestsdk is in maintenance mode, it's not going to happen, even for the least controversial additions. For now, why not just fix the rest of this file so it uses _XPLATSTR consistently, and leave it at that. Thanks. :-)
The not-to-decode is only applied on the key path, in the above examples, they are the |
As discussed on Slack, the proposed implementation is currently to apply the Boolean flag not-to-decode to all but the last argument of each function. This works for many functions which are of the form We now propose to adopt the RQL-specified array-form for "nested" properties (some way down the RQL Rules) to solve the issue instead:
(The example has Since This would mean that the following are equivalent: But property names with dots would be specified by a client like so: Another nail in the coffin for the first suggestion of using |
…he `.` for the key path, e.g. eq((foo,bar),baz) which is the same as eq(foo.bar,baz)
…changes and there are explicit tests for web::json::extract
* for build and dependencies, e.g. sony#197, sony#198, sony#207, sony#211, sony#215, sony#229, sony#230, sony#235, sony#243 * for SDP parser/generator, e.g. sony#201, sony#205, sony#219, sony#241, sony#242, sony#244 * for RQL, e.g. sony#224 * for CI tests, e.g. sony#218, sony#231, sony#239, sony#250
In RQL, the
.character is used to represent the key separator for the key path, use of the.escape sequence which allows.can be used inside the key.e.g. handle RQL on
tags, where the tag names might include.