How to use apex_json.get_count in a nested array - oracle

I am trying to loop through the array from the count of the benefits element in my json. I keep getting numeric value error although get count function in apex json returns a number.
If we change the loop to for 1 in 1..2 - it works fine.
If we hardcode the array index to 1 then the code works but if we try to pass a variable using %d p0 then i get a numeric value error - ORA - 06502.
Below is the code:
j apex_json.t_values;
l_count_chg number;
l_count_pers number;
l_count_ben number;
l_members wwv_flow_t_varchar2;
l_paths apex_t_varchar2;
l_paths2 apex_t_varchar2;
l_paths3 apex_t_varchar2;
v_get_person varchar2(10);
v_get_benefit varchar2(10);
v_get_benopt varchar2(10);
r_count number;
begin
apex_json.parse(j,'{
"PolicyUpdate": {
"contractNumber": 12345,
"effectiveDate": "2022-04-01",
"planChanges": [
{
"changeCode": 1,
"roleplayerId": "pers1",
"person": {
"id": "pers1",
"surname": "Hazy",
"firstName": "Smith",
"benefits": [
{
"Id": "ben1",
"typeCode": "C1",
"benefitName": "Funeral Benefit",
"roleplayerId": "pers1",
"coverAmount": 10000,
"premiumAmount": 47.47,
"StartDate": "2021-04-01",
"Options": {
"Option": [
{
"Label": "Risk Benefit Classification",
"Value": "Main Life"
},
{
"Label": "Waiting Period (DOC to Event)",
"Value": "6 Months"
},
{
"Label": "Paid Up Benefit (Y/N)",
"Value": "No"
}
]
}
},
{
"Id": "ben2",
"typeCode": "C1",
"benefitName": "Tombstone Benefit",
"roleplayerId": "pers1",
"coverAmount": 5000,
"premiumAmount": 10.47,
"StartDate": "2021-04-01"
}
]
}
},
{
"changeCode": 2,
"roleplayerId": "pers2"
}
]
}
}');
dbms_output.put_line('Contract Number : ' ||
apex_json.get_varchar2(p_path => 'PolicyUpdate.contractNumber'));
dbms_output.put_line('Effective Adte : ' ||
apex_json.get_varchar2(p_path => 'PolicyUpdate.effectiveDate'));
dbms_output.put_line('----------------------------------------');
l_count_chg := apex_json.get_count(p_path => 'PolicyUpdate.planChanges',p_values=>j);
dbms_output.put_line('change count '||l_count_chg);
for i in 1..l_count_chg loop
dbms_output.put_line('changeCode '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].changeCode' ,p_values=>j,p0 => i));
dbms_output.put_line('roleplayerId '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].roleplayerId',p_values=>j,p0 => i));
dbms_output.put_line('firstname '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].person.firstName',p_values=>j,p0 => i));
l_count_ben := apex_json.get_count(p_path => 'PolicyUpdate.planChanges[%d].person.benefits',p_values=>j);
dbms_output.put_line('benefit count '|| l_count_ben);
for x in 1..l_count_ben loop
dbms_output.put_line('ben id '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].person.benefits[%d].Id',p_values=>j,p0 => i,p1 => x));
end loop;
end loop;
end;

For me the count didn't return a number (just no value) so I made some changes - I found it safest to check if the path exists before getting the count:
declare
j apex_json.t_values;
l_count_chg number;
l_count_pers number;
l_count_ben number;
l_members wwv_flow_t_varchar2;
l_paths apex_t_varchar2;
l_paths2 apex_t_varchar2;
l_paths3 apex_t_varchar2;
v_get_person varchar2(10);
v_get_benefit varchar2(10);
v_get_benopt varchar2(10);
r_count number;
begin
apex_json.parse(j,'{
"PolicyUpdate": {
"contractNumber": 12345,
"effectiveDate": "2022-04-01",
"planChanges": [
{
"changeCode": 1,
"roleplayerId": "pers1",
"person": {
"id": "pers1",
"surname": "Hazy",
"firstName": "Smith",
"benefits": [
{
"Id": "ben1",
"typeCode": "C1",
"benefitName": "Funeral Benefit",
"roleplayerId": "pers1",
"coverAmount": 10000,
"premiumAmount": 47.47,
"StartDate": "2021-04-01",
"Options": {
"Option": [
{
"Label": "Risk Benefit Classification",
"Value": "Main Life"
},
{
"Label": "Waiting Period (DOC to Event)",
"Value": "6 Months"
},
{
"Label": "Paid Up Benefit (Y/N)",
"Value": "No"
}
]
}
},
{
"Id": "ben2",
"typeCode": "C1",
"benefitName": "Tombstone Benefit",
"roleplayerId": "pers1",
"coverAmount": 5000,
"premiumAmount": 10.47,
"StartDate": "2021-04-01"
}
]
}
},
{
"changeCode": 2,
"roleplayerId": "pers2"
}
]
}
}');
dbms_output.put_line('Contract Number : ' ||
apex_json.get_varchar2(p_path => 'PolicyUpdate.contractNumber',p_values => j));
dbms_output.put_line('Effective Adte : ' ||
apex_json.get_varchar2(p_path => 'PolicyUpdate.effectiveDate',p_values => j));
dbms_output.put_line('----------------------------------------');
l_count_chg := apex_json.get_count(p_path => 'PolicyUpdate.planChanges',p_values=>j);
dbms_output.put_line('change count '||l_count_chg);
for i in 1..l_count_chg loop
dbms_output.put_line('changeCode '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].changeCode' ,p_values=>j,p0 => i));
dbms_output.put_line('roleplayerId '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].roleplayerId',p_values=>j,p0 => i));
dbms_output.put_line('firstname '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].person.firstName',p_values=>j,p0 => i));
IF apex_json.does_exist(p_path => 'PolicyUpdate.planChanges[%d].person.benefits',p0 => i,p_values=>j) THEN
l_count_ben := apex_json.get_count(p_path => 'PolicyUpdate.planChanges[%d].person.benefits',p0 => i,p_values=>j);
dbms_output.put_line('benefit count '|| l_count_ben);
for x in 1..l_count_ben loop
dbms_output.put_line('ben id '||apex_json.get_varchar2(p_path => 'PolicyUpdate.planChanges[%d].person.benefits[%d].Id',p_values=>j,p0 => i,p1 => x));
end loop;
END IF;
end loop;
end;

Koen's suggestion of testing with EXISTS is excellent.
He also added this, but didn't spell out that your original version seemed to be missing the parameter:
p0 => i
when calculating l_count_ben
So the get_count value would have been unknown.

Related

Parse JSON array APEX_JSON in PL SQL

I have the following JSON object -
{
"items": [
{
"tableName": "contacts",
"count": 1,
"columnNames": [
"id"
],
"rows": [
[
"45"
]
]
}
],
"links": [
{
"rel": "self",
},
{
"rel": "describedby",
}
]
}
I am trying to extract the value from rows- I need the value 45.
I tried to extract using -
row_id := apex_json.get_number ('rows[%d]', 1);
How I can extract it?
Thanks in advance.
Here's an example:
declare
l_json_val apex_json.t_values;
l_clob clob;
l_row_val varchar2(255);
begin
l_clob := q'-
{
"items": [
{
"tableName": "contacts",
"count": 1,
"columnNames": [
"id"
],
"rows": [
[
"45"
]
]
}
],
"links": [
{
"rel": "self",
},
{
"rel": "describedby",
}
]
}
-';
apex_json.parse(p_values => l_json_val, p_source => l_clob, p_strict => false);
l_row_val := apex_json.get_varchar2(p_path => 'items[1].rows[1][1]', p_values => l_json_val);
dbms_output.put_line(l_row_val);
end;
Note that the path expressions with apex_json use a 1 base array index, not 0.

APEX_JSON get array object

declare
l_json_doc VARCHAR2(32767);
l_numcols number;
l_numrows number;
begin
l_json_doc := '{
"table": {
"name": "sometablename",
"numofcolumns": 5,
"numofrows": 5,
"colheadings": [{
"colname": "customcol1",
"coltype": "number"
},
{
"colname": "customcol2",
"coltype": "varchar2"
},
{
"colname": "customcol3",
"coltype": "varchar2"
},
{
"colname": "customcol4",
"coltype": "varchar2"
},
{
"colname": "customcol5",
"coltype": "number"
}
],
"data": [{
"customcol1": "datacolumn1",
"customcol2": "datacolumn2",
"customcol3": "datacolumn3",
"customcol4": "datacolumn4",
"customcol5": "datacolumn5"
},
{
"customcol1": "2datacolumn1",
"customcol2": "2datacolumn2",
"customcol3": "2datacolumn3",
"customcol4": "2datacolumn4",
"customcol5": "2datacolumn5"
},
{
"customcol1": "3datacolumn1",
"customcol2": "3datacolumn2",
"customcol3": "3datacolumn3",
"customcol4": "3datacolumn4",
"customcol5": "3datacolumn5"
},
{
"customcol1": "4datacolumn1",
"customcol2": "4datacolumn2",
"customcol3": "4datacolumn3",
"customcol4": "4datacolumn4",
"customcol5": "4datacolumn5"
}
]
}
}';
APEX_JSON.parse(l_json_doc);
l_numcols := APEX_JSON.get_count(p_path => 'table.colheadings');
l_numrows := APEX_JSON.get_count(p_path => 'table.data');
FOR i IN 1 .. l_numrows LOOP
FOR j IN 1 .. l_numcols LOOP
dbms_output.put_line('TEST ' || APEX_JSON.get_varchar2(p_path => 'table.data[%d]') );
END LOOP;
END LOOP;
end;
This is my code which is supposed to extract the data array objects. I expect the following output:
TEST {"customcol1": "datacolumn1","customcol2": "datacolumn2","customcol3": "datacolumn3","customcol4": "datacolumn4","customcol5": "datacolumn5"}
TEST { "customcol1": "2datacolumn1","customcol2": "2datacolumn2","customcol3": "2datacolumn3","customcol4": "2datacolumn4","customcol5": "2datacolumn5"}
etc ...
But when I try to get the json object from the data array using the APEX_JSON.get_varchar2 it returns empty
Two problems with the code there
You have not provided an index to the get_varchar2 function. So the path is not complete. You need to provide a value in the p0 parameter of that function
That's not how APEX_JSON.get_varchar2 works. You are expecting the
function to return the entire JSON object in the data array but get_varchar2 cannot do that. It can only get you the VALUE that is a VARCHAR at a specified path. It cannot get you the entire object.
For your output
DECLARE
l_json_doc VARCHAR2 (32767);
l_numcols NUMBER;
l_numrows NUMBER;
v_colname VARCHAR2 (32767);
BEGIN
l_json_doc := '{
"table": {
"name": "sometablename",
"numofcolumns": 5,
"numofrows": 5,
"colheadings": [{
"colname": "customcol1",
"coltype": "number"
},
{
"colname": "customcol2",
"coltype": "varchar2"
},
{
"colname": "customcol3",
"coltype": "varchar2"
},
{
"colname": "customcol4",
"coltype": "varchar2"
},
{
"colname": "customcol5",
"coltype": "number"
}
],
"data": [{
"customcol1": "datacolumn1",
"customcol2": "datacolumn2",
"customcol3": "datacolumn3",
"customcol4": "datacolumn4",
"customcol5": "datacolumn5"
},
{
"customcol1": "2datacolumn1",
"customcol2": "2datacolumn2",
"customcol3": "2datacolumn3",
"customcol4": "2datacolumn4",
"customcol5": "2datacolumn5"
},
{
"customcol1": "3datacolumn1",
"customcol2": "3datacolumn2",
"customcol3": "3datacolumn3",
"customcol4": "3datacolumn4",
"customcol5": "3datacolumn5"
},
{
"customcol1": "4datacolumn1",
"customcol2": "4datacolumn2",
"customcol3": "4datacolumn3",
"customcol4": "4datacolumn4",
"customcol5": "4datacolumn5"
}
]
}
}';
APEX_JSON.parse (l_json_doc);
l_numcols := APEX_JSON.get_count (p_path => 'table.colheadings');
l_numrows := APEX_JSON.get_count (p_path => 'table.data');
FOR i IN 1 .. l_numrows
LOOP
DBMS_OUTPUT.put ('TEST {');
FOR j IN 1 .. l_numcols
LOOP
v_colname :=
apex_json.get_varchar2 ('table.colheadings[%d].colname', j);
DBMS_OUTPUT.put (
'"' || v_colname || '":"'
|| APEX_JSON.get_varchar2 (
p_path => 'table.data[%d].' || v_colname,
p0 => i)
|| '",');
END LOOP;
DBMS_OUTPUT.put_line ('}');
END LOOP;
END;
Here is the output of the code:
TEST {"customcol1":"datacolumn1","customcol2":"datacolumn2","customcol3":"datacolumn3","customcol4":"datacolumn4","customcol5":"datacolumn5",}
TEST {"customcol1":"2datacolumn1","customcol2":"2datacolumn2","customcol3":"2datacolumn3","customcol4":"2datacolumn4","customcol5":"2datacolumn5",}
TEST {"customcol1":"3datacolumn1","customcol2":"3datacolumn2","customcol3":"3datacolumn3","customcol4":"3datacolumn4","customcol5":"3datacolumn5",}
TEST {"customcol1":"4datacolumn1","customcol2":"4datacolumn2","customcol3":"4datacolumn3","customcol4":"4datacolumn4","customcol5":"4datacolumn5",}
NOTE: I didn't care to remove the final comma after the last key:value pair in each row. If you want that then you have to store all the key value pairs in a variable and RTRIM the comma out.

Filter on aggregated bucket keys?

Given data model structure like this,
{
Id: 123,
"string_facet": [
{
"name": "make",
"value": "Audi"
},
{
"name": "carListType",
"value": "PERSON EU"
},
{
"name": "modelType",
"value": ""
},
{
"name": "engineBrand",
"value": "APT"
},
{
"name": "typeDescription",
"value": "8D2"
}
],
"number_facet": [
{
"name": "typeNumber",
"value": 4614
},
{
"name": "serialNumber",
"value": 2
},
{
"name": "engineSize",
"value": 18
},
{
"name": "horsePower",
"value": 125
},
{
"name": "kw",
"value": 92
},
{
"name": "engineVolume",
"value": 1781
},
{
"name": "listType",
"value": 0
}
],
"dateTime_facet": [
{
"name": "fromDate",
"value": "1999-04-01T00:00:00"
},
{
"name": "toDate",
"value": "2000-10-01T00:00:00"
}
]
}
I want to get aggregates facet names, and values per name. However, I'm only interested in facets that have specific names, such as: make and engineBrand. Note that facets are of type nested.
I have tried the following .NEST expression, but it still returns all of the facet names.
.Global("global", g => g
.Aggregations(ag => ag
.Filter("global_makes", f => f
.Filter(ff => ff
.Nested(n => n
.Path("string_facet")
.Filter(pf => pf.Term("string_facet.name", "make")))
)
.Aggregations(agg => agg
.Nested("nested_string_facet", nested => nested
.Path("string_facet")
.Aggregations(stringFacet => stringFacet
.Terms("name", nameAgg => nameAgg.Field("string_facet.name").Size(0)
.Aggregations(nameAggNext => nameAggNext
.Terms("value", valueAgg => valueAgg.Field("string_facet.value").Size(0))
)
)
)
)
)
)
)
)
);
I have a filter within global (to lose scope of a passed in query), and then filter only on string_facet.name which match "make", but results still include all other names as well. How do I filter out aggregation to include only buckets where name is "make"?
This helped. https://github.com/elastic/elasticsearch/issues/4449
Essentially had to move filter part deeper into aggregation.

Update single value in sub sub array in RethinkDB

We are trying to update a single answer in our sub sub array.
However our query is causing the following error:
{
"deleted": 0 ,
"errors": 1 ,
"first_error": "Inserted value must be an OBJECT (got ARRAY):
[
{
"answers": [
{
"answer": "wassup",
"owner": 12201836
}
],
"question": "Vraag 1?",
"questionId": 0,
"time": "10"
},
{
"answers": [],
"question": "Vraag 2?",
"questionId": 1,
"time": "15"
},
{
"answers": [],
"question": "Vraga 3?",
"questionId": 2,
"time": "20"
}
]" ,
"inserted": 0 ,
"replaced": 0 ,
"skipped": 0 ,
"unchanged": 0
}
Our table structure looks like the following:
Youtube
- Id
- Course
- Unit
- Session
- Number
- Group
- Questions (array)
- Question Id
- Time
- Answers (array)
- Id
- Answer
- Owner
Our query:
r.db('GitSmurf')
.table('youtube')
.update(function (row) {
return row('questions').merge(function (q) {
return r.branch(q('questionId').eq(0), { "answers": q('answers').merge(function(answer) {
return r.branch(answer('owner').eq(12201836), {"answer": "wassup"}, {})} )},{})
})
})
Test content:
{
"completed": [ ],
"course": "swd" ,
"group": "dwa-group-b" ,
"id": "44443377-ed15-4358-a005-f561e7b6a42d" ,
"number": 1 ,
"session": 1 ,
"unit": 1,
"questions": [
{
"answers": [
{
"answer": "hallo" ,
"owner": 12201836
}
] ,
"question": "Vraag 1?" ,
"questionId": 0 ,
"time": "10"
} ,
{
"answers": [ ],
"question": "Vraag 2?" ,
"questionId": 1 ,
"time": "15"
} ,
{
"answers": [ ],
"question": "Vraga 3?" ,
"questionId": 2 ,
"time": "20"
}
] ,
}
Any help is greatly appreciated!
We forgot to return a new object in the update query.
When we added that it worked.
r.db('GitSmurf')
.table('youtube')
.update(function (row) {
return { questions: row('questions').merge(function (q) {
return r.branch(q('questionId'), { "answers": q('answers').merge(function(answer) {
return r.branch(answer('owner').eq(12201836), {"answer": "tom"}, {})
})},{})
})}
})

Rethinkdb: Calculate tag occurrence per user

My table contains documents that look like this:
[{ user: {
key: '100'
},
product: {
name: 'Product 1',
tags: [ 'tag1', 'tag2' ],
}
}, { user: {
key: '100'
},
product: {
name: 'Product 1',
tags: [ 'tag1', 'tag3' ],
}
}, ...]
I would like to create a query which would
groupe documents by the user.key field (1 document per user on result),
the product.tags would be an object (instead of array) with tag occurrences count for each tag.
Result example:
[ { user: {
key: '100'
},
product: {
name: 'Product 1',
tags: {
tag1: 2, // tag1 found 2x for user.key=100
tag2: 1, // tag2 found 1x for user.key=100
tag3: 1
}
}
}, ...]
I think I could do this by mapping and reducing but I have problems - I'm using rethinkdb for the first time.
Here's a way to do it:
// Group by user key
r.table('30400911').group(r.row('user')('key'))
// Only get the product info inside the reduction
.map(r.row('product'))
.ungroup()
.map(function (row) {
return {
user: row('group'),
// Group by name
products: row('reduction').group('name').ungroup().map(function (row) {
return {
name: row('group'),
// Convert array of tags into key value pairs
tags: r.object(r.args(row('reduction').concatMap(function (row) {
return row('tags')
}).group(function (row) {
return row;
}).count().ungroup().concatMap(function (row) {
return [row('group'), row('reduction')]
})))
}
})
}
})
For the following data:
{
"id": "0565e91a-01ca-4ba3-b4d5-1043c918c79d" ,
"product": {
"name": "Product 2" ,
"tags": [
"tag1" ,
"tag3"
]
} ,
"user": {
"key": "100"
}
} {
"id": "39999c9f-bbef-4cb7-9311-2516ca8f9ba1" ,
"product": {
"name": "Product 1" ,
"tags": [
"tag1" ,
"tag3"
]
} ,
"user": {
"key": "100"
}
} {
"id": "566f3b79-01bf-4c29-8a9c-fd472431eeb6" ,
"product": {
"name": "Product 1" ,
"tags": [
"tag1" ,
"tag2"
]
} ,
"user": {
"key": "100"
}
} {
"id": "8e95c467-cedc-4734-ad4d-a1f7a371efd5" ,
"product": {
"name": "Product 1" ,
"tags": [
"tag1" ,
"tag2"
]
} ,
"user": {
"key": "200"
}
}
The results would be:
[
{
"products": [
{
"name": "Product 1" ,
"tags": {
"tag1": 2 ,
"tag2": 1 ,
"tag3": 1
}
}, {
"name": "Product 2" ,
"tags": {
"tag1": 1 ,
"tag3": 1
}
}
],
"user": "100"
} ,
{
"products": [
{
"name": "Product 1" ,
"tags": {
"tag1": 1 ,
"tag2": 1
}
}
] ,
"user": "200"
}
]

Resources