Remove Key Value pair from jsonb nested array in postgresql - postgresql

I have jsonb data as
{
"a":[
{"b":1,"c":2,"d":3},
{"b":4,"c":5,"d":6}
],
"g":[
{"b":1,"c":2,"d":3},
{"b":4,"c":5,"d":6}
]
}
I want to remove c key from nested array in both "a" and "g" keys. Is there a single query to perform this?

step-by-step demo: db<>fiddle
SELECT
jsonb_object_agg(key, a) -- 5
FROM (
SELECT
mydata,
key,
jsonb_agg(a_elems.value - 'c') as a -- 3/4
FROM
mytable,
jsonb_each(mydata) elems, -- 1
jsonb_array_elements(elems.value) AS a_elems -- 2
GROUP BY mydata, key -- 4
) s
GROUP BY mydata -- 5
Expand the JSON elements into one row each. This generates two columns: One for the key and one for the value (the JSON array)
Expand the JSON array into one row each (which separates the aggregated JSON object from which you want to remove the c element)
You can use the - operator to remove the element.
To reaggregate the original JSON object, you need to group it backwards. jsonb_agg() reaggregates the arrays
Finally you need to rebuild the original JSON object with jsonb_object_agg() using the previously generated key column and the new array column.

Related

Build jsonb array from jsonb field

I have column options with type jsonb , in format {"names": ["name1", "name2"]} which was created with
UPDATE table1 t1 SET options = (SELECT jsonb_build_object('names', names) FROM table2 t2 WHERE t2.id= t1.id)
and where names have type jsonb array.
SELECT jsonb_typeof(names) FROM table2 give array
Now I want to extract value of names as jsonb array. But query
SELECT jsonb_build_array(options->>'names') FROM table
gave me ["[\"name1\", \"name2\"]"], while I expect ["name1", "name2"]
How can I get value in right format?
The ->> operator will return the value of the field (in your case, a JSON array) as a properly escaped text. What you are looking for is the -> operator instead.
However, note that using the jsonb_build_array on that will return an array containing your original array, which is probably not what you want either; simply using options->'names' should get you what you want.
Actually, you don't need to use jsonb_build_array() function.
Use select options -> 'names' from table; This will fix your issue.
jsonb_build_array() is for generating the array from jsonb object. You are following wrong way. That's why you are getting string like this ["[\"name1\", \"name2\"]"].
Try to execute this sample SQL script:
select j->'names'
from (
select '{"names": ["name1", "name2"]}'::JSONB as j
) as a;

get first element from jsonb list of strings postgres

I have a List which is stored in my table as a jsonb and need a native query to get the first element from this jsonb column
Tried using jsonb_array_elements_text but couldn't get it to work.
select col from tbl;
returns:
["abc", "def", "etc"]
I need a query which can just return me abc
You can use the operator that picks the n-th element of an array:
select col ->> 0 as first_element
from tbl;
Note that unlike Postgres native arrays, the first element in a JSON array has the index 0

How to split array in json using json_query?

I've got a column in a table that's a json. It contains only values without keys like
Now I'm trying to split the data from the json and create new table using every index of each array as new entry like
I've already tried
SELECT JSON_QUERY(abc) as 'Type', Id as 'ValueId' from Table FOR JSON AUTO
Is there any way to handle splitting given that some arrays might be empty and look like
[]
?
A fairly simply approach would be to use outer apply with openjson.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
Id int,
Value nvarchar(20)
)
INSERT INTO #T VALUES
(1, '[10]'),
(2, '[20, 200]'),
(3, '[]'),
(4, '')
The query:
SELECT Id, JsonValues.Value
FROM #T As t
OUTER APPLY
OPENJSON( Value ) As JsonValues
WHERE ISJSON(t.Value) = 1
Results:
Id Value
1 10
2 20
2 200
3 NULL
Note the ISJSON condition in the where clause will prevent exceptions in case the Value column contains anything other than a valid json (an empty array [] is still considered valid for this purpose).
If you don't want to return a row where the json array is empty, use cross apply instead of outer apply.
Your own code calling for FOR JSON AUTO tries to create JSON out of tabular data. But what you really needs seems to be the opposite direction: You want to transform JSON to a result set, a derived table. This is done by OPENJSON.
Your JSON seems to be a very minimalistic array.
You can try something along this.
DECLARE #json NVARCHAR(MAX) =N'[1,2,3]';
SELECT * FROM OPENJSON(#json);
The result returns the zero-based ordinal position in key, the actual value in value and a (very limited) type-enum.
Hint: If you want to use this against a table's column you must use APPLY, something along
SELECT *
FROM YourTable t
OUTER APPLY OPENJSON(t.TheJsonColumn);

How to update a jsonb column in postgresql which is just an array of values and no keys

I need to update a jsonb column which is called "verticals" and the array of values it holds are like HOM, BFB etc. There are no keys in the array.
Table: Product(verticals jsonb, code int)
sample value stored in "verticals" column is
[HOM,rst,NLF,WELSAK,HTL,TRV,EVCU,GRT]
I need to update the value 'HOM' to 'XXX' in the column "verticals" where code =1
My expected output is
[XXX,rst,NLF,WELSAK,HTL,TRV,EVCU,GRT]
Because you chose to store your data in a de-normalized way, updating it is more complicated then it has to be.
You need to first unnest the array (essentially normalizing the data), replace the values, then aggregate them back and update the column:
update product p
set verticals = t.verticals
from (
select jsonb_agg(case when x.v = 'HOM' then 'XXX' else x.v end order by idx) as verticals
from product p2, jsonb_array_elements_text(p2.verticals) with ordinality as x(v,idx)
where code = 1
) t
where p.code = t.code;
This assumes that product.code is a primary (or unique) key!
Online example: http://rextester.com/KZQ65481
If the order of the array elements is not important, this gets easier:
update product
set verticals = (verticals - 'HOM')||'["XXX"]'
where code = 1;
This removes the element 'HOM' from the array (regardless of the posisition) and then appends 'XXX' to the end of the array.
You should use jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean]) and array_position() OR array_replace(anyarray, anyelement, anyelement)
https://www.postgresql.org/docs/9.5/static/functions-json.html
https://www.postgresql.org/docs/10/static/functions-array.html

Zend_Db_Adapter_Mysqli::fetchAssoc() I don't want primary keys as array indexes!

According to ZF documentation when using fetchAssoc() the first column in the result set must contain unique values, or else rows with duplicate values in the first column will overwrite previous data.
I don't want this, I want my array to be indexed 0,1,2,3... I don't need rows to be unique because I won't modify them and won't save them back to the DB.
According to ZF documentation fetchAll() (when using the default fetch mode, which is in fact FETCH_ASSOC) is equivalent to fetchAssoc(). BUT IT'S NOT.
I've used print_r()function to reveal the truth.
print_r($db->fetchAll('select col1, col2 from table'));
prints
Array
(
[0] => Array
(
[col1] => 1
[col2] => 2
)
)
So:
fetchAll() is what I wanted.
There's a bug in ZF documentation
From http://framework.zend.com/manual/1.11/en/zend.db.adapter.html
The fetchAssoc() method returns data in an array of associative arrays, regardless of what value you have set for the fetch mode, **using the first column as the array index**.
So if you put
$result = $db->fetchAssoc(
'SELECT some_column, other_column FROM table'
);
you'll have as result an array like this
$result['some_column']['other_column']