How can i insert object which got arrays into arrays using jq? - json

I may not discribed it properly, I tried many times and looked up the manaual on jq Manaual , and i got no idea how to insert object which contains array into a json file by jq command, anyway, here is the origin test.json:
[
{
"gate": [
{
"pro1": "1"
}
],
"home": [
{
"mem1": "1"
}
],
"holder": "1"
}
]
And i wish to be like this after insert:
[
{
"gate": [
{
"pro1": "1"
}
],
"home": [
{
"mem1": "1"
}
],
"holder": "1"
},
{
"gate": [
{
"pro1": "2"
}
],
"home": [
{
"mem1": "2"
}
],
"holder": "1"
}
]
Could it possibly done by jq?

Since you haven't provided more detail I am going to assume that you simply want to append a hard-coded object to an existing array. (If that's not what you mean you'll need to be more precise in your question.)
You can add items to the end of a list with the + operator. So in your case:
jq '.+[{"gate":[{"pro1": "2"}], "home":[{"mem1": "2"}],"holder": "1"}]' input.json
[
{
"gate": [
{
"pro1": "1"
}
],
"home": [
{
"mem1": "1"
}
],
"holder": "1"
},
{
"gate": [
{
"pro1": "2"
}
],
"home": [
{
"mem1": "2"
}
],
"holder": "1"
}
]
The . takes the existing input, which is an array. The +[ {...} ] concatenates it with another array of one object. If you want to put the new item(s) at the start instead of the end, swap it round: [ {...} ]+.

Related

jmespath :select json object element based on other (array) element in the object

I have this JSON
{
"srv_config": [{
"name": "db1",
"servers": ["srv1", "srv2"],
"prop": [{"source":"aa"},"destination":"bb"},{"source":"cc"},"destination":"cc"},]
}, {
"name": "db2",
"servers": ["srv2", "srv2"],
"prop": [{"source":"dd"},"destination":"dd"},{"source":"ee"},"destination":"ee"},]
}
]
}
I try to build a JMESPath expression to select the prop application in each object in the main array, but based on the existence of a string in the servers element.
To select all props, I can do:
*.props [*]
But how do I add condition that says "select only if srv1 is in servers list"?
You can use the contains function in order to filter based on a array containing something.
Given the query:
*[?contains(servers, `srv1`)].prop | [][]
This gives us:
[
{
"source": "aa",
"destination": "bb"
},
{
"source": "cc",
"destination": "cc"
}
]
Please mind that I am also using a bit of flattening here.
All this run towards a corrected version of you JSON:
{
"srv_config":[
{
"name":"db1",
"servers":[
"srv1",
"srv2"
],
"prop":[
{
"source":"aa",
"destination":"bb"
},
{
"source":"cc",
"destination":"cc"
}
]
},
{
"name":"db2",
"servers":[
"srv2",
"srv2"
],
"prop":[
{
"source":"dd",
"destination":"dd"
},
{
"source":"ee",
"destination":"ee"
}
]
}
]
}

jq update json document to alter an array element

I know this has to be simple, but for some reason it's eluding me how to find an element given a condition and modify one of its fields. The doc should be fully output (sed style) with the edit made.
{
"state": "wait",
"steps": {
"step1": [
{ "name":"Foo", "state":"wait" },
{ "name":"Bar", "state":"wait" }
],
"step2": [
{ "name":"Foo", "state":"wait" },
{ "name":"Zoinks", "state":"ready" }
],
"step3": [
{ "name":"Foo", "state":"cancel" }
]
}
}
I'm expecting something like this should be workable.
jq '. | (select(.steps[][].name=="Foo" and .steps[][].state=="wait") |= . + {.state:"Ready"}'
or
jq '. | (select(.steps[][]) | if (.name=="Foo" and .state=="wait") then (.state="Ready") else . end)
The desired output, of course, would be
{
"state": "wait",
"steps": {
"step1": [
{ "name":"Foo", "state":"ready" },
{ "name":"Bar", "state":"wait" }
],
"step2": [
{ "name":"Foo", "state":"ready" },
{ "name":"Zoinks", "state":"ready" }
],
"step3": [
{ "name":"Foo", "state":"cancel" }
]
}
}
Instead, when I'm not getting cryptic errors, I'm either modifying a top-level field in the document or modifying the field for all the elements or repeated the entire doc multiple times.
Any insights greatly appreciated.
Thanks.
p.s. is there a better syntax than [] to wildcard the named-elements under steps? Or after the pipe to identify the indices discovered by the select?
Pipe the output of .steps[][] into a select call that chooses the objects with the desired name and state values, then set the state value on the result.
$ jq '(.steps[][] | select(.name == "Foo" and .state == "wait")).state = "ready"' tmp.json
{
"state": "wait",
"steps": {
"step1": [
{
"name": "Foo",
"state": "ready"
},
{
"name": "Bar",
"state": "wait"
}
],
"step2": [
{
"name": "Foo",
"state": "ready"
},
{
"name": "Zoinks",
"state": "ready"
}
],
"step3": [
{
"name": "Foo",
"state": "cancel"
}
]
}
}
You can help confirm this using diff (the first jq just normalizes the formatting so that only the changes made by the second one show up in the diff):
$ diff <(jq . tmp.json) <(jq '...' tmp.json)
7c7
< "state": "wait"
---
> "state": "ready"
17c17
< "state": "wait"
---
> "state": "ready"

Mongodb query on triple nested array of object

I'm having some problem to write a query to return a triple nested value from a document. The documents I'm using are structured like this
{
"areaname": "name1",
"places": [
{
"placename": "place1",
"objects": [
{
"objname": "obj1",
"tags": [
"tag1",
"tag2"
]
},
{
"objname": "obj2",
"tags": [
"tag6",
"tag7"
]
}
]
},
{
"placename": "place2",
"objects": [
{
"objname": "obj45",
"tags": [
"tag46",
"tag34"
]
},
{
"objname": "obj77",
"tags": [
"tag56",
"tag11"
]
}
]
}
]
}
It is quite simple actually but I can't find a solution to a simple query like:
"return the objname of the object that contains tag1 inside their tag"
So for the give document if I use "tag1" as a parameter it is expected for the query to return "obj1"
It should give me the same result if I use "tag2" as a parameter
Other example: using "tag56" it should return only "obj77"
Right now i have no problem returning the whole document using the dot-notation or top level field such as areaname or others
db.users.find( {"places.objects.tags":"tag1"}, { areaname: 1, _id:0 } )
Is this even possible?
Keeping it simple:
[
{
"$match" : {
"places.objects.tags" : "tag1"
}
},
{
"$unwind" : "$places"
},
{
"$unwind" : "$places.objects"
},
{
"$match" : {
"places.objects.tags" : "tag1"
}
},
{
"$group" : {
"_id" : "$_id",
"obj_names" : {
"$push" : "$places.objects.objname"
}
}
}
],
You should add any other fields you want to keep to the group stage,
this can also be done without the double $unwind stage but i choose this for read-ability.

jmespath flatten multiple hash values

Ideally, I want to write a query that returns a flat list output: ["abc", "bcd", "cde", "def"] from the following JSON sample:
{
"l_l": [
[1,2,3],
[4,5,6]
],
"l_h_l": [
{ "n": [10,2,3] },
{ "n": [4,5,60] }
],
"l_h_m": [
{
"n": {
"1234": "abc",
"2345": "bcd"
}
}, {
"n": {
"3456": "cde",
"4567": "def"
}
}
]
}
The closest I can get is l_h_m[].n.* which returns the contents that I want as an unflattened list of lists:
[
[
"abc",
"bcd"
],
[
"cde",
"def"
]
]
jmespath lets you flatten lists of lists. Queries l_l[] and l_h_l[].n[] both returned flattened results, when the source json is structured that way.
Looks like your solution just required another flattening operator.
l_h_m[].n.*[]
returns
[
"abc",
"bcd",
"cde",
"def"
]

Create one JSON from another, with extra data va JQ

I have this file:
[
"smoke-tests",
"push-apps-manager"
]
I'd like to get this output using JQ:
{
"errands": [
{"name": "smoke-tests", "post_deploy": true},
{"name": "push-apps-manager", "post_deploy": true}
]
}
It seems so simple, yet, I have so much difficulty here...
It's a little tricky, since you need to embed the input into the list bound to the errands key. Start by creating the sequence of name/post_deploy objects:
% jq '.[] | {name: ., post_deploy: true}' names.json
{
"name": "smoke-tests",
"post_deploy": true
}
{
"name": "push-apps-manager",
"post_deploy": true
}
Then wrap that in the list in the outer object:
% jq '{errands: [.[] | {name: ., post_deploy: true}]}' names.json
{
"errands": [
{
"name": "smoke-tests",
"post_deploy": true
},
{
"name": "push-apps-manager",
"post_deploy": true
}
]
}
You can also use the map function (which I rarely remember how to use correctly, but it turns out is pretty simple here):
% jq '{errands: map({name:., post_deploy: true})}' names.json
Here is another approach. If you are new to jq it may be easiest to work towards the goal in small steps.
1) Start with the identity filter
.
which produces as expected
[
"smoke-tests",
"push-apps-manager"
]
2) next add the outer object with the "errands" key:
{ "errands": . }
which produces
{
"errands": [
"smoke-tests",
"push-apps-manager"
]
}
3) next move the data into an array
{ "errands": [ . ] }
which produces
{
"errands": [
[
"smoke-tests",
"push-apps-manager"
]
]
}
4) add the inner object with the "name" and "post_deploy" keys
{ "errands": [ { "name": ., "post_deploy": true } ] }
which produces
{
"errands": [
{
"name": [
"smoke-tests",
"push-apps-manager"
],
"post_deploy": true
}
]
}
5) Now we're really close. All we need to do is take advantage of jq's Object Construction behavior when an expression produces multiple results :
{ "errands": [ { "name": .[], "post_deploy": true } ] }
which gives us the desired result
{
"errands": [
{
"name": "smoke-tests",
"post_deploy": true
},
{
"name": "push-apps-manager",
"post_deploy": true
}
]
}