can't select data from a dictionary inside a list - postgresql

I have two jsonb fields in the table below and I would like to do a query where I filter any key of the dictionary.
My problem is that those dictionaries are inside of a list and when I try to access them with:
SELECT *
FROM public.monitoring_environmentalcontrol
WHERE celery_status->'queue'='0'
I get nothing:

You can use jsonb_array_elements DOC function to achieve your goal plus a LATERAL JOIN 7.2.1.5. LATERAL Subqueries on it:
This is the setup I created:
create table test (
id int,
celery_status jsonb
);
insert into test values
(1,'[{"queue":"a"}, {"queue":"b"}, {"queue":"c"}]'),
(2,'[{"queue":"d"}, {"queue":"e"}, {"queue":"f"}]'),
(3,'[{"queue":"g"}, {"queue":"h"}, {"queue":"i"}]');
This is the query:
select t.id, t.celery_status, obj->>'queue'
from test t
join lateral
jsonb_array_elements(t.celery_status) obj(value) on obj->>'queue' = 'a'
You can see it working here: http://sqlfiddle.com/#!17/bf7bf/6

Related

How to convert an jsonb array and use stats moment

how are you?
I needed to store an array of numbers as JSONB in PostgreSQL.
Now I'm trying to calculate stats moments from this JSON, I'm facing some issues.
Sample of my data:
I already was able to convert a JSON into a float array.
I used a function to convert jsonb to float array.
CREATE OR REPLACE FUNCTION jsonb_array_castdouble(jsonb) RETURNS float[] AS $f$
SELECT array_agg(x)::float[] || ARRAY[]::float[] FROM jsonb_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;
Using this SQL:
with data as (
select
s.id as id,
jsonb_array_castdouble(s.snx_normalized) as serie
FROM
spectra s
)
select * from data;
I found a function that can do these calculations and I need to pass an array for that: https://github.com/ellisonch/PostgreSQL-Stats-Aggregate/
But this function requires an array in another way: unnested
I already tried to use unnest, but it will get only one value, not the entire array :(.
My goal is:
Be able to apply stats moment (kurtosis, skewness) for each row.
like:
index
skewness
1
21.2131
2
1.123
Bonus: There is a way to not use this 'with data', use the transformation in the select statement?
snx_wavelengths is JSON, right? And also you provided it as a picture and not text :( the data looks like (id, snx_wavelengths) - I believe you meant id saying index (not a good idea to use a keyword, would require identifier doublequotes):
1,[1,2,3,4]
2,[373,232,435,84]
If that is right:
select id, (stats_agg(v::float)).skewness
from myMeasures,
lateral json_array_elements_text(snx_wavelengths) v
group by id;
DBFiddle demo
BTW, you don't need "with data" in the original sample if you don't want to use and could replace with a subquery. ie:
select (stats_agg(n)).* from (select unnest(array[16,22,33,24,15])) data(n)
union all
select (stats_agg(n)).* from (select unnest(array[416,622,833,224,215])) data(n);
EDIT: And if you needed other stats too:
select id, "count","min","max","mean","variance","skewness","kurtosis"
from myMeasures,
lateral (select (stats_agg(v::float)).* from json_array_elements_text(snx_wavelengths) v) foo
group by id,"count","min","max","mean","variance","skewness","kurtosis";
DBFiddle demo

Building a jsonb object without using subquery in postgresql 12

I have a table called users that has a jsonb column called 'history'. This is an array of objects, one of the elements is called uid which is the id of the person visiting the page as follows:
[ {"ip":"...","uid":2} , {"ip":"...","uid":4} , ... ]
I'm running a query that appends the jsonb object with the field uname to make understanding who 'uid' is a bit easier which will produce:
[ {"ip":"...","uid":2,"uname":"bob"} , {"ip":"...","uid":4,"uname":"dave"} , ... ]
I'm currently doing this using the following query (say, where uid=2):
SELECT json_agg(history2||jsonb_build_object('uname',uname::text)) FROM
(SELECT jsonb_array_elements(history) AS history2 FROM users WHERE uid=2) AS table1
LEFT JOIN users AS table2 ON history2->>'uid'=table2.uid
I'm using the subquery to return a table of json objects that's then joined to the user table again to get the username.
My question is: Is there a way of doing this without having the subquery? I've read that lateral joins could be used but all my attempts at this don't seem to work.
Thanks in advance.
You can move jsonb_array_elements into the FROM clause with an outer join:
SELECT jsonb_agg(h.item||jsonb_build_object('uname', u.uname))
FROM users u
LEFT JOIN jsonb_array_elements(u.history) as h(item) on h.item ->> 'uid' = u.uid::text
WHERE u.uid = 2

Fetch rows from postgres table which contains a specific id in jsonb[] column

I have a details table with adeet column defined as jsonb[]
a sample value stored in adeet column is as below image
Sample data stored in DB :
I want to return the rows which satisfies id=26088 i.e row 1 and 3
I have tried array operations and json operations but it does'nt work as required. Any pointers
Obviously the type of the column adeet is not of type JSON/JSONB, but maybe VARCHAR and we should fix the format so as to convert into a JSONB type. I used replace() and r/ltrim() funcitons for this conversion, and preferred to derive an array in order to use jsonb_array_elements() function :
WITH t(jobid,adeet) AS
(
SELECT jobid, replace(replace(replace(adeet,'\',''),'"{','{'),'}"','}')
FROM tab
), t2 AS
(
SELECT jobid, ('['||rtrim(ltrim(adeet,'{'), '}')||']')::jsonb as adeet
FROM t
)
SELECT t.*
FROM t2 t
CROSS JOIN jsonb_array_elements(adeet) j
WHERE (j.value ->> 'id')::int = 26088
Demo
You want to combine JSONB's <# operator with the generic-array ANY construct.
select * from foobar where '{"id":26088}' <# ANY (adeet);

Conditionally do jsonb_array_elements

I have a jsonb column called info that some type have the form of an object: { id: 2 }
and some other types it has the shape of an array: [{ id: 2 }]
I have a query that does this:
SELECT * FROM users
CROSS JOIN LATERAL jsonb_array_elements(users.info)
Now, if my data set has just arrays in the info column, there is no issue, but if there is one that is not an array, but rather an object, I get the error:
ERROR: cannot extract elements from an object
Is there a way in PostgreSQL for that CROSS JOIN LATERAL to ignore the rows that are not arrays in the info column?
Use the function jsonb_typeof() to eliminate rows with non-array jsonb column:
select *
from (
select *
from users
where jsonb_typeof(info) = 'array'
) s
cross join lateral jsonb_array_elements(info);
Working example in rextester.

How to rename a key in a jsonb list?

I am trying to rename a json key of all the objects in a list e.g. given my_table with my_col containing a jsonb list:
[{name:0}, {name:1}, {name:2}]
I have worked out how to change a single element of a list by removing and adding a key using explicit indexing e.g.
select (my_col->0)::jsonb - 'name' ||
jsonb_build_object('new_name', my_col->0->'name')
from my_table
But how can this then be applied to all elements in the list?
Here is my best answer so far that works using a lateral join.
update my_table t1
set my_col =
(select json_agg(el::jsonb - 'name' || jsonb_build_object('new_name', el->'name'))
from my_table t2, jsonb_array_elements(t2.my_col) as el
where t1.id = t2.id)
Ideally there would just be some neat pattern matching operators e.g. my_col->*->'name'.