Retrieving nested json information from a postgresql table - no current index - json

I'd like to retrieve nested json information from a jsonb field (sub_table) in a postgresql table (prices).
I'm able to retrieve the json using the command:
with jsontable as (
SELECT "sub_table"
FROM "prices"
WHERE "Scenario" = 'A' AND "data_type" = 'new'
)
SELECT * from jsontable
This returns a json table like this:
{
"0": {
"Name": "CompX",
"Price": 10,
"index": 1,
"Date": "2020-01-09T00:00:00.000Z"
},
"1": {
"Name": "CompY",
"Price": 20,
"index": 1,
"Date": "2020-01-09T00:00:00.000Z"
},
"2": {
"Name": "CompX",
"Price": 19,
"index": 2,
"Date": "2020-01-10T00:00:00.000Z"
}
}
I want to return all data relating to Name = "CompX" but can't get the query to work.
I've tried to follow the examples here, but can't work it out. Do I need to reindex in some way?

You can return extracted JSONB data row-wisely by using jsonb_each function with filtering out by (j.value -> 'Name')::text = '"CompX"' condition :
SELECT j.value
FROM prices p
CROSS JOIN jsonb_each(sub_table) AS j(e)
WHERE (j.value -> 'Name')::text = '"CompX"'
Demo

Related

N1QL Query to join array fields with an array in another document

I have 3 documents types :
Data
{
"formId": "7508e7b2-bcf7-437b-a206-9fee87256d01",
"dataValues": [
{
"questionId": "Someguid123",
"questionValue": "Question1"
},
{
"questionId": "Someguid",
"questionValue": "Question2"
},
{
"questionId": "AnotherGuid",
"questionValue": "Question3"
}
],
"lastUpdateDateTime": "2023-01-04T10:56:49Z",
"type": "Data",
"templateId": "41e4cc2c-e9fb-4bdc-9dc2-af19e5988984",
"creationDateTime": "2022-12-28T11:20:46Z"
}
AttachedDocuments
{
"id": "AttachedDocuments::77961b70-2071-4410-837a-436c908a4fa5",
"lastUpdateDateTime": "2023-01-05T11:47:17Z",
"documents": [
{
"isUploaded": false,
"id": "DocumentMetadata::001",
"isDeleted": false,
"type": "photo",
"parentId": "Someguid123"
},
{
"isUploaded": false,
"id": "DocumentMetadata::002",
"isDeleted": false,
"type": "photo",
"parentId": "Someguid123"
}
],
"type": "AttachedDocuments",
"parentDocId": "MyFormData::7508e7b2-bcf7-437b-a206-9fee87256d01",
"creationDateTime": "2022-12-28T11:20:46Z"
}
DocumentMetaData
{
"id": "DocumentMetadata::001",
"type": "DocumentMetadata",
"name": "MyForm_001.png",
"documentId": "549c4da2-ad3a-4f92-bfa2-019750a11007",
"contentType": "FILE",
"parentDocumentId": "AttachedDocuments::77961b70-2071-4410-837a-436c908a4fa5",
"creationDateTime": "2023-01-04T10:56:49Z"
},
{
"id": "DocumentMetadata::002",
"type": "DocumentMetadata",
"name": "MyForm_002.png",
"documentId": "549c4da2-ad3a-4f92-bfa2-019750a11007",
"contentType": "FILE",
"parentDocumentId": "AttachedDocuments::77961b70-2071-4410-837a-436c908a4fa5",
"creationDateTime": "2023-01-04T10:56:49Z"
}
Every Data type document has only one AttachedDocuments document with parentDocId* field set to formId field of Data document.
If items in Data.dataValues has a document attached to it, AttachedDocuments.documents array have items with parentId field set to Data.dataValues[i].questionId.
Also every AttachedDocuments.documents[i] item has a DocumentMetadata document with id of AttachedDocuments.documents[i].id field.
I want to have a query which returns all Data.dataValues as an array but containing a field links that contains the DocumentMetadata.name field like below :
[
{
"questionId": "Someguid123",
"questionValue": "Question1",
"links": ["MyForm_001.png", "MyForm_002.png"]
},
{
"questionId": "Someguid",
"questionValue": "Question2"
},
{
"questionId": "AnotherGuid",
"questionValue": "Question3"
}
]
I tried unnest clause but couldn't output datavalues items without documents. How should I write the query to include those also?
Thank you
Assuming you have a 1:1 relationship between Data & AttachedDocuments, you can try:
CREATE SCOPE default.f;
CREATE COLLECTION default.f.Data;
CREATE COLLECTION default.f.AttachedDocuments;
CREATE COLLECTION default.f.DocumentMetaData;
CREATE INDEX ix1 ON default.f.DocumentMetaData(id);
SELECT dataValues.questionId, dataValues.questionValue, links
FROM default.f.Data join default.f.AttachedDocuments ON "MyFormData::"||Data.formId = AttachedDocuments.parentDocId
UNNEST Data.dataValues AS dataValues
LET links = (SELECT RAW DocumentMetaData.name
FROM default.f.DocumentMetaData
WHERE DocumentMetaData.parentDocumentId = AttachedDocuments.id
AND id IN ARRAY a.id FOR a IN AttachedDocuments.documents WHEN a.parentId = dataValues.questionId END
)
;
If you have a 1:n relationship between Data & AttachedDocuments but the attachments for a single question are wholly in a single attached document:
CREATE INDEX ix2 ON default.f.AttachedDocuments(parentDocId);
CREATE INDEX ix3 ON default.f.AttachedDocuments(id);
SELECT dataValues.questionId, dataValues.questionValue, links
FROM default.f.Data join default.f.AttachedDocuments ON "MyFormData::"||Data.formId = AttachedDocuments.parentDocId
UNNEST Data.dataValues as dataValues
LET links = (SELECT RAW md.name
FROM default.f.AttachedDocuments ad JOIN default.f.DocumentMetaData md ON ad.id = md.parentDocumentId
UNNEST ad.documents d
WHERE ad.parentDocId = "MyFormData::"||Data.formId
AND d.id = md.id
AND d.parentId = dataValues.questionId
)
WHERE ANY dv IN AttachedDocuments.documents SATISFIES dv.parentId = dataValues.questionId END
;
If attachments for a single question can be spread over multiple attached documents, add a DISTINCT to the above statement.
HTH.
(You can use the same logic without collections adding appropriate aliasing and type field filtering.)

TSQL how to merge two Json arrays

How to merge new array elements into an existing Json property?
Our two json objects look like this and we want to append the items of the 2nd json object to the 1st json object. (See desired result)
Is there a way to achieve this with JSON_MODIFY?
The microsoft documentation doesn't really show any example of multiple elements being merged into the already existing array. Only a single element. But we have a list of multiple elements that need to be merged.
Edit:
JSON_MODIFY(#json1, 'append $.Items', JSON_QUERY(#json2)) seems to create new array brackets instead of merging the items into the array.
Json Object 1:
DECLARE #json1 NVARCHAR(MAX) = '{
"id": 1,
"Items": [
{
"id": 1,
"name" : "Item #1"
},
{
"id": 2,
"name" : "Item #2"
}
]
}'
Json Object 2:
DECLARE #json2 NVARCHAR(MAX)='{
"Items": [
{
"id": 3,
"name": "Item #3"
},
{
"id": 4,
"name": "Item #4"
}
]
}'
Desired Result:
{
"id": 1,
"Items": [
{
"id": 1,
"name": "Item #1"
},
{
"id": 2,
"name": "Item #2"
},
{
"id": 3,
"name": "Item #3"
},
{
"id": 4,
"name": "Item #4"
}
]
}
It's Cumbersome but possible to achieve with SQL Server's built it JSON support.
First, set proper sample data (Please save us this step in your future questions):
DECLARE #Json1 nvarchar(max) =
'{
"id": 1,
"Items": [
{
"id": 1,
"name" : "Item #1"
},
{
"id": 2,
"name" : "Item #2"
}
]
}',
#Json2 nvarchar(max) =
'{
"Items": [
{
"id": 3,
"name": "Item #3"
},
{
"id": 4,
"name": "Item #4"
}
]
}';
Then, wrap a union all query containing openjson and json_query for each one of the variables with a common table expression:
With cteArray as
(
SELECT *
FROM OPENJSON(JSON_QUERY(#Json1, '$.Items'))
WITH(
Id int '$.id',
Name varchar(100) '$.name'
)
UNION ALL
SELECT *
FROM OPENJSON(JSON_QUERY(#Json2, '$.Items'))
WITH(
Id int '$.id',
Name varchar(100) '$.name'
)
)
The result of that union all query is this:
Id Name
1 Item #1
2 Item #2
3 Item #3
4 Item #4
Then, select the id from the first json using json_value, and add a subquery to select everything from the cte with for json path. Add another for json path and specify without_array_wrapper to the outer query:
SELECT JSON_VALUE(#Json1, '$.id') As id,
(
SELECT *
FROM cteArray
FOR JSON PATH
) as Items
FOR JSON PATH,
WITHOUT_ARRAY_WRAPPER
The final result:
{
"id": "1",
"Items": [{
"Id": 1,
"Name": "Item #1"
}, {
"Id": 2,
"Name": "Item #2"
}, {
"Id": 3,
"Name": "Item #3"
}, {
"Id": 4,
"Name": "Item #4"
}
]
}
You can see a live demo on Db<>Fiddle
Your problem can be solved by
SELECT dbo.udf_native_json_merge(#json1,#json2,null)
We faced similar issues trying to merge JSONs in MS SQL. We also wanted it to be recursive and allow us to define a strategy for arrays like "union", "concat" and "replace".
Our solution for JSON manipulations like merge, JSON path expressions and more is open source and available # Github
Feel free to use, comment and contribute so we can further improve JSON methods for MS SQL.
Not pretty but this will merge text and object elements. I'm sure it isn't bullet proof. Offered only as a potential solution.
Declare #json1 nvarchar(max) = '{"id":1,"messages":[{"type":"Info","text":"message1"},{"type":"Info","text":"message2"}]}'
Declare #json2 nvarchar(max) = '{"id":1,"messages":["justPlanText",{"type":"Info","text":"message3"},{"type":"Info","text":"message4"}]}'
select #json1 = case
when isjson(m.value) = 1 then
json_modify(#json1,'append $.messages',json_query(m.value))
else
json_modify(#json1,'append $.messages',m.value)
end
from openjson(json_query(#json2,'$.messages')) m
select #json1
Results for #json1:
{"id":1,"messages":[{"type":"Info","text":"message1"},{"type":"Info","text":"message2"},"justPlanText",{"type":"Info","text":"message3"},{"type":"Info","text":"message4"}]}

How to query to JSON column in a postgres table , to retrieve all the values from all json for a particular json key

We are using postgres , in that we had a table containing column of type of JSON , in that we have below type of JSON documents ,
please find sample here
i want all the values of json key student_id from all the JSON docum
i mean we have such documents , for each row for that column and each document contains contains that JSON key , am trying to get all the values for that json key from all the documents(all rows for that column)
You are probably looking for json_array_elements.
Find everything you need in the json functions page.
For your sample you could do something like this:
select json_array_elements(json_array_elements('{
"srs_student_information": {
"header": {
"name": "kkkk"
},
"beginning_segment": {
"age": 12
},
"loop_id_sls": [{
"student_level_details": {
"class": "12"
},
"parent_details": [{
"name": "assa"
}],
"student_identification": [{
"student_id_qual": "BM",
"student_id": "00547311"
}, {
"student_id_qual": "CN",
"student_id": "467931496024"
}, {
"student_id_qual": "CN",
"student_id": "467931496035"
}, {
"student_id_qual": "CN",
"student_id": "467931496046"
}]
}]
}
}'::json->'srs_student_information'->'loop_id_sls')->'student_identification')->>'student_id' student_id

Add new fields to nested JSON array in JSONB

I have a nested JSON structure stored in a PostgreSQL table.
Table users:
id | content [JSON]
JSON:
{
"purchases": [
{
"id": 1,
"costs": [
{
"amount": 23
},
{
"amount": 34
}
]
},
{
"id": 2,
"costs": [
{
"amount": 42
}
]
}
]
}
I would like to add a field "jsonClass": "Static" to all the objects within the costs array so I have following in the end:
{
"purchases": [
{
"id": 1,
"costs": [
{
"jsonClass": "Static",
"amount": 23
},
{
"jsonClass": "Static",
"amount": 34
}
]
},
{
"id": 2,
"costs": [
{
"jsonClass": "Static",
"amount": 42
}
]
}
]
}
I couldn't figure out how to add values to such a nested structure. Anyone knows how to achieve such thing? The only way I found was to make it a text and do string replace which is not very performant and I have a lot of such entries.
Unfortunately, due to having to change multiple sub-objects, I don't know of a better way than to deconstruct and then reconstruct the object. It gets pretty hairy.
UPDATE users
SET content=(
SELECT jsonb_agg(purchase)
FROM (
SELECT jsonb_build_object('id', pid, 'purchases', jsonb_agg(cost)) AS purchase
FROM (
SELECT pid, cost || '{"jsonClass":"static"}'::jsonb AS cost
FROM (
SELECT purchase->'id' AS pid, jsonb_array_elements(purchase->'costs') AS cost
FROM jsonb_array_elements(content::jsonb->'purchases') AS purchase
) AS Q
) AS R
GROUP BY pid
) AS S
);
Fiddle
EDIT: Sorry about all the edits, forgot to test for multiple rows. Should be good now. It might be possible to simplify it a bit more, not sure.

How to search nested JSON in MySQL

I am using MySQL 5.7+ with the native JSON data type. Sample data:
[
{
"code": 2,
"stores": [
{
"code": 100,
"quantity": 2
},
{
"code": 200,
"quantity": 3
}
]
},
{
"code": 4,
"stores": [
{
"code": 300,
"quantity": 4
},
{
"code": 400,
"quantity": 5
}
]
}
]
Question: how do I extract an array where code = 4?
The following (working) query has the position of the data I want to extract and the search criterion hardcoded:
SELECT JSON_EXTRACT(data_column, '$[0]')
FROM json_data_table
WHERE data_column->'$[1].code' = 4
I tried using a wildcard (data_column->'$[*].code' = 4) but I get no results in return.
SELECT row FROM
(
SELECT data_column->"[*]" as row
FROM json_data_table
WHERE 4 IN JSON_EXTRACT(data_column, '$[*].code')
)
WHERE row->".code" = 4
... though this would be much easier to work with if this wasn't an unindexed array of objects at the top level. You may want to consider some adjustments to the schema.
Note:
If you have multiple rows in your data, specifying "$[i]" will pick that row, not the aggregate of it. With your dataset, "$[1].code" will always evaluate to the value of code in that single row.
Essentially, you were saying:
$ json collection
[1] second object in the collection.
.code attribute labeled "code".
... since there will only ever be one match for that query, it will always eval to 4...
WHERE 4 = 4
Alternate data structure if possible
Since the entire purpose of "code" is as a key, make it the key.
[
"code2":{
"stores": [
{
"code": 100,
"quantity": 2
},
{
"code": 200,
"quantity": 3
}
]
},
"code4": {
"stores": [
{
"code": 300,
"quantity": 4
},
{
"code": 400,
"quantity": 5
}
]
}
]
Then, all it would require would be:
SELECT datacolumn->"[code4]" as code4
FROM json_data_table
This is what you are looking for.
SELECT data_column->'$[*]' FROM json_data_table where data_column->'$[*].code' like '%4%'.
The selected data will have [] around it when selecting from an array thus data_column->'$[*].code' = 4 is not possible.