Can't sort by all array's items? - postgresql

Postgresq 9.6
json
"availability": [
{
"qty": 25,
"price": 1599,
"is_available": true
},
{
"qty": 72,
"price": 3599,
},
"is_available": true
]
table with column data. Type is jsonb
If I want to sort first array's(availability) item by field "price" I this:
SELECT *
from product prod
WHERE to_tsvector('english', prod.data) ## to_tsquery('gram')
ORDER BY prod.data #> '{availability,0,price}' desc
OK.
But I need to sort all fields "price" in array availability
Smt like this (pseudo code)
SELECT *
from product prod
WHERE to_tsvector('english', prod.data) ## to_tsquery('gram')
ORDER BY prod.data #> '{availability,*,price}' desc
I need to to order by "price" desc.
The result must be
First record of result is second json
"availability": [
{
"qty": 25,
"price": 11599,
"is_available": true
},
{
"qty": 72,
"price": 13599,
},
"is_available": true
]
...
"availability": [
{
"qty": 25,
"price": 1599,
"is_available": true
},
{
"qty": 72,
"price": 3599,
},
"is_available": true
]
Is it possible?

This could be done like this:
select id,
jsonb_set(data, '{availability}',
(select jsonb_agg(item order by (item ->> 'price')::numeric)
from jsonb_array_elements(data -> 'availability') as x(item))
) as data
from product
where ...

Related

How to format & design the JSON format in Postgres

I have a request to make the below JSON format and it seems very complex to me, I tried with the below solution but facing 2 issues
1.Sub child items under the child items seems not to be working.
Also if i have multiple products in one order , I would like to have that comes under this tags "lineItems" , But instead with the solution i have worked upon, If there are multiple products each is generating a separate JSON row, I need one JSON row per order.
It will be of very help if anyone of can give shred some light here.
Sample JSON String to be constructed.
{
"order_id": "string",
"createdAt": 0,
"orderNumber": "string",
"tags": [
"string"
],
"lineItems": [
{
"line_id": "string",
"productName": "string",
"quantity": 0,
"productTags": [
"string"
],
"discounts": [
{
"title": "string",
"voucherCode": "string",
"discountAmount": 0.1
}
],
"taxes": [
{
"tactitle": "string",
"taxRate": 0.1,
"taxAmount": 0.1
}
],
"totalAmountBeforeTaxesAndDiscounts": 0.1,
"totalAmountAfterTaxesAndDiscounts": 0.1
}
],
"shipping": {
"city": "string",
"zipOrPostalCode": "string",
"providerDescriptor": "string",
"shippingTotalAmountBeforeTaxAndDiscounts": 0.1,
"discounts": [
{
"title": "string",
"voucherCode": "string",
"discountAmount": 0.1
}
],
"taxes": [
{
"title": "string",
"taxRate": 0.1,
"taxAmount": 0.1
}
],
"shippingTotalAmountAfterTaxAndDiscounts": 0.1
},
"transactionCosts": 0.1,
"customer": {
"id": "string",
"email": "string",
"tags": [
"string"
]
},
"optional": {
"googleAnalyticsTransactionId": "string",
"orderSourceName": "string",
"orderChannelName": "string",
"orderPlatformName": "string"
}
}
The solution I have tried is below.
With orders as
(
SELECT "order_id",
"createdAt",
"orderNumber",
STRING_AGG(tags,',') as tags
FROM orders o
)
,lineItems as
(
SELECT "line_id",
order_id
"productName",
"quantity",
STRING_AGG(productTags,',') as "productTags",
"vouchertitle",
"voucherCode",
"discountAmount",
"taxtitle",
"taxRate",
"taxAmount",
"totalAmountBeforeTaxesAndDiscounts",
"totalAmountAfterTaxesAndDiscounts"
FROM items
)
,shipments as
(
SELECT order_id,
"city",
"zipOrPostalCode",
"providerDescriptor",
"shippingTotalAmountBeforeTaxAndDiscounts",
"title",
"voucherCode",
"discountAmount",
"taxtitle",
"taxRate",
"taxAmount",
"shippingTotalAmountAfterTaxAndDiscounts"
FROM shipments s
INNER JOIN orders o ON s.order_id=o.id
)
,customer AS
(
SELECT
order_id,
"customer_id",
"email"
STRING_AGG("customer_tags") as tags
FROM customers c
)
, optional AS
(
SELECT order_id,
"googleAnalyticsTransactionId",
"source",
"channel",
"platform"
FROM analytics
)
, base as (
select
cm.*,i as lineItems
, s as shipment
, c as customer
, ga as "optionalIdentifiers"
from
orders cm
LEFT JOIN lineItems i ON cm.order_id = i.order_id
LEFT JOIN shipments s ON cm.order_id=s.order_id
LEFT JOIN customer c ON cm."order_id"=c.order_id
LEFT JOIN optional ga ON cm."order_id"=ga.order_id
)
select row_to_json(c) as "data" from base c
Sample Input
create temp table orders(order_id int,"createdAt" date,"orderNumber" text,tags text);
INSERT INTO orders(order_id,"createdAt","orderNumber",tags)
VALUES (1,'2022-12-09' , '10001', 'no tags'),
(2,'2022-12-10' , '19999', 'tag1,tags 2');
create temp table lineItems(line_id int,order_id int,"productName" text,"quantity" int,
"productTags" text,"vouchertitle" text,"voucherCode" text,"discountAmount" real,
"taxtitle" text,"taxRate" real,"taxAmount" real,"totalAmountBeforeTaxesAndDiscounts" real,
"totalAmountAfterTaxesAndDiscounts" real);
INSERT INTO lineItems(line_id ,order_id ,"productName" ,"quantity" ,
"productTags" ,"vouchertitle" ,"voucherCode" ,"discountAmount" ,
"taxtitle" ,"taxRate" ,"taxAmount" ,"totalAmountBeforeTaxesAndDiscounts" ,
"totalAmountAfterTaxesAndDiscounts" )
VALUES (0,1,'Vitamin D',100,'Bio','Xmas campaign','XMAS001',1000,'TAX 10%',10,500,15000,14000),
(2,1,'Vitamin C',50,'No Tags','Xmas campaign','XMAS001',500,'TAX 7%',7,33,1900,1400),
(11,2,'Vitamin C',50,'No Tags','Xmas campaign','XMAS002',100,'TAX 7%',7,55,2500,2400)
;
create temp table shipments(order_id int,"city" text,"zipOrPostalCode" text,"providerDescriptor" text,
"shippingTotalAmountBeforeTaxAndDiscounts" real,"title" text,"voucherCode" text,"discountAmount" real,
"taxtitle" text,"taxRate" real,"taxAmount" real,"shippingTotalAmountAfterTaxAndDiscounts" real);
INSERT INTO shipments(order_id ,"city" ,"zipOrPostalCode" ,"providerDescriptor" ,
"shippingTotalAmountBeforeTaxAndDiscounts" ,"title" ,"voucherCode" ,"discountAmount" ,
"taxtitle" ,"taxRate" ,"taxAmount" ,"shippingTotalAmountAfterTaxAndDiscounts" )
VALUES (1,'Berlin','100203','DHL','100','Shipper','XMAS001',1000,'TAX 10%',10,500,1000),
(2,'Milan','122203','Hermes','100','Shipp_001','XMAS002',1000,'TAX 7%',7,500,1000);
create temp table customer( order_id int,"customer_id" int,"email" text,"customer_tags" text);
INSERT INTO customer(order_id ,"customer_id" ,"email" ,"customer_tags" )
VALUES (1,1900,'xxxx#gmail.com','new')
, (2,2000,'yyyy#gmail.com','return');
create temp table optional( order_id int,"googleAnalyticsTransactionId" int,"source" text,"channel" text,"platform" text);
INSERT INTO optional(order_id ,"googleAnalyticsTransactionId" ,"source" ,"channel" ,platform)
VALUES (1,'9990001','facebook','paid marketing','mobile')
,(2,'7770001','gppgle','direct','mobile');
Sample Expected Output
The output JSON is formulated with only one entry,With order id=1
{
"order_id": "1",
"createdAt": "2022-12-09",
"orderNumber": '10001',
"tags": ['no tags' ],
"lineItems": [
{
"line_id": "0",
"productName": 'Vitamin D',
"quantity": 100,
"productTags": [ 'Bio'],
"discounts": [
{
"title": 'Xmas campaign',
"voucherCode": 'XMAS001',
"discountAmount": 1000
}
],
"taxes": [
{
"tactitle": 'TAX 10%',
"taxRate": 10,
"taxAmount": 500
}
],
"totalAmountBeforeTaxesAndDiscounts": 15000,
"totalAmountAfterTaxesAndDiscounts": 14000
},
{
"line_id": "2",
"productName": 'Vitamin C',
"quantity": 50,
"productTags": [ 'No Tags'],
"discounts": [
{
"title": 'Xmas campaign',
"voucherCode": 'XMAS001',
"discountAmount": 500
}
],
"taxes": [
{
"tactitle": 'TAX 7%',
"taxRate": 7,
"taxAmount": 33
}
],
"totalAmountBeforeTaxesAndDiscounts": 1900,
"totalAmountAfterTaxesAndDiscounts": 1400
}
],
"shipping": {
"city": 'Berlin',
"zipOrPostalCode": "100203",
"providerDescriptor": "DHL",
"shippingTotalAmountBeforeTaxAndDiscounts": 100,
"discounts": [
{
"title": "TAX 10%",
"voucherCode": 'XMAS001',
"discountAmount": 500
}
],
"taxes": [
{
"title": "XMAS001",
"taxRate": 10,
"taxAmount": 500
}
],
"shippingTotalAmountAfterTaxAndDiscounts": 1000
},
"transactionCosts": 0.1,
"customer": {
"id": "1900",
"email": "xxxx#gmail.com",
"tags": [
"new"
]
},
"optional": {
"googleAnalyticsTransactionId": "9990001",
"orderSourceName": "facebook",
"orderChannelName": "paid marketing",
"orderPlatformName": "mobile"
}
}
I have written a part for you, which by looking at the code, you can understand how the functions work. I think you can write the continuation of the same by looking at this logic. If you have a problem, you can tell me, I will write it in full, I just didn't write all the names of the fields, so I'm sorry.
SQL query:
select json_build_object('lineItems',
json_agg(json_build_object(
'line_id', lt.line_id,
'productName', lt."productName",
'quantity', lt.quantity,
'discounts',
json_build_array(json_build_object(
'title', lt.vouchertitle,
'voucherCode', lt."voucherCode",
'discountAmount', lt."discountAmount")),
'taxes',
json_build_array(json_build_object(
'tactitle', lt.taxtitle,
'taxRate', lt."taxRate",
'taxAmount', lt."taxAmount")),
'totalAmountBeforeTaxesAndDiscounts', lt."totalAmountBeforeTaxesAndDiscounts",
'totalAmountAfterTaxesAndDiscounts', lt."totalAmountAfterTaxesAndDiscounts"))
)
from orders o
inner join lineitems lt on o.order_id = lt.order_id
inner join shipments sp on o.order_id = sp.order_id
where o.order_id = 1
And result:
{
"lineItems": [
{
"line_id": 0,
"productName": "Vitamin D",
"quantity": 100,
"discounts": [
{
"title": "Xmas campaign",
"voucherCode": "XMAS001",
"discountAmount": 1000
}
],
"taxes": [
{
"tactitle": "TAX 10%",
"taxRate": 10,
"taxAmount": 500
}
],
"totalAmountBeforeTaxesAndDiscounts": 15000,
"totalAmountAfterTaxesAndDiscounts": 14000
},
{
"line_id": 2,
"productName": "Vitamin C",
"quantity": 50,
"discounts": [
{
"title": "Xmas campaign",
"voucherCode": "XMAS001",
"discountAmount": 500
}
],
"taxes": [
{
"tactitle": "TAX 7%",
"taxRate": 7,
"taxAmount": 33
}
],
"totalAmountBeforeTaxesAndDiscounts": 1900,
"totalAmountAfterTaxesAndDiscounts": 1400
}
]
}

How to correctly index jsonb arrays and the fields in array elements in postgreSQL

a have a simple table of purchases consisting of id and jsonb column, like this:
CREATE TABLE purchase (id SERIAL, products JSONB)
Then and index:
CREATE INDEX idx_purchase_products ON purchase USING GIN (products);
The sample data are like this:
INSERT INTO purchase VALUES (
1, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
}
]')
),
(
2, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
},
{
"country": 1,
"type": 2,
"size": 12,
"color": 4
},
{
"country": 2,
"type": 1,
"size": 12,
"color": 3
}
]')
),
(
3, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
}
]')
),
(
4, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
},
{
"country": 1,
"type": 2,
"size": 12,
"color": 4
},
{
"country": 2,
"type": 1,
"size": 12,
"color": 3
}
]')
);
And some scenarios of searching:
SELECT *
FROM purchase
WHERE products #> '[{"country": 1}]'
SELECT *
FROM purchase
WHERE products #> '[{"country": 1, "type": 1}]'
SELECT *
FROM purchase
WHERE products #> '[{"size": 12}]'
SELECT *
FROM purchase
WHERE products #> '[{"size": 12, "color": 4}]'
It is expected, that the customer could search for combinations:
country,
country + type
country + type + size
country + type + size + color
country + size
size + color
type + color
etc.
And there is a big chance, the list of 4 field (country, type, size, color) will grow in future to 7-10.
And of course we want also search combinations like this:
.. WHERE products #> '[{"country": 1}]' OR products #> '[{"color": 4}]' OR products #> '[{"type": 1, "size": 10}]'
Estimated size of the table purchase is 9-12 millions rows (depending on season).
Any idea how to implement the indexes to get the query result as fast as possible?

Postgresql migrate data from jsonb array to jsonb

Let's say I have a column with this jsonb data :
{
"indicators": [
{
"year": 2019,
"indicatorsByYear": [
{
"value": 3120,
"code": "Nb_h"
},
{
"value": 18,
"code": "S_ppa"
},
{
"value": 95,
"code": "T_occ"
}
]
},
{
"year": 2020,
"indicatorsByYear": [
{
"value": 300,
"code": "Nb_h"
},
{
"value": 18,
"code": "S_ppa"
},
{
"value": 55,
"code": "T_occ"
}
]
}
],
"dataProvidedByUser": false
}
The idea is to migrate this column to a simplified object like this :
{
"indicatorsByYear": {
"2019": [
{
"value": 3120,
"code": "Nb_h"
},
{
"value": 18,
"code": "S_ppa"
},
{
"value": 95,
"code": "T_occ"
}
],
"2020": [
{
"value": 300,
"code": "Nb_h"
},
{
"value": 18,
"code": "S_ppa"
},
{
"value": 55,
"code": "T_occ"
}
]
},
"dataProvidedByUser": false
}
How can I transform the indicators array to map object with year as key and indicatorsByYear as value.
For info, the maximum number of years that I can have is 11 years (from 2010 to 2020), some columns have all the years others only some.
My attempts with something like that without success
update site
SET data = data
|| jsonb_build_object('indicatorsByYear',
jsonb_build_object(
data -> 'indicators' ->> 'year',
data -> 'indicators' ->> 'indicatorsByYear'
))
Any help would be very much appreciated! Thanks in advance.
data -> 'indicators' is an array, whose elements you need to consider individually and then aggregate back together into an object. You can use jsonb_array_elements and jsonb_object_agg respectively for this.
Also, you'll want to remove the old indicators key from the data column.
UPDATE site
SET data = jsonb_set(
data - 'indicators',
'{indicatorsByYear}',
(
SELECT jsonb_object_agg(el ->> 'year', el -> 'indicatorsByYear')
FROM jsonb_array_elements(data -> 'indicators') el
)
);

Search inside array of array in JSONB column in Postgresql

I have a JSONB column in my PostgreSQL database. The data looks like this:
{
"cars":
[
{
"id": 1,
"brand": "BMW"
"parts":
[
{
"partId": 5,
"type": "battery"
}
]
},
{
"id": 2,
"brand": "Mercedes"
"parts":
[
{
"partId": 5,
"type": "battery"
},
{
"partId": 6,
"type": "engine"
}
]
}
]
}
Is there any way that I can search for all cars that have a part with type "battery"? How can I search inside of cars array and then inside of the parts array of each car element?
As it's not clear in your question that what output you want. So I am assuming that you want id and brand name in output:
so you try this:
select distinct x.y->>'id', x.y->>'brand'
from test
cross join lateral jsonb_array_elements(data->'cars') x(y)
cross join lateral jsonb_array_elements(x.y->'parts') a(b)
where a.b->>'type'='battery'
DEMO

Select all values inside different arrays inside an array

I have a document that looks like this:
"userName": "sample name",
"values": [
{
"values": [
{
"brand": "SOLIGNUM CLEAR",
"name": "Solignum Colourless AZ",
"price": "569",
"qip": "30.00",
"sku": "1L",
"unit": "Piece"
}
]
},
{
"values": [
{
"brand": "FirePRO",
"name": "FirePRO",
"price": "419.75",
"qip": "30.00",
"sku": "1L",
"unit": "Cartons"
},
{
"brand": "SOLIGNUM AEROSOL",
"name": "Solignum Colourless AZ Aerosol",
"price": "397",
"qip": "30.00",
"sku": "500ML",
"unit": "Piece"
}
]
}
]
My query looks like this:
SELECT orders.unit, orders.sku, orders.name, orders.srp, TONUMBER(orders.price) AS price, orders.qip as quantity
FROM jdi stoCallLog
UNNEST stoCallLog.`values`[0].`values` AS orders
Query result looks like this
I have tried changing the unnest block into this:
UNNEST stoCallLog.`values`[1].`values` AS orders
selects only the 2nd array value
Also like this:
UNNEST stoCallLog.`values`.`values` AS orders
not possible i guess, it returns none
I need a way to select all of the values at once. Is there any way to do it?
Solved by modifying the UNNEST block to:
UNNEST `values` as rawOrders
UNNEST rawOrders.`values` as orders