Does Postgres allow json[] or jsonb[]? - json

So l have been trying to find an answer on the internet with 0 luck.
Does postgres support having arrays of objects in a single field, e.g.
[
{
key: value,
another: value
},
{
key: value,
value: key
}
]
and saving this to a single field?
Also how would you perform the single INSERT or UPDATE
would it be: UPDATE db SET value='[{ key: val }, { key: val }]' ??

Postgres supports any valid json values, including json arrays.
What you are going to use is a single json (jsonb) column, not a Postgres array:
create table example (id int, val jsonb);
insert into example
values (1, '[{ "name": "aga" }, { "gender": "female" }]');
select * from example;
id | val
----+-----------------------------------------
1 | [{"name": "aga"}, {"gender": "female"}]
(1 row)

It depends on your definitoion of objects I guess.
You can use JSON: http://www.postgresql.org/docs/current/static/functions-json.html and insert unstructured data:
# create table test (field json);
CREATE TABLE
# insert into test values ('[1,2,3]');
INSERT 0 1
# insert into test values ('[{"key": "value"}, {"key": "value"}]');
INSERT 0 1
# select * from test;
field
--------------------------------------
[1,2,3]
[{"key": "value"}, {"key": "value"}]
There is also support for arrays: http://www.postgresql.org/docs/current/static/arrays.html

Related

is there a way to insert json multiple times into the same record?

Let's say I have inserted a record like this:
id | u_id | the_data
-------+------+--------------------------------------------------
1 | 2863 |[{name: a, body: lorem}]
using this command:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
u_id INT,
the_data JSON
);
INSERT INTO users (u_id, the_data) VALUES (2863, '[{"name": "a", "body": "lorem"}]');
But now, I want to insert some more data into the same record without losing the old array of json. How to do this type of insertion?
id | u_id | the_data
-------+------+------------------------------------------------------------------------------
1 | 2863 |[ {name: a, body: lorem}, {name: b, body: ipsum} ]
Please note: Below command creates a new record which I don't want.
INSERT INTO users (u_id, the_data)
VALUES (2863, '[{"name": "b", "body": "ipsum"}]');
Not looking for solutions like below since they all insert at the same time:
INSERT INTO users (u_id, the_data)
VALUES (2863, '[{"name": "a", "body": "lorem"}, {"name": "b", "body": "ipsum"}]');
INSERT INTO users (u_id, the_data)
VALUES (2863, '[{"name": "a", "body": "lorem"}]'), (2863, '[{"name": "b", "body": "ipsum"}]');
As the top level JSON object is an array, you can use the standard concatenation operator || to append an element to the array:
update users
set the_data = the_data || '{"name": "b", "body": "ipsum"}'
where u_id = 2863;
You should change your column definition to jsonb as that offers a lot more possibilities for querying or changing the value. Otherwise you will be forced to cast the column to jsonb every time you want to do something more interesting with it.
If you can't or don't want to change the data type you need to cast it:
set the_data = the_data::jsonb || '....'
You can create list of object and parse it to loop :
For Example:
var data = {
Id : Id
Name : Name
}
Json Request :
Data: data
Well, that's not a simple json object. You're trying to add a object to an array of values that is saved as json field.
So it's not about keeping the old array, but rather keeping the objects that were already present on the array saved in the json field and adding the new one.
I tried this on Postgres 12, it works, basically as someone else said, you will need to cast the jsonb type if you've json and use pipes operator to concatenate the new value.
UPDATE users
SET the_data = the_data::jsonb || '{"name": "b", "body": "ipsum"}'
WHERE id = 1;
Taken from here:
https://stackoverflow.com/a/69630521/9231145

postgres cast json vs. to_json function (why different output)

I have a Postgres table like:
CREATE table json_str(
foo int,
js text
);
insert into json_str(foo, js) values (1, '{ "key": "value", "foo": 1}');
and want to cast the column js of type text to JSON.
Why is the output of cast different from to_json?
Ideally, I can fit the type change into an alter table statement. However, a simple cast is not accepted and I am forced to use to_json (which returns doubly quoted non-plain JSON. Is there a way to get the standard (direct) JSON layout for an alter table statement?
select * from json_str;
-- { "key": "value", "foo": 1}
select cast(js as jsonb) from json_str;
--- {"foo": 1, "key": "value"}
select to_json(js) from json_str;
-- "{ \"key\": \"value\", \"foo\": 1}"
alter table json_str alter column js type jsonb using to_jsonb(js);
select * from json_str;
-- "{ \"key\": \"value\", \"foo\": 1}"
This is the working alter table statement:
alter table json_str alter column js type jsonb using js::jsonb;

Export SQLite table which contains JSON column without double escaping

Let a SQLite table be built like :
create table t(id, j json);
insert into t values (1, '{"name": "bob"}');
insert into t values (2, '{"name": "alice", "age":20, "hobbies":[ "a", "b", "c"] }');
What's the easiest way to export the whole table as valid JSON, without JSON strings being escaped?
sqlite> .mode json
sqlite> select id, j from t;
[{"id":1,"j":"{\"name\": \"bob\"}"},
{"id":2,"j":"{\"name\": \"alice\", \"age\":20, \"hobbies\":[ \"a\", \"b\", \"c\"] }"}] -- WRONG!
JSON column may vary. Couldn't do it with json_extract function. Expecting parsable JSON,
[{"id":1,"j":{"name": "bob"}},
{"id":2,"j":{"name": "alice", "age":20, "hobbies":[ "a", "b", "c"] }}]
Use the functions json() to remove escaping from the column j and json_group_array() to aggregate:
select json_group_array(json_object('id', id, 'j', json(j))) result
from t
See the demo.
Results:
result
[{"id":1,"j":{"name":"bob"}},{"id":2,"j":{"name":"alice","age":20,"hobbies":["a","b","c"]}}]

SQL Server For JSON Path dynamic column name

We are exploring the JSON feature in SQL Sever and for one of the scenarios we want to come up with a SQL which can return a JSON like below
[
{
"field": {
"uuid": "uuid-field-1"
},
"value": {
"uuid": "uuid-value" //value is an object
}
},
{
"field": {
"uuid": "uuid-field-2"
},
"value": "1". //value is simple integer
}
... more rows
]
The value field can be a simple integer/string or a nested object.
We are able to come up with a table which looks like below:
field.uuid | value.uuid | value|
------------|---------- | -----|
uuid-field-1| value-uuid | null |
uuid-field-2| null | 1 |
... more rows
But as soon as we apply for json path, it fails saying
Property 'value' cannot be generated in JSON output due to a conflict with another column name or alias. Use different names and aliases for each column in SELECT list.
Is it possible to do it somehow generate this? The value will either be in the value.uuid or value not both?
Note: We are open to possibility of if we can convert each row to individual JSON and add all of them in an array.
select
json_query((select v.[field.uuid] as 'uuid' for json path, without_array_wrapper)) as 'field',
value as 'value',
json_query((select v.[value.uuid] as 'uuid' where v.[value.uuid] is not null for json path, without_array_wrapper)) as 'value'
from
(
values
('uuid-field-1', 'value-uuid1', null),
('uuid-field-2', null, 2),
('uuid-field-3', 'value-uuid3', null),
('uuid-field-4', null, 4)
) as v([field.uuid], [value.uuid], value)
for json auto;--, without_array_wrapper;
The reason for this error is that (as is mentioned in the documentation) ... FOR JSON PATH clause uses the column alias or column name to determine the key name in the JSON output. If an alias contains dots, the PATH option creates nested objects. In your case value.uuid and value both generate a key with name value.
I can suggest an approach (probably not the best one), which uses JSON_MODIFY() to generate the expected JSON from an empty JSON array:
Table:
CREATE TABLE Data (
[field.uuid] varchar(100),
[value.uuid] varchar(100),
[value] int
)
INSERT INTO Data
([field.uuid], [value.uuid], [value])
VALUES
('uuid-field-1', 'value-uuid', NULL),
('uuid-field-2', NULL, 1),
('uuid-field-3', NULL, 3),
('uuid-field-4', NULL, 4)
Statement:
DECLARE #json nvarchar(max) = N'[]'
SELECT #json = JSON_MODIFY(
#json,
'append $',
JSON_QUERY(
CASE
WHEN [value.uuid] IS NOT NULL THEN (SELECT d.[field.uuid], [value.uuid] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
WHEN [value] IS NOT NULL THEN (SELECT d.[field.uuid], [value] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
END
)
)
FROM Data d
SELECT #json
Result:
[
{
"field":{
"uuid":"uuid-field-1"
},
"value":{
"uuid":"value-uuid"
}
},
{
"field":{
"uuid":"uuid-field-2"
},
"value":1
},
{
"field":{
"uuid":"uuid-field-3"
},
"value":3
},
{
"field":{
"uuid":"uuid-field-4"
},
"value":4
}
]

Update every value in an array in postgres json

In my postgres database I have json that looks similar to this:
{
"myArray": [
{
"myValue": 1
},
{
"myValue": 2
},
{
"myValue": 3
}
]
}
Now I want to rename myValue to otherValue. I can't be sure about the length of the array! Preferably I would like to use something like set_jsonb with a wildcard as the array index, but that does not seem to be supported. So what is the nicest solution?
You have to decompose a whole jsonb object, modify individual elements and build the object back.
The custom function will be helpful:
create or replace function jsonb_change_keys_in_array(arr jsonb, old_key text, new_key text)
returns jsonb language sql as $$
select jsonb_agg(case
when value->old_key is null then value
else value- old_key || jsonb_build_object(new_key, value->old_key)
end)
from jsonb_array_elements(arr)
$$;
Use:
with my_table (id, data) as (
values(1,
'{
"myArray": [
{
"myValue": 1
},
{
"myValue": 2
},
{
"myValue": 3
}
]
}'::jsonb)
)
select
id,
jsonb_build_object(
'myArray',
jsonb_change_keys_in_array(data->'myArray', 'myValue', 'otherValue')
)
from my_table;
id | jsonb_build_object
----+------------------------------------------------------------------------
1 | {"myArray": [{"otherValue": 1}, {"otherValue": 2}, {"otherValue": 3}]}
(1 row)
Using json functions are definitely the most elegant, but you can get by on using character replacement. Cast the json(b) as text, perform the replace, then change it back to json(b). In this example I included the quotes and colon to help the text replace target the json keys without conflict with values.
CREATE TABLE mytable ( id INT, data JSONB );
INSERT INTO mytable VALUES (1, '{"myArray": [{"myValue": 1},{"myValue": 2},{"myValue": 3}]}');
INSERT INTO mytable VALUES (2, '{"myArray": [{"myValue": 4},{"myValue": 5},{"myValue": 6}]}');
SELECT * FROM mytable;
UPDATE mytable
SET data = REPLACE(data :: TEXT, '"myValue":', '"otherValue":') :: JSONB;
SELECT * FROM mytable;
http://sqlfiddle.com/#!17/1c28a/9/4