How to format & design the JSON format in Postgres - postgresql
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
}
]
}
Related
POSTGRES jsonb document GIN Indexes are created on what all objects?
I am using Postgres DB 13.5. From pgdocs - The technical difference between a jsonb_ops and a jsonb_path_ops GIN index is that the former creates independent index items for each key and value in the data, while the latter creates index items only for each value in the data. Basically, each jsonb_path_ops index item is a hash of the value and the key(s) leading to it; for example to index {"foo": {"bar": "baz"}} Understanding the above in detail is important for me coz my jdata (document) is big with many keys and nested objects. Consider my json data that is stored as jsonb in a column named jdata looks like below - { "supplier": { "id": "3c67b6eb-3b0d-492d-8736-66df107b83b3", "customer": { "type": "pro", "name": "John George", "address": [ { "add-id": "098ad4df-2a90-4fda-8f92-dbe8d7196732", "addressActive": true, "street": "abc street", "zip": 94044, "staying-since": "long long", "accessibility": { "traffic": "heavy/congested", "bestwaytoreach": { "weekdays": { "bart/metro/calltrain": true, "price": { "off-peak-hours": "affordable", "peak-hours": "high" }, "journey-time": "super-fast" } }, "weekends": { "byroad": { "ok": true, "distance": "long", "has-tolls": { "true": true, "toll-price": "relatively-high" }, "journey-speed": "fast" } } } }, { "add-id": "ddd1d2a0-9050-4bcf-a3ad-2e608d65e468", "addressActive": true, "street": "xyz street", "zip": 10001, "staying-since": "moved recently", "accessibility": { "traffic": "heavy/congested", "bestwaytoreach": { "weekdays": { "subway": true, "price": { "off-peak-hours": "affordable", "peak-hours": "high" }, "journey-speed": "super-fast" } }, "weekends": { "byroad": { "ok": true, "distance": "moderate", "tolls": { "has-tolls": true, "toll-price": "relatively-high" }, "journey-time": "super-fast" } } } } ], "firstName": "John", "lastName": "CRAWFORD", "emailAddresses": { "personal": [ "johnreplies#jg.com", "ursjohn#jg.com", "1234#jg.com" ], "official": [ { "repies-in": "1 day", "email": "jg#jg.com" }, { "check's regularly": true, "repies-in": "1 Hour", "email": "jg-watching#jg.com" } ] }, "cities": [ "NYC", "LA", "SF", "DC" ], "splCustFlag": null, "isPerson": true, "isEntity": false, "allowEmailSolicit": "Y", "allowPhoneSolicit": "Y", "taxPayer": true, "suffix": null, "title": null, "birthDate": "05/10/1993", "loyaltyPrograms": null, "phoneNumbers-summary": [ 1234567890, 1234567899, 1234567898, 1234567897 ], "phoneNumbers": [ { "description": null, "extension": null, "number": 1234567890, "countryCode": null, "type": "Business" }, { "description": null, "extension": null, "number": 1234567899, "countryCode": null, "type": "Home" } ], "data-privacy": { "required": true, "laws": [ "CCPA", "GDPR" ] } } } } Now if I create GIN jsonb_ops index for jdata column - I want to clarify what all keys and values will be part of index. For example - "staying-since" is a key nested at below path and it's part of "address" array too. But it's still a key, thought nested deep in the document. So will it be part of the index. { "supplier": { "customer": { "address": [ { "staying-since": "long long" ... And similarly "long long" is a value of a deeply nested key. Will it also be indexed. And if GIN jsonb_path_ops index is created for jdata column -- Will a hash of "long long" value along with the path that leads to it will also be indexed. hash( "supplier": { "customer": { "address":[{"staying-since": "long long"}] } } ) will the above also gets index. I am aware about the operators that are supported by the GIN index types and am aware about the usage of these operators - jsonb_ops ? ?& ?| #> #? ## jsonb_path_ops #> #? ##
Read JSON in ADF
In Azure Data Factory, I need to be able to process a JSON response. I don't want to hardcode the array position in case they change, so something like this is out of the question: #activity('Place Details').output.result.components[2].name How can I get the name 123 where types = number given a JSON array like below: "result": { "components": [ { "name": "ABC", "types": [ "alphabet" ] }, { "name": "123", "types": [ "number" ] } ] }
One example using the OPENJSON method: DECLARE #json NVARCHAR(MAX) = '{ "result": { "components": [ { "name": "ABC", "types": [ "alphabet" ] }, { "name": "123", "types": [ "number" ] } ] } }' ;WITH cte AS ( SELECT JSON_VALUE( o.[value], '$.name' ) [name], JSON_VALUE( o.[value], '$.types[0]' ) [types] FROM OPENJSON( #json, '$.result.components' ) o ) SELECT [name] FROM cte WHERE types = 'number' I will have a look at other methods.
How to exclude some item when query with typeorm in Nestjs and Postgres
I want to build a query when i send username to server so i can exclude the record has the excludeUsers has the same username i sent. Here is my data : ` "id": "f4830220-9912-4cbb-b685-edf4aaaf8fd5", "createdAt": "2022-03-24T10:19:48.096Z", "updatedAt": "2022-03-24T10:26:42.487Z", "isDeleted": false, "username": "vietphuongthoa98", "link": "", "type": "follow", "current": 13456, "target": 13556, "totalPurchase": 100, "purchasedPacks": [ { "id": "fbb3079b-32c9-4c4b-a297-16741d1f5485", "name": "Some packets", "type": "follow", "count": 2, "price": 100, "follows": 50, "createdAt": "2022-03-23T07:18:22.898Z", "isDeleted": false, "updatedAt": "2022-03-23T09:49:30.192Z", "description": "Some packages description.", "originPrice": 0 } ], "excludeUsers": [ { "id": "9d8c25d2-8f03-46b3-92fa-b9489b943a56", "deviceName": "iPhone 12 Pro Max", "username": "hoaa.hanassii", "platform": "ios", "timeZone": 0, "deviceAge": 21, "subscribed": true, "coins": 19600, "subscriptionExpiration": "2022-04-27T00:00:00.000Z", "isBlocked": false } ] Here is my query builder : queryBuilder .leftJoin('featured_user.excludeUsers', 'excludeUsers') .andWhere('excludeUsers.username != :username', { username: filter.username, }); The actual query result is : SELECT "featured_user"."id" AS "featured_user_id", "featured_user"."tenant_id" AS "featured_user_tenant_id", "featured_user"."created_at" AS "featured_user_created_at", "featured_user"."updated_at" AS "featured_user_updated_at", "featured_user"."is_deleted" AS "featured_user_is_deleted", "featured_user"."username" AS "featured_user_username", "featured_user"."link" AS "featured_user_link", "featured_user"."type" AS "featured_user_type", "featured_user"."current" AS "featured_user_current", "featured_user"."target" AS "featured_user_target", "featured_user"."total_purchase" AS "featured_user_total_purchase", "featured_user"."purchased_packs" AS "featured_user_purchased_packs", "featured_user"."user_id" AS "featured_user_user_id", "excludeUsers"."username" AS "excludeUsers_username", "excludeUsers"."id" AS "excludeUsers_id" FROM "featured_users" "featured_user" LEFT JOIN "featured_users_exclude_users_users" "featured_user_excludeUsers" ON "featured_user_excludeUsers"."featured_users_id"="featured_user"."id" LEFT JOIN "users" "excludeUsers" ON "excludeUsers"."id"="featured_user_excludeUsers"."users_id" WHERE "excludeUsers"."username" != $1 -- PARAMETERS: ["hoaa.hanassii"] The problem is nothing response. Any ideal ? Thanks
can you add getMany() end of querybuilder and try queryBuilder .leftJoin('featured_user.excludeUsers', 'excludeUsers') .andWhere('excludeUsers.username != :username', { username: filter.username, }).getMany()
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 ) );
Can't sort by all array's items?
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 ...