How to pass json array to postgresql function - postgresql

I have a json object contains array of json.
i.e:
{
ID:'1',
Name:'Pooja',
School:[{Name:'ABC',Address:'Nagpur'},{Name:'CDF'},{Name:'GHI',Year:{From:'2015',To:'2016'}}]
}
I want to insert this in three different table as User, School and Year.
Can anyone help?

Assuming I understood you correctly, you want to split the provided JSON object and write its subobjects to some tables.
PostgreSQL has several JSON operators that might help.
First of all, you should cast JSON's textual representation to type json. This allows you to use JSON operators and functions, such as -> (Get JSON object field):
select
'{
"ID":1,
"Name":"Pooja",
"School":[
{
"Name":"ABC",
"Address":"Nagpur"
},
{
"Name":"CDF"
},
{
"Name":"GHI",
"Year":{
"From":"2015",
"To":"2016"
}
}
]
}'::json -> 'Name';
?column?
----------
Pooja
(1 row)
Or, for example, #> (Get JSON object at specified path):
select 'YOUR_JSON'::json #> '{"School", 2, "Year"}';
?column?
----------------------------
{ +
"From":"2015",+
"To":"2016" +
}
(1 row)
All you have to do now is insert the result of the operator application into the table of your choice:
insert into user select 'YOUR_JSON'::json -> 'Name';
If you simply want to extract the School array, you could still use -> operator:
select 'YOUR_JSON'::json -> 'School';
?column?
-----------------------------
[ +
{ +
"Name":"ABC", +
"Address":"Nagpur"+
}, +
{ +
"Name":"CDF" +
}, +
{ +
"Name":"GHI", +
"Year":{ +
"From":"2015", +
"To":"2016" +
} +
} +
]
(1 row)
Read the documentation for more.

Related

sequelize, query property on array of objects

I have looked extensively for an answer but could not find a simple solution.
I have a table that contains a column subscriptionHistory
The data can look like so:
[
{
"fromDate": "2023-01-24T10:11:57.150Z",
"userSubscribedToo": "EuuQ13"
},
{
"fromDate": "2022-01-24T10:11:57.150Z",
"tillDate": "2022-02-24T22:59:59.999Z",
"userSubscribedToo": "a4ufoAB"
}
]
I'm trying to find the records of the subscriptions.
In Mongo we do
'subscriptionHistory.$.userSubscribedToo' = 'a4ufoAB'
Nice and easy.
I'm using PostgreSQL and Sequelize,
The following doesn't work.
const totalEarnings = await SubscriptionToken.count({
where: {
'subscriptionHistory.$.userSubscribedToo': user.id,
},
});
Neither do any direct queries
SELECT *
FROM vegiano_dev."subscription-tokens"
WHERE "subscriptionHistory"->>'userSubscribedToo' = 'a4ufoAB'
--WHERE "subscriptionHistory" #> '{"userSubscribedToo": "a4ufoAB"}'
Not sure where to go now :-/
You can use a JSON path condition with the ## (exists) operator:
select *
from vegiano_dev."subscription-tokens"
where "subscriptionHistory" ## '$[*].userSubscribedToo == "a4ufoAB"'
The #> will work as welll, but because subscriptionHistory is an array, you need to use an array with that operator:
where "subscriptionHistory" #> '[{"userSubscribedToo": "a4ufoAB"}]'
This assumes that subscriptionHistory is defined as jsonb which it should be. If it's not, you need to cast it: "subscriptionHistory"::jsonb

How can I access a key value in jsonb postgresql?

{
"data": {
"val": "{\"cell_number\": \"123\"}"
}
}
I want to get the value in data -> val -> cell_number i.e '123'. Is there a way to do it in postgresql?
If that is not a typo and you put a stringified json object under the val key, then this will untangle it for you:
with invar as (
select '{
"data": {
"val": "{\"cell_number\": \"123\"}"
}
}'::jsonb as jsonb_col
)
select ((jsonb_col->'data'->>'val')::jsonb)->>'cell_number' from invar;
?column?
----------
123
(1 row)
The first step gets you down to the val key. That result has to be returned as text (hence the ->>) and then cast to jsonb so that cell_number can be dereferenced.

Postgres find in jsonb nested array

I have a case when my data in in nested arrays of jsonb in order to find the value I have to do multiple JSONB_ARRAY_ELEMENTS which is costly and takes a lots of nested code.
The json file has the continents inside countries and inside cities.
I need to access a city value.
Is there a way to make this query simpler and faster?
I was trying to solve it using JSON_EXTRACT_PATH but in order to get in to a array but I need the indexes.
WITH mydata AS (
SELECT '
{
"continents":[
{
"name":"America",
"area":43316000,
"countries":[
{
"country_name":"Canada",
"capital":"Toronto",
"cities":[
{
"city_name":"Ontario",
"population":2393933
},
{
"city_name":"Quebec",
"population":12332
}
]
},
{
"country_name":"Brazil",
"capital":"Brasilia",
"cities":[
{
"city_name":"Sao Paolo",
"population":34534534
},
{
"city_name":"Rio",
"population":445345
}
]
}
]
},
{
"name":"Europa",
"area":10530751,
"countries":[
{
"country_name":"Switzerland",
"capital":"Zurich",
"cities":[
{
"city_name":"Ginebra",
"population":4564565
},
{
"city_name":"Basilea",
"population":4564533
}
]
},
{
"country_name":"Norway",
"capital":"Oslo",
"cities":[
{
"city_name":"Oslo",
"population":3243534
},
{
"city_name":"Steinkjer",
"population":4565465
}
]
}
]
}
]
}
'::JSONB AS data_column
)
SELECT cit.city->>'city_name' AS city,
(cit.city->>'population')::INTEGER AS population
FROM (SELECT JSONB_ARRAY_ELEMENTS(coun.country->'cities') AS city
FROM (SELECT JSONB_ARRAY_ELEMENTS(cont.continent->'countries') AS country
FROM (SELECT JSONB_ARRAY_ELEMENTS(data_column->'continents') AS continent
FROM mydata
) AS cont
WHERE cont.continent #> '{"name":"Europa"}'
) AS coun
WHERE coun.country #> '{"country_name" : "Norway"}'
) AS cit
WHERE cit.city #> '{"city_name": "Oslo"}'
See my nested queries? looks ugly, I can get the answer using: JSONB_EXTRACT_PATH( data_column->'continents', '1', 'countries', '1', 'cities', '0', 'population') but I had to hardcode the array indexes.
Hope you can help me out.
Thanks.
You don't need any nesting, you can do lateral queries:
SELECT
city->>'city_name' AS city,
(city->>'population')::INTEGER AS population
FROM
mydata,
JSONB_ARRAY_ELEMENTS(data_column->'continents') AS continent,
JSONB_ARRAY_ELEMENTS(continent->'countries') AS country,
JSONB_ARRAY_ELEMENTS(country->'cities') AS city
WHERE continent ->> 'name' = 'Europa'
AND country ->> 'country_name' = 'Norway'
AND city ->> 'city_name' = 'Oslo';
(online demo)
However, since you mentioned paths and having to specify indices in there, this is actually the perfect use case for Postgres 12 JSON paths:
SELECT jsonb_path_query(data_column, '$.continents[*]?(#.name == "Europa").countries[*]?(#.country_name=="Norway").cities[*]?(#.city_name=="Oslo")') FROM mydata
(online demo)

Access item in ListField/EmbeddedDocumentListField by using __raw__ in MongoEngine [duplicate]

I use mongoengine with Django and python.
This is my code:
class Chambre(EmbeddedDocument):
max_personne = IntField(default=0)
prix = IntField(default=0)
class Hotel(Document):
code = IntField(default=0)
nom = StringField(max_length=200)
chambre = ListField(EmbeddedDocumentField(Chambre))
resume = StringField(max_length=200)
1 - I want a query to filter all Hotel that have at least a Chambre with prix >= a (a floeat number)
2 - also have that Chambre
Any idea?
You can use the embedded notation as well as the Query Operator for "greater than or equal "
Hotel.objects(chambre__prix__gte=a)
Or if you need to cast as an integer:
Hotel.objects(chambre__prix__gte=int(math.floor(a)))
If you want to only project the "matched" element, use a raw query directly on the driver instead:
Hotel._get_collection().find(
{ 'chambre.prix': { '$gte': int(math.floor(a)) } },
{ 'chambre.$': 1 }
)

SequelizeJS: Recursive include same model

I want to fetch data from a Postgres database recursively including all associated models using SequelizeJS.
GeoLocations table
id | isoCode | name | parentId | type | updatedAt | deletedAt
parentId holds the id of the parent GeoLocation.
Model Associations
GeoLocation.belongsTo(GeoLocation, {
foreignKey: 'parentId',
as: 'GeoLocation'
});
Data explanation and example
GeoLocation types and their relations:
city -> subdivision -> country -> continent
id: 552, name: Brooklyn, type: city, parentId: 551
id: 551, name: New York, type: subdivision, parentId: 28
id: 28, name: United States, type: country, parentId: 27
id: 27, name: North America, type: continent, parentId: NULL
Now, when querying a city, I want all relations to be included as long as parentId is set.
GeoLocation.findOne({
where: {
id: 552
},
include: [
{
model: GeoLocation,
as: 'GeoLocation',
include: [
{
model: GeoLocation,
as: 'GeoLocation',
include: [
{
model: GeoLocation,
as: 'GeoLocation',
}
],
}
],
}
],
});
Response in JSON
{
"GeoLocation":{
"id":552,
"type":"city",
"name":"Brooklyn",
"GeoLocation":{
"id":551,
"type":"subdivision",
"name":"New York",
"GeoLocation":{
"id":28,
"type":"country",
"name":"United States",
"GeoLocation":{
"id":27,
"type":"continent",
"name":"North America"
}
}
}
}
}
The solution above works, but I have the strong feeling, that there are better ways to do this, without having to include the model multiple times. I can't find anything related in the docs. Am I missing something?
I already moved on a different solution. I am not using sequelize native pseudo language to achieve this. Because apparently, recursive includes feature is not possible with sequelize.
Here is the recursive model I use :
I want this recursive info, which organisation is related to which, no matter how I get this info, I'll process it later to get the info into shape (tree, flat list, ... whatever)
I use a sequelize RAW query involving recursive SQL:
exports.getArborescenceByLienId = function (relationType, id) {
const query = 'With cte as (\n' +
' select id_organisationSource, \n' +
' relationType,\n' +
' id_organisationTarget\n' +
' from (\n' +
' select * from relation where relationType= :relationType\n' +
' ) relations_sorted,\n' +
' (\n' +
' select #pv := :orgId\n' +
' ) initialisation\n' +
' where find_in_set(id_organisationSource, #pv)\n' +
' and length(#pv := concat(#pv, \',\', id_organisationTarget))\n' +
')\n' +
'select source.id as `idSource`, cte.relationType, target.id as `idTarget`\n' +
'from cte \n' +
'left outer join organisation source on cte.id_organisationSource = source.id\n' +
'left outer join organisation target on cte.id_organisationTarget = target.id;';
return models.sequelize.query(
query,
{
type: models.sequelize.QueryTypes.SELECT,
replacements: { orgId: id, relationType: relationType}
}
);
};
Imagine I have an object model like this:
(Assuming each relation here have same type : say isManaging)
Result of query with ID = 1 would be :
Result of query with ID = 2 would be :
I then transform this flat list to a Tree, and VOILA :)
Let me know if your interested in this Flat list to tree transforming algorithm.
Hope this help. Cheers !