Cast top-level JSONB field as UUID - postgresql

Consider a table like so:
CREATE TABLE items (
e uuid,
v jsonb
)
I insert the following values:
INSERT INTO items (e, v) VALUES
('9a70439e-33c0-4b34-91f5-efac20b58301', '"92cb730c-8b4f-46ef-9925-4fab953694c6"'),
('92cb730c-8b4f-46ef-9925-4fab953694c6', '"Bob"'),
('92cb730c-8b4f-46ef-9925-4fab953694c6', '52');
Note how for v, I have actually stringified text and numbers -- not an object.
Now, what if I wanted to write a query like so:
WITH match AS (
SELECT * FROM items WHERE e = '9a70439e-33c0-4b34-91f5-efac20b58301'
) SELECT * FROM items JOIN match ON match.v = items.e;
I would get:
Query Error: error: operator does not exist: jsonb = uuid
Is there a way I could tell postgres to "parse" the jsonb value, and see if it is a uuid?
I know about Postgres cast to UUID from JSON , where the ->> operator was able to do the trick. But I can't do that in this case, because our json value is a strong not an object.

You can use ->> 0 to extract the value as text:
SELECT * FROM items
WHERE e = '9a70439e-33c0-4b34-91f5-efac20b58301'
AND e::text = v ->> 0;

Related

Cast uuid into a jsonb field

Consider the following tables:
CREATE TABLE uuid_items (
u uuid
);
CREATE TABLE jsonb_items (
j jsonb
);
INSERT INTO uuid_items (u) values ('6de8874f-99f0-4b05-8c83-b904b3065ff0');
INSERT INTO jsonb_items (j) values ('"6de8874f-99f0-4b05-8c83-b904b3065ff0"');
Now, I would like to join uuid_items on jsonb_items. Something like so:
SELECT * from uuid_items JOIN jsonb_items ON uuid_items.u::jsonb = jsonb_items.j;
This will give me the error:
Query Error: error: cannot cast type uuid to jsonb
I thought of using something like jsonb_build_array as a hack:
SELECT * from uuid_items JOIN jsonb_items ON jsonb_build_array(uuid_items.u)->0 = jsonb_items.j;
But I wonder: is there a better way? How could I cast uuid_items.u, into a valid jsonb value?
You can play with the playground here
You can either convert both to text:
WHERE u::text = j ->> 0
or convert both to jsonb:
WHERE to_jsonb(u) = j
or convert both to uuid:
WHERE u = (j ->> 0)::uuid
Choose according to the indexes you have in place. Best practice is to use the proper data type (uuid) for both columns.

How to lower-case all the elements of a JSONB array of strings of each row in a table

I have a table with a field called "data" which is of JSONB type. The content of "data" is an object with one of the fields called "associated_emails", which is an array of strings.
I need to update the existing table so that the content of "associated_emails" is all lower-case. How to achieve that? This is my attempt so far (it triggers error: ERROR: cannot extract elements from a scalar)
update mytable my
set
"data" = safe_jsonb_set(
my."data",
'{associated_emails}',
to_jsonb(
lower(
(
SELECT array_agg(x) FROM jsonb_array_elements_text(
coalesce(
my."data"->'associated_emails',
'{}'::jsonb
)
) t(x)
)::text[]::text
)::text[]
)
)
where
my.mytype = 'something';
You would like to use JSONB_SET and UPDATE the column with something like given below below:
UPDATE jsonb_test
SET data = JSONB_SET(data, '{0,associated_emails}',
JSONB(LOWER(data ->> 'associated_emails'::TEXT)));

Postgres jsonb : cast array element to integer

Using Postgres 11.2.9 (ubuntu),
In my database, I have a jsonb field containing values that look like this :
[1618171589133, 1618171589245, 1618171589689]
I'd like to retrieve rows where the first element is lower than a specific value. I've tried this :
SELECT * FROM user.times WHERE time ->> 0 < 1618171589133
but I get the following error : ERROR: operator does not exist: text = bigint
Should I somehow cast the time value to numeric value ? I've tried time ->> 0::numeric but I actually don't know what to do.
The ->> operator returns the element at given position as text, which you can then convert to integer (or as it seems in this case, bigint), as you would normally do in postgres, using the :: as suffix.
SELECT * FROM user.times WHERE ((time ->> 0)::bigint) < 1618171589133

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;

how can i update json array object value

I have an array object it is not updated object value.
It always appends a new object in an array.
I have try || operator but it is only working on an object, not array object.
I do the following:
UPDATE "chats" SET
"groupUserStatus"="groupUserStatus" :: JSONB || '{"1":"viewed"}'
WHERE "chats"."chatType"='groupchat' AND
("chats"."groupUserStatus") :: JSONB #> '{"1":"sent"}' :: JSONB
it is updated object successfully but I have update array object.
For Example
Insert Record like this
[{"1":"sent"},{"2":"read"}]
but how i will get record like this (How update record)
[{"1":"viewed"},{"2":"read"}]
sorry for bad english
Use jsonb_set
set groupUserStatus = jsonb_set ( groupUserStatus :: JSONB ,'{0,"1"}', '"viewed"')
DEMO
EDIT : To get the index dynamically and update, you may have to
extract the elements of the array and each key value pairs.
update t t1 set col = jsonb_set ( t2.col :: JSONB ,
('{'||j.idx-1||',"'||e.k||'"}')::text[] , '"viewed"' )
FROM t t2 cross join
jsonb_array_elements (t2.col :: JSONB) with ordinality as j(elem,idx)
cross join jsonb_each_text(j.elem) as e(k,v)
where e.v ='sent'
and t1.id = t2.id
DEMO2