I need to do this with Oracle 19c.
I have arbitrary json data in a column of a table. I mean, I dont know the names of attributes, not even the deep the data is... Suppose that I can identify the json objects in witch I am interested because all of them have an "id", "type", "text", and "call" attributes, but I dont know if is the root object or is under any other object, even in any array of any other object.
I want to do a query that finds in a json field, if it contains the object with type=t, text=x, and call=c. It seems easy to find if there is an objects that have any of them have type=t, others have text=x, and others have call=c (but not consistently of the same object)
WITH Data AS (
SELECT '1' AS id, '{id:"1",type:"menu",text:"option1",call:"option1()"}' AS json FROM DUAL UNION ALL
-- next Select 2a- .. from dual, all in one line, formated only for better view:
SELECT '2-onlySubElements' AS id,
'{id:"2a",type:"menu",text:"option2",call:"option2()",
subElements:[
{id:"2.1",type:"menu",text:"option2.1",call:"option21()",
subElements:[{id:"2.1.1",type:"menu",text:"option2.1.1",call:"option211()"} ]},
{id:"2.2",type:"menu",text:"option2.2",call:"option22()"}
]
}' AS json FROM DUAL UNION ALL
-- next Select 2b-mix .. from dual, all in one line, formated only for better view:
SELECT '2b-mixOfInnerElements' AS id,
'{id:"2b",type:"menu",text:"option3",call:"option3()",
subElements:[
{id:"2.1",type:"menu",text:"option2.1",call:"option21()",
innerElements:[{id:"2.1.1",type:"menu",text:"option2.1.1",call:"option211()"} ]},
{id:"2.2",type:"menu",text:"option2.2",call:"option22()"}
]
}' AS json FROM DUAL UNION ALL
SELECT '0' AS id, '{id:"0",type:"label",text:"label0"}' AS json FROM DUAL
)
SELECT rownum, JSON_VALUE(json,'$.type'), a.*
FROM Data a
WHERE
-- uncomment one of the following options:
-- json_exists(json, '$?(@..type=="menu" && @..text=="option2" && @..call=="option21()" )'); -- BAD. it says exists, but attributes are not of the same element.
-- json_exists(json, '$.subElements[*]?(@.type=="menu" && @.text=="option2.1" && @.call=="option21()" )'); --NOT GOOD: This finds consistent elements in '2a-onlySubElements' and '2b-mixOfInnerElements'. But it needs the path.
-- json_exists(json, '$.subElements[*].subElements[*]?(@.type=="menu" && @.text=="option2.1.1" && @.call=="option211()" )'); --NOT GOOD: This finds one consistent element in '2a-onlySubElements'. But it needs the path
-- json_exists(json, '$.subElements[*]?(@.type=="menu" && @.text=="option2.1.1" && @.call=="option211()" )'); --BAD: This finds nothing (the path is not good)
How can I find a consistent object independently of where the object is inside of the root, or even if the object is the root?
- Clarification 1: As I have arbitrary data (i have only fixed id, and type) i dont know the exact attributes that the object have.
I am looking for something similar to:
json_exists(json, '$?(@..type=="menu" && @..text=="option2" && @..call=="option21()" )')
but, that doesn't need the root $ in the expression, and works in a consistent way, something like (not real notation):
json_exists(json, '?@1.type=="order" && @1.detail.text=="optX" && @2.type=="car" && (( @2.wheel[x].model="abc" && @2.wheel[x].size>215) && @2.wheel[y].model="xyz") || @2.engine.power>100)
that will be true if an object has at least one order and one car (in any place, any deep) with the desired values but @1 and @2 were consistent themselves (one wheel is abc and >215, and another is xyz)
(end of clarification 1.)
Thank you.
(if this is not possible with oracle19, it could be possible with oracle21? Only to know, but I cant change oracle version)