Postgres JSONB do a select from an array of data - sql

I'm using a Postgres database and I'm trying to use the new JSONB type. I have a table named employees with a column named previous_companies that contains the following JSON data:
[{"company":"Facebook", "link": "www.facebook.com"}, {"company":"Google", "link": "www.google.com"}, {"company":"Some Face", "link": "www.someface.com"}]
I'm trying to select all the employees that have certain string for the field "company", for example:
If I want all the employees that worked on a company, that has "face" on it's name I would have:
[{"company":"Facebook", "link": "www.facebook.com"}, {"company":"Some Face", "link": "www.someface.com"}]
I was able to do a query for the EXACT string, like this:
SELECT * FROM employees WHERE previous_companies #> '[{"company":"Facebook"}]'
but it returns this: [{"company":"Facebook", "link": "www.facebook.com"}]
As you can see this does not support querying for incomplete strings.
Thanks!

jsonb_array_elements() function may be helpful for querying by array JSONB column:
SELECT
id,
to_jsonb(array_agg(previous_company)) AS previous_companies
FROM (
SELECT
id,
jsonb_array_elements(previous_companies) AS previous_company
FROM ( VALUES
('id1', '[{"company":"Facebook", "link": "www.facebook.com"},{"company":"Google", "link": "www.google.com"}, {"company":"Some Face", "link": "www.someface.com"}]'::jsonb),
('id2', '[{"company":"Some Face", "link": "www.someface.com"}]'::jsonb),
('id3', '[{"company":"Google", "link": "www.google.com"}]'::jsonb)
) employees (id, previous_companies)
) T
WHERE
lower(previous_company->>'company') LIKE '%face%'
GROUP BY
id
;

Related

PostgreSQL array of object intersection

Given I have rows in my database, with a JSONB column that holds an array of items as such:
[
{"type": "human", "name": "Alice"},
{"type": "dog", "name": "Fido"},
{"type": "dog", "name": "Pluto"}
]
I need to be able to query rows based on this column. The query I want to write is a check to see if my array argument intersects, at any point, with this column.
Eg:
If I search for [{"type": "human", "name": "Alice"}], I should get a hit.
If I search for [{"type": "human", "name": "Alice"}, {"type": "dog", "name": "Doggy"}] I should also get a hit (Since one of the objects intersects)
I've tried using the ?| operator, but according to the docs, comparison is only made by keys. I need to match the entire jsonb object
You can use exists with cross join:
select t.* from tbl t where exists (select 1 from jsonb_array_elements(t.items) v
cross join jsonb_array_elements('[{"type": "human", "name": "Alice"}, {"type": "dog", "name": "Doggy"}]'::jsonb) v1
where v.value = v1.value)
See fiddle.
As a function:
create or replace function get_results(param jsonb)
returns table(items jsonb)
as $$
select t.* from tbl t where exists (select 1 from jsonb_array_elements(t.items) v
cross join jsonb_array_elements(param) v1
where v.value = v1.value)
$$ language sql;
See fiddle.

PostgreSQL JSON - String_agg in json data with multiple objects

I have create table with Json datatype field in PostgreSQL.
Also i have inserted data with multiple object in json data
like this
[
{
"Type": "1",
"Amount": "1000",
"Occurrence": 2,
"StartDate": "1990-01-19",
"EndDate": "1999-04-03"
},
{
"Type": "2",
"Amount": "2000",
"Occurrence": 2,
"StartDate": "1984-11-19",
"EndDate": "1997-09-29"
}
]
Now i have to retrieve my data as per below formate in single row like string_agg() function output.
Type Amount
1--2 1000-2000
also i have checked inbuilt function for json in PostgreSQL (https://www.postgresqltutorial.com/postgresql-json/) but not find any solutions for the same.
You will have to unnest the array and aggregate the individual keys:
The following assumes you have some kind of primary key column on the table (in addition to your JSON column):
select t.id,
string_agg(x.element ->> 'Type', '-') as types,
string_agg(x.element ->> 'Amount', '-') as amounts
from the_table t
cross join jsonb_array_elements(t.data) as x(element)
group by t.id;
jsonb_array_elements() extracts each array element and the main query then aggregates that back per ID using string_agg().
The above assumes your column is defined with the type jsonb (which it should be). If it is not, you need to use json_array_elements() instead.
Online example

SQL JSON Query -retrieve data within a JSON array

I have the following SQL "Product" table structure:
int Id
nvarchar(max) Details
Details contains JSON a string having the following structure:
{
"Id": "10001",
"Description": "example description",
"Variants": [{
"Title": "ABC / no",
"Price": "10"
}, {
"Title": "ABC / Yes",
"Price": "20",
}, {
"Title": "ABC / Yes",
"Price": "30",
}]
}
I need to write an SQL Query that would look through the table and return all the Variants with a particular title.
The following work
Get all rows from the table whose Details field contains a specific title
SELECT * FROM Products
WHERE JSON_VALUE(Details, '$.Description') = 'example description'
Get all rows from the table where Details.Variants[0].Title is equal to '{string}'
SELECT * FROM Products
WHERE JSON_VALUE(Details, '$.Variants[0].Title') = 'ABC / no'
Get all Ids from the table where Details.Variants[0].Title is equal to '{string}'
SELECT JSON_VALUE(Details, '$.Id')
FROM Products
WHERE JSON_VALUE(Details, '$.Variants[0].Title') = 'ABC / no'
I need to get all Variants from all rows in the Product table, where the Variant title is equal to '{string}'
There is a similar example in this documentation but I can't get it to work for my particuar case.
There is also this stack post
You need to use OPENJSON() with explicit schema (columns definitions) and additional APPLYs to parse the input JSON and get the expected results. Note, that you need to use AS JSON option to specify that the $.Variants part of the stored JSON is a JSON array.
Table:
CREATE TABLE Products (Id int, Details nvarchar(max))
INSERT INTO Products (Id, Details)
VALUES (1, N'{"Id":"10001","Description":"example description","Variants":[{"Title":"ABC / no","Price":"10"},{"Title":"ABC / Yes","Price":"20"},{"Title":"ABC / Yes","Price":"30"}]}"')
Statement:
SELECT p.Id, j1.Id, j1.Description, j2.Title, j2.Price
FROM Products p
CROSS APPLY OPENJSON (p.Details, '$') WITH (
Id int '$.Id',
[Description] nvarchar(100) '$.Description',
Variants nvarchar(max) '$.Variants' AS JSON
) j1
CROSS APPLY OPENJSON(j1.Variants) WITH (
Title nvarchar(100) '$.Title',
Price nvarchar(10) '$.Price'
) j2
WHERE
j2.Title = 'ABC / no'
-- or j1.Description = 'example description'
Result:
Id Id Description Title Price
1 10001 example description ABC / no 10

JSONB sort aggregation

I found this query that suits my needs thanks to this answer here in order to sort fields of data in a JSON document.
(Fake, generated random data)
SELECT jsonb_agg(elem)
FROM (
SELECT *
FROM jsonb_array_elements('[{
"id": "1",
"first_name": "Maximo",
"last_name": "Sambiedge",
"email": "msambiedge0#economist.com",
"gender": "Male",
"ip_address": "242.145.232.65"
}, {
"id": "2",
"first_name": "Maria",
"last_name": "Selland",
"email": "aselland1#sitemeter.com",
"gender": "Female",
"ip_address": "184.174.58.32"
}]') a(elem)
ORDER BY (elem->>'email') -- order by integer value of "ts"
) sub;
As we can see, this works with hardcoded data which doesn't quite fit my needs. I can't seem to figure out how to replace the JSON data with the jsonb column in my table.
My attempt below yields 'data is not defined'
SELECT jsonb_agg(elem), (SELECT data FROM file_metadata)
FROM (
SELECT *
FROM jsonb_array_elements(data) a(elem)
ORDER BY (elem->>'email')
) sub;
My suspicions are that a subquery is needed inside the FROM clause?
Here is a SQLFiddle of my issue to help describe the table and how the structure is defined: http://sqlfiddle.com/#!17/41102/92
You are almost there. You just need to bring in the original table, like so:
SELECT jsonb_agg(elem)
FROM (
SELECT elem
FROM file_metadata, jsonb_array_elements(data) a(elem)
ORDER BY (elem->>'email')
) sub;
Updated DB Fiddle

Make Column value as key using JSON function in Postgres

I'm using Postgres database and have a table as below
Table Name: Test
id firstname lastname
1 Sam Crews
2 John Dave
I'm trying to get result set back in below JSON format but with no luck
Expected:
[{"1": {"firstname": "Sam", "lastname": "Crews"}},
{"2": {"firstname": "John", "lastname": "Dave"}}
]
I tried using row_to_json, json_build_object functions but the output is bit different (as below).
Actual:
[{"id": "1", "firstname": "Sam", "lastname": "Crews"},
{"id": "2", "firstname": "John", "lastname": "Dave"}
]
Any pointers on how to achieve expected result would be greatly appreciated.
Thank you!
row_to_json() will always use all column names to create the keys in the JSON document. If you only want two columns, you need to explicitly specify them.
You also want a nested JSON object, so you need to nest the methods to create one:
select json_agg(json_build_object(
id,
json_build_object('firstname', firstname, 'lastname', lastname))
)
from the_table;
Klin made me realize that with Postgres 9.6 you can simplify the creation of the firstname/lastname object:
select json_agg(json_build_object(id, to_jsonb(the_table) - 'id'))
from the_table;
with my_table(id, firstname, lastname) as (
values
(1, 'Sam', 'Crews'),
(2, 'John', 'Dave')
)
select jsonb_agg(obj)
from my_table,
jsonb_build_object(id, to_jsonb(my_table)- 'id') obj
jsonb_agg
------------------------------------------------------------------------------------------------------
[{"1": {"lastname": "Crews", "firstname": "Sam"}}, {"2": {"lastname": "Dave", "firstname": "John"}}]
(1 row)