How to filter elements by node key in JSONPath? - jsonpath

What I want
Apply a JSONPath to given json response, to match specific elements by comparing their children's node keys with a value.
Input
{
"data": {
"ticket": {
"1": "foo",
"2": "bar",
"3": "baz"
}
}
}
Output (expected)
"3": "baz"
Case description
I want to apply a JSONPath expression, to filter ticket elements with ticket key greater than "2", so in this case it should match only the 3rd "baz" ticket.
ticket keys are only integer numbers in my data
Code area
This matches all node keys aka ticket keys
$.data.ticket.*~
This is a basic example of filtering
$..book[?(#.price<10)] // -> filter all books cheaper than 10
I am trying somehow to combine them in order to achieve the desired result
Where I test it
https://jsonpath.com/
References
https://goessner.net/articles/JsonPath/

It is possible with jsonpath-plus. The site https://jsonpath.com/ uses jsonpath-plus library internally.
It has some convenient additions or elaborations not provided in the original spec of jsonpath.
Use the #property to compare the value of the key.
$.data.ticket[?(#property > 2)]

Related

DynamoDb check that a SS attribute in contained in a given SS

Lets say I have this schema:
source_id -> String, HashKey
created_at -> String, RangeKey
required_capabilities -> StringSet
required_capabilities is a Set of Strings that we need to provide in the query in order to be able to retrieve a particular element.
For example:
If I have this three elements:
{
"source_id": "1",
"created_at": "2021-01-18T10:53:25Z",
"required_capabilities": ["Cap1", "Cap2", "Cap3"]
},
{
"source_id": "1",
"created_at": "2021-01-18T10:59:31Z",
"required_capabilities": ["Cap1", "Cap3"]
},
{
"source_id": "1",
"created_at": "2021-01-18T11:05:15Z"
}
I want to create a query, filtering for example source_id = "1" and providing a FilterExpression with the required_capabilities = ["Cap1", "Cap3", "Cap4"].
And I would expect as a result:
{
"source_id": "1",
"created_at": "2021-01-18T10:59:31Z",
"required_capabilities": ["Cap1", "Cap3"] // Since I've provided "Cap1", "Cap3" and "Cap4"
},
{
"source_id": "1",
"created_at": "2021-01-18T11:05:15Z" // Since it doesn't require any capability.
}
I've tried the IN operator as follows, since the stored StringSet should be IN (or Contained by) the given SS, but it didn't work.
aws dynamodb query --table-name TableName --key-condition-expression "source_id = :id" --filter-expression "required_capabilities IN (:rq)" --expression-attribute-values '{":id": {"S": "1"}, ":rq": { "SS": ["Cap1", "Cap3", "Cap4"] }}'
It works only when I provide the exact same StringSet, but If I provide a set that contains the saved one and also have more values, it doesn't return anything.
it seems your issue is around the use of the IN keyword, which does not work with sets. From the docs on conditionals
IN : Checks for matching elements in a list.
AttributeValueList can contain one or more AttributeValue elements of type String, Number, or Binary. These attributes are compared against an existing attribute of an item. If any elements of the input are equal to the item attribute, the expression evaluates to true.
I believe you want the CONTAINS keyword:
CONTAINS : Checks for a subsequence, or value in a set.
AttributeValueList can contain only one AttributeValue element of type String, Number, or Binary (not a set type). If the target attribute of the comparison is of type String, then the operator checks for a substring match. If the target attribute of the comparison is of type Binary, then the operator looks for a subsequence of the target that matches the input. If the target attribute of the comparison is a set ("SS", "NS", or "BS"), then the operator evaluates to true if it finds an exact match with any member of the set. CONTAINS is supported for lists: When evaluating "a CONTAINS b", "a" can be a list; however, "b" cannot be a set, a map, or a list.
Actually, I found out that dynamodb doesn't support the use case I needed, so I found a workaround.
Basically instead of modelling the required_capabilities as a StringSet, I've created a field called required_capability, containing a single required capability (which is ok so far for me) and using the IN operator to check.
If in the future I need to check for more than one capability, I just need to add new fields required_capability_2 and required_capability_3.
It's clearly not ideal, but I guess it's good enough, considering I won't have a lot of required capabilities in a single record, it's usually one, maybe two.

JSONPath - Filter expression to print a field if an array contains a string

I have the following JSON and am trying to write a JSON Path expression which will return me the isbn number when I have a id of either '123456789' or '987654321'. I tried the following but this did not work. Can anybody tell me what I am doing wrong please. Thanks in advance
JSON Path Expression
$.books[?(#.ids== '123456789' )].isbnNumber
JSON
{
"books": [{
"title": "10",
"isbnNumber": "621197725636",
"ids": [
"123456789",
"987654321"
]
}]
}
The (more traditional) JSONPath implementations that stick close(r) to Goessner's reference specification do not offer handy functions like in which are available in extended implementations like JayWay's JSONPath.
Using Gatling's JSONPath, one thing we could do if the positions of the Ids in question are fixed is accessing their respective indices directly to make the comparison:
$.books[?(#.ids[0] == "123456789" || #.ids[1] == "987654321")].isbnNumber
This will give you the desired result of your example; however, some books only have one of the two indices, or they Id to compare to shows up on a different position it won't work.

Add element to arrays, that are values to a given key name (json transformation with jq)

I'm a jq newbie, and I try to transform a json (a Swagger spec). I want to add an element to the array value of the "parameter" keys:
{
...
"paths": {
"/great/endpoint1": {
"get": {
"parameters": [] <<--- add a value here
}
}
"/great/endpoint2": {
"post": {
"parameters": [] <<-- and here too here too etc.
....
The following jqplay almost works. It adds values to the right arrays, but it has the nasty side effect of also removing the "x-id" value from the root of the input json. It's probably because of a faulty if-condition. As the paths contain a varying string (the endpoint names), I don't know how to write a wildcard path expression to address those, which is why I have tried using walk instead:
https://jqplay.org/s/az56quLZa3
Since the sample data is incomplete, it's difficult to say exactly what you're looking for but it looks like you should be using parameters in the call to walk:
walk(if type=="object" and has("parameters")
then .parameters += [{"extra": "value"}]
else . end)
If you want to restrict the walk to the top-level paths, you would preface the above with: .paths |=

Look for Value in Multiple Keys with JSONPath

With JSONPath, how can you extract a single value from a list of known keys?
For example, I want to write one JSON path expression that can extract Sean from all three of these JSON documents:
{ "firstName": "Sean" }
{ "first_name": "Sean" }
{ "first_name": "Sean", "firstName": "Sean" }
This example is a little contrived, but I have an actual use case that requires this behavior.
The best I can come up with is the expression $.firstName,first_name which will work for #1 and #2 but returns an array for #3 — and I just want the first value that matches.
Basically, I’m looking for a JSONPath extract expression that simulates this JavaScript code:
json.firstName || json.first_name
I believe you want something like below :)
You can get json path using the index .Whn I'm using rest-assured I always use something similar to below code to extract values from my json response .
Response response=given().contentType(ContentType.JSON).get("http://localhost:3000/posts");
JsonPath jsonPathEvaluator = response.jsonPath();
String fn1 = jsonPathEvaluator.get("firstName[0]");
String fn_1=jsonPathEvaluator.get("first_name[0]");
String fn2=jsonPathEvaluator.get("firstName[1]");
You can pass all pair to dict and then extract your values or if you need only values you can use set structure to store keys and separate list to values.

Firebase orderByPriority used with equalTo strange behavior

I try to use the second argument of equalTo(value, key) when using orderByPriority(), as documented here.
The problem is that results are inconsistent when using that second key argument.
Example dataset
items
key1
key2
key3
key4
All items have the same priority: 10 (this is just for the example, in my app there are other items with other priorities)
When issuing the following Firebase query to get the first two items with that priority:
dbRef.child('items').orderByPriority().equalTo(10).limitToFirst(2)
I get the following - expected - results :
{ "key1": ..., "key2": ... }
Then I try to get the results after the key2 item, as explained in the docs:
dbRef.child('items').orderByPriority().equalTo(10, 'key2').limitToFirst(2)
Result is pretty strange, with always only one item, the one with the provided key:
{ "key2": ... }
What i was expecting is two results starting at or after the key provided, so
{ "key2": ..., "key3": ... }
Or
{ "key3": ..., "key4": ... }
Question
How should I use the equalTo() filter with its second argument?
It seem this question was already asked, but it did not get any answers...
The equalTo() filter will only get items exactly matching the key specified. If you'd like to start at 'key2' and get multiple after that, use the startAt() filter. You cannot use multiple orderBy statements in the same query, though, so you may need to reformat your code. I would recommend that you save items by desired priority. Then you can query that group of items using the startAt() filter. Heres a dataset:
items10
key1
key2
items9
key1
key2
In the startAt() filter, you specify the starting key, and then it will query for any key with the same or greater value. Here's a link to the docs for this functionality: startAt().
Using this method, limitToFirst(2) should give you key2 and key3. Here's a code example, using priority 10.
dbRef.child('items10').orderByKey().startAt('key2').limitToFirst(2)
Note: you may also need to just grab an element, without knowing its priority. For this, create another key called items that just contains all keys (you would not use this for queries).

Resources