Querying for an array object in Postgres jsonb column - arrays

I have a Postgres table with 2 columns "nodes" & "timestamp".The "nodes" column is of type jsonb & is an array of objects of the following format:
[
{
"addr": {},
"node_number": "1",
"primary": false
},
{
"addr": {},
"node_number": "2",
"primary": true
},
]
I want to find the object in this array that has "primary":true in the most recent row. If the above was the latest row, the result should be:
{
"addr": { },
"node_number": "2",
"primary": true
}
I have tried:
SELECT(nodes -> 0) FROM table WHERE nodes #> '[{"primary": true}]'
order by timestamp desc
limit 1;
which gives the object at index 0 in the array not the desired object that has "primary": true.
How can I implement the query ?

Use jsonb_array_elements() in a lateral join:
select elem
from my_table
cross join jsonb_array_elements(nodes) as elem
where (elem->>'primary')::boolean
elem
---------------------------------------------------
{"addr": {}, "primary": true, "node_number": "2"}
(1 row)

Related

Postgres Jsonb — Add key and value to objects in a jsonb array if they don't exist based on a condition

I have a table, let's call it myTable with the following structure
org
data
text
jsonb
The data in the jsonb field is an array structured in the following way:
[
{
"type": "XYZ",
"valueA": "500",
"valueB": "ABC",
},
{
"type": "ABC",
"valueA": "300",
"valueB": "CDE",
}
]
What I want to do is transform that data by checking if an object in the array has a "type" key with a value of "XYZ" and if true, adding a valueC key.
valueC's value will be an array of strings. The values of the array will depend on the value of the org column.
I want to do this for all rows such that if a specific org is present, and the jsonb array in the data column contains an object with "type": "XYZ", then I get this result:
[
{
"type": "XYZ",
"valueA": "500",
"valueB": "ABC",
"valueC": ["SOMETHING"],
},
{
"type": "ABC",
"valueA": "300",
"valueB": "CDE",
}
]
I also want to ensure this script only runs if valueC is not present in the object that matches the conditions, so it is not re-run during a migration/rollback unless needed.

Querying list in a Snowflake VARIANT file

I have a VARIANT table that has many JSON files but, for the sake of the example and to illustrate my issue, let's look at only the two rows below.
{
"id" : "1",
"fields":
[
{
"id": "somekey1",
"value" : "value1"
},
{
"id": "somekey2",
"value" : "value2"
}
]
},
{
"id" : "2",
"fields":
[
{
"id": "somekey1",
"value" : "value1"
},
{
"id": "somekey2",
"value" : "value2"
},
{
"id": "somekey3",
"value" : "value3"
}
]
}
I want to write a query that would give me this output:
ID
VALUES
1
["value1","value2","value3"]
2
["value1","value2"]
I have tried many things and this query gave me something of a result but not remotely close to the desired output:
SELECT
file:id as ID,
s.value:value::varchar as VALUES
from variant_table,
table(flatten(FILE:fields)) s
And the result is below, plus it omits the JSON if the fields is empty:
ID
VALUES
1
"value1"
1
"value2"
1
"value3"
2
"value1"
2
"value2"
What would be the best approach to solve this in Snowflake?
After flattening, this will turn the rows into arrays for the VALUES column:
SELECT
file:id::int as ID,
array_agg(s.value:value::varchar) as "VALUES"
from variant_table,
table(flatten(FILE:fields)) s
group by ID

How to retrieve all child nodes from JSON file

I have below JSON file, which is in the external stage, I'm trying to write a copy query into the table with the below query. But it's fetching a single record from the node "values" whereas I need to insert all child elements for the values node. I have loaded this file into a table with the variant datatype.
The query I'm using:
select record:batchId batchId, record:results[0].pageInfo.numberOfPages NoofPages, record:results[0].pageInfo.pageNumber pageNo,
record:results[0].pageInfo.pageSize PgSz, record:results[0].requestId requestId,record:results[0].showPopup showPopup,
record:results[0].values[0][0].columnId columnId,record:results[0].values[0][0].value val
from lease;
{
"batchId": "",
"results": [
{
"pageInfo": {
"numberOfPages": ,
"pageNumber": ,
"pageSize":
},
"requestId": "",
"showPopup": false,
"values": [
[
{
"columnId": ,
"value": ""
},
{
"columnId": ,
"value":
}
]
]
}
]
}
you need to use the LATERAL FLATTEN functions, something like this:
I created this table:
create table json_test (seq_no integer, json_text variant);
and then populated it with this JSON string:
insert into json_test(seq_no, json_text)
select 1, parse_json($${
"batchId": "a",
"results": [
{
"pageInfo": {
"numberOfPages": "1",
"pageNumber": "1",
"pageSize": "100000"
},
"requestId": "a",
"showPopup": false,
"values": [
[
{
"columnId": "4567",
"value": "2020-10-09T07:24:29.000Z"
},
{
"columnId": "4568",
"value": "2020-10-10T10:24:29.000Z"
}
]
]
}
]
}$$);
Then the following query:
select
json_text:batchId batchId
,json_text:results[0].pageInfo.numberOfPages numberOfPages
,json_text:results[0].pageInfo.pageNumber pageNumber
,json_text:results[0].pageInfo.pageSize pageSize
,json_text:results[0].requestId requestId
,json_text:results[0].showPopup showPopup
,f.value:columnId columnId
,f.value:value value
from json_test t
,lateral flatten(input => t.json_text:results[0]:values[0]) f;
gives these results - which I think is roughly what you are looking for:
BATCHID NUMBEROFPAGES PAGENUMBER PAGESIZE REQUESTID SHOWPOPUP COLUMNID VALUE
"a" "1" "1" "100000" "a" false "4567" "2020-10-09T07:24:29.000Z"
"a" "1" "1" "100000" "a" false "4568" "2020-10-10T10:24:29.000Z"

SNOWFLAKE querying the array of elements

I am using SNOW_FLAKE and trying to query the data stored in the form of array of elements under column name nested_colmn as example:
nested_colmn
[
{
"firstKey": "val1",
"secondKey": 2555,
"thirdKey": false,
"fourthkey": "otrvalue"
},
{
"firstKey": "val2",
"secondKey": 255221,
"thirdKey": true,
"fourthkey": "otrvalu"
}
]
The above Array gets returned as one complete row if I do
Select nested_colmn from table_name
Now I want to query/get the results only for the firstkey(nested_colmn.firstkey) from the Attributes column. How do I frame the query to be to retrieve the individual custom elements from an array instead of getting all. Please help me if any thoughts on this
Note: I will assume that you truly want the source table to have the array as a value, instead of stripping the outer array and placing each element into its own row.
First, create a test table with your sample data:
CREATE OR REPLACE TEMPORARY TABLE table_name (
nested_colmn VARIANT
)
AS
SELECT PARSE_JSON($1) AS nested_colmn
FROM VALUES
($$
[
{
"firstKey": "val1",
"secondKey": 2555,
"thirdKey": false,
"fourthkey": "otrvalue"
},
{
"firstKey": "val2",
"secondKey": 255221,
"thirdKey": true,
"fourthkey": "otrvalu"
}
]
$$)
;
With that, here is a sample query:
SELECT F.VALUE:"firstKey"::VARCHAR AS FIRST_KEY
FROM table_name T
,LATERAL FLATTEN(nested_colmn) F
;
You're going to need to run a lateral flatten on the array and then parse the JSON:
WITH x as (
SELECT array_construct(parse_json('
{
"firstKey": "val1",
"secondKey": 2555,
"thirdKey": false,
"fourthkey": "otrvalue"
}'),parse_json('
{
"firstKey": "val2",
"secondKey": 255221,
"thirdKey": true,
"fourthkey": "otrvalu"
}')) as var)
SELECT p.value:firstKey::varchar
FROM x,
lateral flatten(input => x.var) p;

How can I update a multi level nested array in MongoDB?

How can I update a record in a document with multiple levels of array nesting?
My document structure is the following:
{
"_id": "5bfa09f0a0441f38d45dcc9c",
"nombre": "PROYECTO MAIN",
"area": "Sistemas",
"fecha": "27/01/2018",
"reuniones": [
{
"_id": "5bfa09f0a0441f38d45dcc99",
"objetivo": "Objetivo MODIFICADO",
"fecha": "25/10/2018",
"participantes": [
{
"nomina": 1,
"nombre": "MODIFICADO",
"rol": "rol",
"area": "area",
"firma": "url/imagen.jpg"
},
{
"nomina": 2,
"nombre": "nombre 2",
"rol": "rol",
"area": "area",
"firma": "url/imagen.jpg"
}
]
}
],
"_class": "proyecto"
}
Using the following query, returns me the document mentioned above.
db.proyectos.find({
_id:ObjectId("5bfa09f0a0441f38d45dcc9c"),
"reuniones._id":ObjectId("5bfa09f0a0441f38d45dcc99"),
"reuniones.participantes.nomina":2
})
I want to update the firma field of participant with nomina 2.
Since Mongo 3.6, you can update multi-nested arrays by combining the following operators:
$set (to update a specific field)
$[] (to match any item in an array)
$[<identifier>] (to match specific items in an array)
Example
Here's how you can update a specific proyectos document that has a reuniones array that has a participantes array that has an object with the field nomina equal to 2:
// update a specific proyectos document
// that has a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2
db.proyectos.update({
_id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
$set: {
"reuniones.$[].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"j.nomina": 2
}
]
})
If you wanted to limit your query to a specific reunion, you can do:
db.proyectos.update({
_id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
$set: {
"reuniones.$[i].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"i._id": ObjectId("5bfa09f0a0441f38d45dcc99")
},
{
"j.nomina": 2
}
]
})
To update all proyectos satisfying the above condition, just omit the _id query:
// update all proyectos
// that have a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2
db.proyectos.update({}, {
$set: {
"reuniones.$[].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"j.nomina": 2
}
]
})

Resources