Can't filter by nodes in field with type jsonb - postgresql-9.5

Table has two columns: id and data.
Column data has type jsonb
json :
"weight": {
"qty": 300,
"unit": {
"name": "gram",
"title": {
"en": "g"
}
}
I want to filter by json's nodes weight and qty.
Here query:
SELECT
(product.data #>'{weight,qty}') AS weight
FROM product
WHERE weight = 300
order by id desc
But I get error:
ERROR: column "weight" does not exist
LINE 4: WHERE weight = 123
^
SQL state: 42703
Character: 70

Related

Average of numeric values in Postgres JSON column

I have a jsonb column with the following structure:
{
"key1": {
"type": "...",
"label": "...",
"variables": [
{
"label": "Height",
"value": 131315.9289,
"variable": "myVar1"
},
{
"label": "Width",
"value": 61085.7525,
"variable": "myVar2"
}
]
},
}
I want to query for the average height across all rows. The top-level key values are unknown, so I have something like this:
select id,
avg((latVars ->> 'value')::numeric) as avg
from "MyTable",
jsonb_array_elements((my_json_field->jsonb_object_keys(my_json_field)->>'variables')::jsonb) as latVars
where my_json_field is not null
group by id;
It's throwing the following error:
ERROR: set-returning functions must appear at top level of FROM
Moving the jsonb_array_elements function above MyTable in the FROM clause doesn't work.
I'm following the basic advice found in this SO answer to no avail.
Any advice?
jsonb_array_elements is not relevant until my_json_field is a json array at the top level.
You can use instead the jsonb_path_query function based on the jsonpath language if postgres >= 12 :
select id
, avg(v.value :: numeric) as avg
from "MyTable"
, jsonb_path_query(my_json_field, '$.*.variables[*] ? (#.label == "Height").value') AS v(value)
where my_json_field is not null
group by id;

Can't add new item to array with value from prev array's item

column data has type jsonb
example:
"availability": [
{
"qty": 31,
"is_available": false,
"store": {
"name": "test_value
}
},
It's contain array availability with one item.
I want to add new item to this array. And get value of node qty from first item. And then add this value to second (new) array's item.
As result availability will contain 2 items. And nodes qty must be equals in both items.
I try this:
WITH subquery AS (
SELECT
id,
data #>>'{availability,0,qty}' as qty
from copy_product
)
UPDATE copy_product
SET
data = (
jsonb_set(data, '{availability}', data -> 'availability' || '{
"qty": subquery.qty,
"is_available": false,
"store": {
"address": null
}}')
)
But I get error:
ERROR: invalid input syntax for type json
LINE 10: ...onb_set(data, '{availability}', data -> 'availability' || '{
^
DETAIL: Token "subquery" is invalid.
CONTEXT: JSON data, line 1: {
"qty": subquery...
SQL state: 22P02
Character: 190
No need for a sub-query (or even a CTE), you can reference the existing value directly as part of the expression passed to jsonb_set()
UPDATE copy_product
SET data = jsonb_set(data,
'{availability}',
data -> 'availability'
|| ('{"qty": '||(data #>>'{availability,0,qty}')||', "is_available": false, "store": {"address": null}}')::jsonb)
Alternatively using jsonb_build_object()
UPDATE copy_product
SET data = jsonb_set(data,
'{availability}',
data -> 'availability' ||
jsonb_build_object(
'qty', data #>>'{availability,0,qty}',
'is_available', false,
'store', jsonb_build_object('address', null)
)

Get data from any items in array

PosgreSQL 9.5
Field type: jsonb
Here json
{
"options": [
{
"name": "method"
},
{
"name": "flavor"
},
{
"name": "weight",
"value": {
"name": "300g"
}
}
]
}
And here query that get value of item (weight) with index = 2 from array:
SELECT
id,
product.data #>'{title,en}' AS title_en,
product.data #>>'{options, 2, value, name }' as options_weight_value
FROM product
Nice. It's work fine.
But the problem that weight can be in any index in array. First or second and so on.
So I need to get value of name (300g) in node "weight" .
I need smt like this:
SELECT
id,
product.data #>'{title,en}' AS title_en,
product.data #>>'{options, *, value, name, weight }' as options_weight_value
FROM product
Is it possible ?
I think I found solution:
SELECT
id,
p.data #>'{title,en}' AS title_en,
p.data #>'{weight,qty}' AS weight_qty,
(select *
from jsonb_array_elements(p.data -> 'options') AS options_array
where
options_array ->> 'name' = 'weight'
) #>'{value,name}' as options_weight
from product p
And now find value of weight(if exist) in any array's item. In this example it = 300g

How to generate JSON array from multiple rows, then return with values of another table

I am trying to build a query which combines rows of one table into a JSON array, I then want that array to be part of the return.
I know how to do a simple query like
SELECT *
FROM public.template
WHERE id=1
And I have worked out how to produce the JSON array that I want
SELECT array_to_json(array_agg(to_json(fields)))
FROM (
SELECT id, name, format, data
FROM public.field
WHERE template_id = 1
) fields
However, I cannot work out how to combine the two, so that the result is a number of fields from public.template with the output of the second query being one of the returned fields.
I am using PostGreSQL 9.6.6
Edit, as requested more information, a definition of field and template tables and a sample of each queries output.
Currently, I have a JSONB row on the template table which I am using to store an array of fields, but I want to move fields to their own table so that I can more easily enforce a schema on them.
Template table contains:
id
name
data
organisation_id
But I would like to remove data and replace it with the field table which contains:
id
name
format
data
template_id
At the moment the output of the first query is:
{
"id": 1,
"name": "Test Template",
"data": [
{
"id": "1",
"data": null,
"name": "Assigned User",
"format": "String"
},
{
"id": "2",
"data": null,
"name": "Office",
"format": "String"
},
{
"id": "3",
"data": null,
"name": "Department",
"format": "String"
}
],
"id_organisation": 1
}
This output is what I would like to recreate using one query and both tables. The second query outputs this, but I do not know how to merge it into a single query:
[{
"id": 1,
"name": "Assigned User",
"format": "String",
"data": null
},{
"id": 2,
"name": "Office",
"format": "String",
"data": null
},{
"id": 3,
"name": "Department",
"format": "String",
"data": null
}]
The feature you're looking for is json concatenation. You can do that by using the operator ||. It's available since PostgreSQL 9.5
SELECT to_jsonb(template.*) || jsonb_build_object('data', (SELECT to_jsonb(field) WHERE template_id = templates.id)) FROM template
Sorry for poorly phrasing what I was trying to achieve, after hours of Googling I have worked it out and it was a lot more simple than I thought in my ignorance.
SELECT id, name, data
FROM public.template, (
SELECT array_to_json(array_agg(to_json(fields)))
FROM (
SELECT id, name, format, data
FROM public.field
WHERE template_id = 1
) fields
) as data
WHERE id = 1
I wanted the result of the subquery to be a column in the ouput rather than compiling the entire output table as a JSON.

select query for >= jsonb column value in postgresql

I have a postgres database with jsonb format column tagsin table. I am trying to query for rows where the confidence >= 50.
I am unsure how to index into the predictions list to check for the confidence. I have tried the query below it executes without error but doesn't return any rows.
select * from mytable where (tags->>'confidence')::int >= 50;
Here is an example row jsonb
{
"predictions": [
{
"label": "Shopping",
"confidence": 91
},
{
"label": "Entertainment",
"confidence": 4
},
{
"label": "Events",
"confidence": 2
}
]
}
You need to normalize the data by un-nest the array, then you can
select p.d
from mytable mt
cross join lateral jsonb_array_elements(mt.tags -> 'predictions') as p(d)
where (p.d ->> 'confidence')::int >= 50;
For the above sample data, that returns:
{"label": "Shopping", "confidence": 91}
Online example: http://rextester.com/CBIAR76462