Postgres queries for JSON Array - arrays

I have a table called cust_data which stores id and JSON object. I want to write postgres select statements to fetch:
select all id's where "gender": "Female" is not present in persons array [this should return id#3 from below data]
select all id's where "gender": "Female" is present and "status":"married" [this should return id#2 from below data]
Table : cust_data
id(numeric) | connections (jsonb)
------------------------------
1, {"Persons": [
{
"personName": "Tom",
"gender": "Male",
"country": "USA",
"status":"single"
},
{
"personName": "Harry",
"gender": "Male",
"country": "USA",
"status":"single"
},
{
"personName": "Lisa",
"gender": "Female",
"country": "Mexico",
"status":"single"
}
]
}
2,{
"Persons": [
{
"personName": "Lisa",
"gender": "Male",
"country": "UK",
"status":"single"
},
{
"personName": "Harry",
"gender": "Male",
"country": "USA",
"status":"single"
},
{
"personName": "Lisa",
"gender": "Female",
"country": "Mexico",
"status":"married"
}
]
}
3,{
"Persons": [
{
"personName": "Lisa",
"gender": "Male",
"country": "UK",
"status":"single"
},
{
"personName": "Harry",
"gender": "Male",
"country": "USA",
"status":"single"
}
]
}

You can use boolean aggregate functions:
select id
from cust_data,
lateral jsonb_array_elements(connections->'Persons')
group by 1
having not bool_or(value->>'gender' = 'Female');
id
----
3
(1 row)
select id
from cust_data,
lateral jsonb_array_elements(connections->'Persons')
group by 1
having bool_or(value->>'gender' = 'Female' and value->>'status' = 'married');
id
----
2
(1 row)
Test it here.
If the arrays may be empty you should use left join ... on true instead of lateral. Add also coalesce() with appropriate default value for aggregates as they can yield null, e.g.:
select id
from cust_data
left join jsonb_array_elements(connections->'Persons') on true
group by 1
having not coalesce(bool_or(value->>'gender' = 'Female'), false);

Query for 1:
WITH test AS (
SELECT id, jsonb_array_elements(t.connections->'Persons') AS elem
FROM cust_data t
), findFemale AS (
SELECT DISTINCT id FROM test
WHERE elem->>'gender' = 'Female'
)
SELECT id FROM cust_data
WHERE id NOT IN (select * from findFemale)
Query for 2:
WITH test as (SELECT id, jsonb_array_elements(t.connections->'Persons') AS elem
from cust_data t
) , findFemaleMarried as (
select distinct id from test
where
elem ->> 'gender' = 'Female' and elem ->> 'status' = 'married'
)
select * from findFemaleMarried
I hope above query will solve your problem.

Related

Permanently changing column name in JSON SNOWFLAKE

2 { "country": "England", "id": "100200", "status": "morestatus" }
3 { "country": "Netherlands", "id": "100300", "status": "morestatus" }
1 { "country": "UK", "id": "100100", "status": "somestatus" }
how to change the country to COUNTRY permanently with update command:
SET T.RECORD = object_delete(object_insert(RECORD, 'COUNTRY', RECORD:country), 'country')
WHERE T.RECORD:country
You can nest object_insert inside object_delete to do this.
create or replace temp table t1 as select parse_json('{ "country": "England", "id": "100200", "status": "morestatus" }') as V;
insert into t1 select parse_json('{ "country": "Netherlands", "id": "100300", "status": "morestatus" }');
insert into t1 select parse_json('{ "country": "UK", "id": "100100", "status": "somestatus" }');
update t1 set v = object_delete(object_insert(v, 'COUNTRY', v:country), 'country');

Json data flattening on snowflake

I'm trying to flatten below Json data on snowflake :
Json Data :
{
"empDetails": [
{
"kind": "person",
"fullName": "John Doe",
"age": 22,
"gender": "Male",
"phoneNumber": {
"areaCode": "206",
"number": "1234567"
},
"children": [
{
"name": "Jane",
"gender": "Female",
"age": "6"
},
{
"name": "John",
"gender": "Male",
"age": "15"
}
],
"citiesLived": [
{
"place": "Seattle",
"yearsLived": [
"1995"
]
},
{
"place": "Stockholm",
"yearsLived": [
"2005"
]
}
]
},
{
"kind": "person",
"fullName": "Mike Jones",
"age": 35,
"gender": "Male",
"phoneNumber": {
"areaCode": "622",
"number": "1567845"
},
"children": [
{
"name": "Earl",
"gender": "Male",
"age": "10"
},
{
"name": "Sam",
"gender": "Male",
"age": "6"
},
{
"name": "Kit",
"gender": "Male",
"age": "8"
}
],
"citiesLived": [
{
"place": "Los Angeles",
"yearsLived": [
"1989",
"1993",
"1998",
"2002"
]
},
{
"place": "Washington DC",
"yearsLived": [
"1990",
"1993",
"1998",
"2008"
]
},
{
"place": "Portland",
"yearsLived": [
"1993",
"1998",
"2003",
"2005"
]
},
{
"place": "Austin",
"yearsLived": [
"1973",
"1998",
"2001",
"2005"
]
}
]
},
{
"kind": "person",
"fullName": "Anna Karenina",
"age": 45,
"gender": "Female",
"phoneNumber": {
"areaCode": "425",
"number": "1984783"
},
"citiesLived": [
{
"place": "Stockholm",
"yearsLived": [
"1992",
"1998",
"2000",
"2010"
]
},
{
"place": "Russia",
"yearsLived": [
"1998",
"2001",
""
]
},
{
"place": "Austin",
"yearsLived": [
"1995",
"1999"
]
}
]
}
]
}
I'm able to flatten the most of the data except for column/array years Lived,
for last column I'm getting null values.
below is what I have tried so far :
select empd.value:kind,
empd.value:fullName,
empd.value:age,
empd.value:gender,
empd.value:phoneNumber,
empd.value:phoneNumber.areaCode,
empd.value:phoneNumber.number ,
empd.value:children,
chldrn.value:name,
chldrn.value:gender,
chldrn.value:age,
city.value:place,
yr.value:yearsLived
from my_json emp,
lateral flatten(input=>emp.Json_data:empDetails) empd ,
lateral flatten(input=>empd.value:children, OUTER => TRUE) chldrn,
lateral flatten(input=>empd.value:citiesLived) city,
lateral flatten(input=>city.value:yearsLived) yr -- not getting data for
this array
can someone help me understand why I'm getting null values for yearsLived array ? I'm not sure if I'm missing anything here
Your query returns the column
yr.value:yearsLived
as if yr.value were an OBJECT with fields.
But you have already expanded the yearsLived field in the line
lateral flatten(input=>city.value:yearsLived) yr
so yr.value is really just a VARIANT containing the year. You can leave it as such—or perhaps wrap it in TO_NUMBER or TO_VARCHAR to have a more precise type.
Why don't you try this out.
create or replace table json_tab as
select parse_json('{ "place": "Austin","yearsLived": [ "1995","1999"]}') as years
select years:yearsLived[0]::int from json_tab
Since your JSON data is an array, you need to access the elements via index if you would like to get specific values or use any array function to explode it.
with flatten function
select years, v.value::string
from json_tab,
lateral flatten(input =>years:yearsLived ) v;

Flatten JSON Data on snowflake

below is the Json data I'm trying to Flatten on snowflake
Json Document :
[
"empDetails": [
{
"kind": "person",
"fullName": "John Doe",
"age": 22,
"gender": "Male",
"phoneNumber": {
"areaCode": "206",
"number": "1234567"
},
"children": [
{
"name": "Jane",
"gender": "Female",
"age": "6"
},
{
"name": "John",
"gender": "Male",
"age": "15"
}
],
"citiesLived": [
{
"place": "Seattle",
"yearsLived": [
"1995"
]
},
{
"place": "Stockholm",
"yearsLived": [
"2005"
]
}
]
},
{
"kind": "person",
"fullName": "Mike Jones",
"age": 35,
"gender": "Male",
"phoneNumber": {
"areaCode": "622",
"number": "1567845"
},
"children": [
{
"name": "Earl",
"gender": "Male",
"age": "10"
},
{
"name": "Sam",
"gender": "Male",
"age": "6"
},
{
"name": "Kit",
"gender": "Male",
"age": "8"
}
],
"citiesLived": [
{
"place": "Los Angeles",
"yearsLived": [
"1989",
"1993",
"1998",
"2002"
]
},
{
"place": "Washington DC",
"yearsLived": [
"1990",
"1993",
"1998",
"2008"
]
},
{
"place": "Portland",
"yearsLived": [
"1993",
"1998",
"2003",
"2005"
]
},
{
"place": "Austin",
"yearsLived": [
"1973",
"1998",
"2001",
"2005"
]
}
]
},
{
"kind": "person",
"fullName": "Anna Karenina",
"age": 45,
"gender": "Female",
"phoneNumber": {
"areaCode": "425",
"number": "1984783"
},
"citiesLived": [
{
"place": "Stockholm",
"yearsLived": [
"1992",
"1998",
"2000",
"2010"
]
},
{
"place": "Russia",
"yearsLived": [
"1998",
"2001",
""
]
},
{
"place": "Austin",
"yearsLived": [
"1995",
"1999"
]
}
]
}
]
}
In this data I have 3 employees and their details like Name, children, cities Lived
but for one of the employee "Anna Karenina" children details are not there, but for other 2 employees have children data.
because of the missing children details I'm not able to flatten 3rd emp data.
below is what I have tried so far :
Snowflake Flatten Json Code :
select empd.value:kind,
empd.value:fullName,
empd.value:age,
empd.value:gender,
--empd.value:phoneNumber,
empd.value:phoneNumber.areaCode,
empd.value:phoneNumber.number ,
empd.value:children -- flattening childrean
//chldrn.value:name,
//chldrn.value:gender,
//chldrn.value:age,
//city.value:place,
//yr.value:yearsLived
from my_json emp , lateral flatten(input=>emp.Json_data:empDetails) empd ,
lateral flatten(input=>empd.value:children) chldrn,
//lateral flatten(input=>empd.value:citiesLived) city,
//lateral flatten(input=>city.value:yearsLived) yr
You need to use OUTER switch:
FLATTEN
OUTER => TRUE | FALSE
If FALSE, any input rows that cannot be expanded, either because they cannot be accessed in the path or because they have zero fields or entries, are completely omitted from the output.
If TRUE, exactly one row is generated for zero-row expansions (with NULL in the KEY, INDEX, and VALUE columns).
select empd.value:kind,
empd.value:fullName,
empd.value:age,
empd.value:gender,
empd.value:phoneNumber,
empd.value:phoneNumber.areaCode,
empd.value:phoneNumber.number ,
empd.value:children,
chldrn.value:name,
chldrn.value:gender,
chldrn.value:age,
city.value:place,
yr.value:yearsLived
from my_json emp,
lateral flatten(input=>emp.Json_data:empDetails) empd ,
lateral flatten(input=>empd.value:children, OUTER => TRUE) chldrn, -- <HERE>
lateral flatten(input=>empd.value:citiesLived) city,
lateral flatten(input=>city.value:yearsLived) yr

How to merge two arrays based on the property value in react with ES6

I have two arrays:
a = [{"sourceId": "1", "targetId": "2", "name": "heats air"} ,
{"sourceId": "3", "targetId": "4", "name": "power"}]
b = [{"name": "Hair Dryer", "id": "1"},
{"name": "Heating System", "id": "2"},
{"name": "Mains", "id": "3"},
{"name": "Blower", "id": "4"}]
How do I get the output like this:
[{"sourceId": "1", "targetId": "2", "name": "heats air", "from": "Hair Dryer", "to": "Heating System"},
{"sourceId": "3", "targetId": "4", "name": "power","from": "Mains", "to": "Blower"]
I want to merge them based on the property values: the keys "sourceId" and "targetId" of array a should correspond to the key "id" of array b. If a sourceId is matched with an id, add the value of the name with key "from" to the object in array a; If a targetId is matched with an id, add the value of the name with key "to" to the item in array a. Also,I am wondering whether I can do this without using lodash. (using ES6)
Convert b to a Map of id to name using Array#reduce. Then Array#map a to the required form using Object#assign, and the bMap:
const a = [{"sourceId":"1","targetId":"2","name":"heats air"},{"sourceId":"3","targetId":"4","name":"power"}];
const b = [{"name":"Hair Dryer","id":"1"},{"name":"Heating System","id":"2"},{"name":"Mains","id":"3"},{"name":"Blower","id":"4"}];
const bMap = b.reduce((map, item) => map.set(item.id, item.name), new Map);
const result = a.map((item) => (Object.assign({
from: bMap.get(item.sourceId),
to: bMap.get(item.targetId)
}, item)));
console.log(result);
Here you go.
const a = [{"sourceId": "1", "targetId": "2", "name": "heats air"} ,
{"sourceId": "3", "targetId": "4", "name": "power"}]
const b = [{"name": "Hair Dryer", "id": "1"},
{"name": "Heating System", "id": "2"},
{"name": "Mains", "id": "3"},
{"name": "Blower", "id": "4"}]
const result = a.reduce((arr, curr) => {
const from = b.filter(bObj => {
return bObj.id === curr.sourceId;
})[0]
const to = b.filter(bObj => {
return bObj.id === curr.targetId;
})[0];
arr.push({ ...curr, from: from.name, to: to.name });
return arr;
}, []);
console.log(result);

Angular Differences Between Two Models

I have an angular application which uses a significantly large shared model. Currently, when a user presses save the entire model is posted to a RESTful service. Ideally I would only like to post the fields that have changed. Since this is a shared model I do not have access to form validation states such as dirty/pristine etc. The idea I can think of is to have two models, the original and the modified and compare these.
Original Model
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"id": "123",
"number": "212 555-1234"
},
{
"id": "456",
"number": "646 555-4567"
},
{
"id": "789",
"number": "123 456-7890"
}
],
"children": [],
"spouse": null
}
Changed Model
{
"firstName": "Jane",
"lastName": "Smith",
"isAlive": true,
"age": 50,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"id": "123",
"number": "1234567890"
},
{
"id": "456",
"number": "646 555-4567"
},
{
"id": "789",
"number": "123 456-7890"
}
],
"children": [],
"spouse": null
}
Data Posted - This is what I need!
{
"firstName": "Jane",
"age": 50,
"phoneNumbers": [
{
"id":"123",
"number": "1234567890"
}
]
}
How can I achieve this? I need the changed fields including any fields called id!
You'll need something like this. Working Fiddle: https://jsfiddle.net/jyyyLaot/
function filter(obj1, obj2) {
var result = {};
for(key in obj1) {
if(obj2[key] != obj1[key]) result[key] = obj2[key];
if(typeof obj2[key] == 'array' && typeof obj1[key] == 'array')
result[key] = arguments.callee(obj1[key], obj2[key]);
if(typeof obj2[key] == 'object' && typeof obj1[key] == 'object')
result[key] = arguments.callee(obj1[key], obj2[key]);
}
return result;
}

Resources