How to find an item in a json array using kusto - azure-data-explorer

I have an json array logged as
[
{
"Key": "key0",
"Value": 0
},
{
"Key": "key1",
"Value": 2
}
]
How do I get Value for Key with value key0, so 0.
I have been using this kluge.
...
| extend jsonarray = parse_json(...)
| extend value = toint(case(
jsonarray[0].Key == 'key0', jsonarray[0].Value,
jsonarray[1].Key == 'key0', jsonarray[1].Value,
"<out-of-range>"))
Update:
Using mv-apply:
| extend jsonarray = parse_json(...)
| mv-apply jsonarray on (
where jsonarray.Key == 'key0'
| project value = toint(jsonarray.Value)
)

you could use mv-expand or mv-apply:
print d = dynamic([
{
"Key": "key0",
"Value": 0
},
{
"Key": "key1",
"Value": 2
}
])
| mv-apply d on (
where d.Key == "key0"
| project d.Value
)
https://learn.microsoft.com/en-us/azure/kusto/query/mvexpandoperator
https://learn.microsoft.com/en-us/azure/kusto/query/mv-applyoperator

Related

Using jq I want to modify multiple values

I have a json like this:
{
"bla": { "body": {
"mode": "raw",
"raw": "{\n \"accountId\": \"1111\",\n \"monetaryAmount\": {\n \"amount\": 111,\n \"exponent\": 2,\n \"currency\": \"aaa\"\n },\n \"remarks\": \"consequat quis\"\n}",
"options": {
"raw": {
"language": "json"
}
}
}},
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{token}}",
"type": "string"
}
]
},
"segg": {
"key": "txn_id",
"value": "{{token}}",
"type": "string"
},
"slugg": {
"key": "companyId",
"value": "{{token}}",
"type": "string"
},
"blu": [ {
"key": "teamMemberId",
"value": "{{token}}",
"type": "string"
} ]
}
Conditions: There can be anywhere any deep ..body.raw strings, on these I want to do a simple search and replace (Or better: Parse the string as JSon and do some jq on that?).
And any deep objects with .key and .value (strings), in this I just want to replace the .value="{{"+.key+"}}"
Thanks
This is what I could do:
del(.. | objects | .auth)|
(.. | objects | select(.key == "teamMemberId")).value="{{teamMemberId}}"|
(.. | objects | select(.key == "accountId")).value="{{accountId}}"|
(.. | objects | select(.key == "companyId")).value="{{companyId}}"|
(.. | objects | select(.key == "transactionId")).value="{{transactionId}}"|
(.. | objects | select(.key == "txn_id")).value="{{transactionId}}"|
(.. | objects | select(.key == "limitType")).value="MONTHLY"|
(.. | objects | select(.raw | type == "string")).raw=null
In case of ..raw I can not find a way to replace, and in case of .key -> .value I would prefer something like find by regex, and replace as mentioned. So that the whole jq expression is more compact.
One way to process the .body.raw values would be to start with:
(.. | try .body.raw // empty) |= fromjson
You can elaborate fromjson as per your requirements.
One way to update the .value values would be to include the following in your jq pipeline:
walk(if type == "object" and has("key") and has("value")
then .value = "{{\(.key)}}" else . end)

JQ does not contain for "key": "value" JSON structure

The question is similar to THIS, but the JSON structure is different.
In my usecase JSON has an arrays with key:value data:
{
"data":[
{
"name":"banana",
"tags":[
{
"id":"yellow"
},
{
"id":"long"
}
]
},
{
"name":"apple",
"tags":[
{
"id":"red"
},
{
"id":"round"
}
]
},
{
"name":"orange",
"tags":[
{
"id":"orange"
},
{
"id":"round"
},
{
"id":"colored"
}
]
}
]
}
What required is to filter the only elements that do not have certain keyword - "red" for instance.
When i use jq '.data[] | select(.tags[].id | index( "red" ))' it brings me the correct resut of 'apple' (as it has "id": "red") and 'orange' (as it has "id": "colored").
However, when i add the negation jq '.data[] | select(.tags[].id | index( "red" ) | not)' the results are more than strange, with elements' duplication, totally enigmatic.
How can i use jq to filter the result the way it returns only elements that do not have the exact match among the array values?
You can use all to give a condition all items must meet. Here, all values in .tags[].id must be unequal != to "red":
jq '.data[] | select(all(.tags[].id; . != "red"))'
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
Add .name and use the -r option to only get the names:
jq -r '.data[] | select(all(.tags[].id; . != "red")).name'
banana
orange
Demo
index works on an array, your current filter does not pass index to an array, therefore you're getting other results then expected.
.data[] | select([ .tags[].id ] | index("red") | not)
Here we create an array with all the id's [ .tags[].id ] and use that array to check for red: | index("red") | not
The above filter gives the following output:
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
If you want to exclude 'colored', use contains():
.data[] | select([ .tags[].id ] | contains(["red"]) | not)
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
Demo

Conditional append a element to an array

Here is the jq I have, it just wants to build a new element and then append it to an array,
[.[] | . as { foo: $foo1, bar: $bar1} |
{
names: ([
$foo1 | range(0;length) as $i |
{ key: ($foo1[$i]) }
] + [{ key: $bar1 }])
} |
{
values: .names,
}
]
And suppose I have a json like this,
{
"foo":[
"key1",
"key2"
],
"bar": "key3"
}
This will generate a json file like this,
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
},
{
"key": "key3"
}
]
}
]
But the element should be only appended when the $bar is not an empty string, can I do something like this?
[.[] | . as { foo1: $foo1, bar1: $bar1 if $bar != ""}
...
or do it when is appended,
names: ([
$foo1 | range(0;length) as $i |
{ key: ($foo1[$i]) }
] + [{ key: $bar1 }] | if $bar != "")
thanks in advance for any help!
You could just use select to filter out that case
jq '[{values: (.foo + [.bar | select(. != "")]) | map({key:.})}]'
If .bar == "key3", it prints
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
},
{
"key": "key3"
}
]
}
]
If .bar == "", it prints
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
}
]
}
]
If .bar does not exist, it will print
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
},
{
"key": null
}
]
}
]
If in this last case you want to have the same result as with .bar == "", then change . != "" to values != "" in the filter to consider only values that are not null (or to strings != "" to only consider (non-empty) strings and disregard any other type).

JQ: How do I extract item which has part of subitem value of "FOO" [duplicate]

I have a JSON file that looks like this:
{
"InstanceId": "i-9KwoRGF6jbhYdZi823aE4qN",
"Tags": [
{
"Key": "blah",
"Value": "server-blah"
},
{
"Key": "environment",
"Value": "ops"
},
{
"Key": "server_role",
"Value": "appserver"
},
{
"Key": "Name",
"Value": "some_name"
},
{
"Key": "product",
"Value": "some_server"
}
]
}
{
...more objects like the above...
}
I need to display the InstanceId where "Key" == "environment" and "Value" == "ops".
I have jq-1.6.
If I say:
cat source.json | jq '
{ InstanceId, Tags } |
(.Tags[] | select( .Key == "environment" ))
'
I get some of what I want, but I cannot figure out how to include InstanceId in the output nor how to incorporate the "and" part of the select.
Here is a simple but efficient approach using any:
select( any(.Tags[]; .Key=="environment" and .Value == "ops") )
| .InstanceId
An alternative approach that avoids .Tags[]:
{"Key": "environment", "Value": "ops"} as $object
| select( .Tags | index($object) )
| .InstanceId
I'm not sure if this is the exact output you're looking for (comment if it isn't), but this will output the InstanceIds of JSON objects that contain a Tag with Key environment and Value ops.
jq 'select( .Tags[] | (.Key == "environment" and .Value == "ops")) | .InstanceId' < source.json

`jq` selection based on condition of different levels

I'd like to select the following node in a long json file.
The conditions are "locator": "mmc1" and "#name": "section-title". This nested structure makes the jq query very complex. Notice that I have to specify things like "locator" and "section-title" multiple times.
.. | .["$$"]?
| select((.[]? | ."#name" == "section-title"?) and (..[]? | .locator? | test("mmc[0-9]+")?))
| [
(..[]? | select(.locator? | test("mmc[0-9]+")?) | .locator)
, (.[] | select(."#name" == "section-title") | ._)
] | #tsv
Could anybody let me know how to make the query simpler in jq yet make sure its function is exact the same as the original query? Just making the following simplified test input producing the same output should not be considered as an equivalent query. Thanks.
...
"$$": [
{
"#name": "label",
"_": "Appendix A"
},
{
"#name": "section-title",
"$": {
"id": "sectitle0145"
},
"_": "Supplementary data"
},
{
"#name": "para",
"$": {
"id": "p0210",
"view": "all"
},
"$$": [
{
"#name": "__text__",
"_": "The following is the supplementary data related to this article:"
},
{
"#name": "display",
"$$": [
{
"#name": "e-component",
"$": {
"id": "ec1"
},
"$$": [
{
"#name": "link",
"$": {
"locator": "mmc1",
"type": "simple",
"role": "http://data.elsevier.com/vocabulary/ElsevierContentTypes/46.1",
"href": "pii:S2212877817302818/mmc1",
"id": "aep-link-id8"
}
}
]
}
]
}
]
}
]
...
See the output below.
$ jq '.. | .["$$"]? | select((.[]? | ."#name" == "section-title"?) and (..[]? | .locator? | test("mmc[0-9]+")?)) | [ (..[]? | select(.locator? | test("mmc[0-9]+")?) | .locator) , (.[] | select(."#name" == "section-title") | ._) ] | #tsv' < 1.json
"mmc1\tSupplementary data"
$ cat 1.json
{
"$$": [
{
"#name": "label",
"_": "Appendix A"
},
{
"#name": "section-title",
"$": {
"id": "sectitle0145"
},
"_": "Supplementary data"
},
{
"#name": "para",
"$": {
"id": "p0210",
"view": "all"
},
"$$": [
{
"#name": "__text__",
"_": "The following is the supplementary data related to this article:"
},
{
"#name": "display",
"$$": [
{
"#name": "e-component",
"$": {
"id": "ec1"
},
"$$": [
{
"#name": "link",
"$": {
"locator": "mmc1",
"type": "simple",
"role": "http://data.elsevier.com/vocabulary/ElsevierContentTypes/46.1",
"href": "pii:S2212877817302818/mmc1",
"id": "aep-link-id8"
}
}
]
}
]
}
]
}
]
}
If you want to get the "locator" value off of all objects unconditionally, you could use this:
..|objects.locator|strings
To find all objects with #name == "section-title" and select the _ value:
..|select(objects."#name" == "section-title")._
Putting it all together:
[(..|objects.locator|strings), (..|select(objects."#name" == "section-title")._)] | #tsv
https://jqplay.org/s/xHWg8aGSSS
The following is simpler in at least some respects, produces the desired result, and seems to reflect the requirements:
..
| .["$$"]?
| (.. | objects | .locator | strings | select(test("mmc[0-9]+")) ) as $locator
| (.. | objects | select(.["#name"] == "section-title") | ._) as $st
| [$locator, $st]
| #tsv

Resources