Search in JSON with Postgres without knowing all keys - postgresql

I'm trying to search in a JSON using Postgres. The JSON looks like that:
"some key": {
"city": "Chicago",
"id": "",
"color": "",
"size": ""
},
"a different key": {
"city": "San Francisco",
"id": null,
"shape": "",
"height": ""
}
I don't know what can the names of the first level keys (that's why I called them "some key" and "a different key" in the example above). I do know that they can be different from one another.
I want to extract all the values of the "city" key, Chicago and San Francisco in the example above.
I guess it's something like that but this one didn't work:
(table_name.row_name-> * ->> 'city') as city_name
(I know that the city is always in the second level on the JSON, but can occur multiple times)

You can iterate over the keys:
select t.some_column,
x.item -> 'city' as city_name
from the_table t
cross join jsonb_each(t.the_column) as x(item)
This returns each city as a new row together with the other columns of that table.
The above assumes your column is defined as jsonb (which it should be). If it isn't you need to use json_each() instead

Related

Extracting info out of lists inside a jsonb

I have a with a jsonb column called jsonb that contains data in the following format.
{
"stuff": [
{
"name": "foo",
"percent": "90.0000"
},
{
"name": "bar",
"percent": "10.0000"
}
],
"countries": [
{
"name": "USA",
"value": "30"
},
{
"name": "Canada",
"value": "25"
},
{
"name": "Mexico",
"value": "20"
},
{
"name": "Ecuador",
"value": "10"
}
]
}
I am having a lot of trouble working with this data. Specifically what I want to do is find all the different values "name" can have in "stuff" as well as in "countries", kind of like a SELECT distinct.
But my problem is that I can't seem to extract anything useful from this jsonb. My approach so far was to do
SELECT jsonb->>'stuff' FROM table, but this only gave me a column of type text which contained [{"name": "foo","percent": "90.0000"},{"name": "bar","percent": "10.0000"}].
But since this is text I can't really do anything with it. I also tried SELECT jsonb_array_elements_text(jsonb) FROM table but that returned the following Error:
ERROR: cannot extract elements from an object
SQL state: 22023
Any help with working with this format of data is greatly appreciated!
You need to unnest each array separately and then create a union on the result of those two steps:
select c.x ->> 'name'
from the_table
cross join lateral jsonb_array_elements(json_column -> 'countries') as c(x)
union
select s.x ->> 'name'
from the_table
cross join lateral jsonb_array_elements(json_column -> 'stuff') as s(x);
Online example: https://rextester.com/ZEOIXF91294

Only query field if value has been passed from API in MongoDB

I have created an API in Mule and have a number of queryParameters specified in the RAML file that I would like to use to query my store MongoDB collection.
A snippet of the collection looks like this:
{
"stores": [{
"storeId": 1234,
"storeName": "Shop Around Ltd",
"opens": "09:00:00.000Z",
"closes": "17:00:00.000Z",
"departments": ["clothing", "computers", "toys", "kitchen and home"],
"address": {
"street": "street1",
"city": "New York",
"state": "New York",
"zipCode": "10002"
}
}]
}
My problem is that the query parameters used to query the collection may be different each time so how can I write a dynamic query for MongoDB so it only queries on the fields that have been passed in with values instead of writing a query for each combination of query parameters? E.g. I may want to query based on zip code for one query so all the other fields will be blank and the next time I may want to query on the departments and whether the store will be open at 11am.
The query will be called from Mule.
Thanks

postgres + jsonb + get values of key from multidimentional array

I am trying to get jsonb result based on matching key.
I have DB table "listings" with number and data column.
number | data
1 | {"name": "XYZ company", "city": "toronto", "province": "ON", "people" : [
{ "firstName": "tom", "lastName": "hanks",
"phonenumber": [{"type": "mobile", "Number": "111111"}],
"Email": [{"type": "business", "address": "tom#xyz.com"},{"type": "personal", "address": "tom#mailinator.com"}] },
{ "firstName": "sandra", "lastName": "petes",
"phonenumber": [{"type": "mobile", "Number": "333"}, {"type": "home", "Number": "444"}],
"Email": [{"type": "business", "address": "sandra#xyz.com"}]
}
]}
I need to pull all values for data column with keys -
people->firstname
people->lastName
people->phonenumber->Number
people->Email->address
What I achieved so far is:
SELECT number
,jonb_array_length(jsonb_extract_path(data,'people')) as people_count
,jsonb_extract_path(data,'people','0','firstname') as FirstName
,jsonb_extract_path(data,'people','0','lastname') as LastName
,jsonb_extract_path(data,'people','0','email','Address'") as personEmail
,jsonb_extract_path(data,'people','0','phonenumber','Number') as personPhone
FROM listings
WHERE number='1';
However, this only gives me 0th element of people, I need to find all elements. Is there any way to achieve this in single query.
Thanks for your time!
You need to use the jsonb_array_elements() function to get all of the elements of the array. Since that function returns a set of rows, you need to use it as a row source.
SELECT '1' AS number,
jsonb_array_length(data->'people') AS people_count,
people->>'firstname' AS FirstName,
people->>'lastname' AS LastName,
people->'email'->0->>'Address' AS personEmail,
people->'phonenumber'->0->>'Number' as personPhone
FROM listings, jsonb_array_elements(data->'people') p(people)
WHERE number = '1';
This will result in a row for every person where number = '1'. The email and phone number objects are arrays too and I pick here just the first value. If you want all of them you need to just get the whole JSON arrays and then wrap this in an outer query where you do jsonb_array_elements() again.

Xcode: model adding entities

I am experimenting with API that returns some fields with underscore like _id. I am not able to map this field in the -xcdatamodel. The attribute must begin with letter.
I've also tried to map this field as "id" and provide in the "User Info" session a Key/Value like id : _id but without success.
Do you have a solution for this problem? As i know there are many APIs that have fields with underscore.
Other non underscore fields are mapped without problems.
{
"__v": 0,
"_avRateDelay": 5,
"_avRateRecommend": 5,
"_avRateStaff": 5,
"_id": "530f733df222bf594b190e0a10",
"_reviews": 1,
"active": 1,
"address": {
"city": "Little Rock",
"country": "USA",
"other": "",
"state": "AZ",
"street": "2701 E Roosevelt Rd",
"zip": "72206"
},
"location": {
"lat": 34.721175,
"lng": -92.24168600000002
},
"name": "Certainteed 69"
}
Don't use id or _id in Objective-C. id is a reserved word. Since many servers like to use that I recommend that you write mapping code so that it is mapped from the server id to something like identifier.
Since you need to write code to parse the fields anyway there is no hardship to look for that key and change it. You can even store the mapping in the NSEntityDescription and set up code to look for other mappings and change them. That way you can change other server styled values like created_at to their Objective-C counterparts like createdAt.
The key/values are editable directly in the model editor and then accessible via the -entity property on the NSManagedObject.

mongodb best practice: nesting

Is this example of nesting generally accepted as good or bad practice (and why)?
A collection called users:
user
basic
name : value
url : value
contact
email
primary : value
secondary : value
address
en-gb
address : value
city : value
state : value
postalcode : value
country : value
es
address : value
city : value
state : value
postalcode : value
country : value
Edit: From the answers in this post I've updated the schema applying the following rules (the data is slightly different from above):
Nest, but only one level deep
Remove unneccesary keys
Make use of arrays to make objects more flexible
{
"_id": ObjectId("4d67965255541fa164000001"),
"name": {
"0": {
"name": "Joe Bloggs",
"il8n": "en"
}
},
"type": "musician",
"url": {
"0": {
"name": "joebloggs",
"il8n": "en"
}
},
"tags": {
"0": {
"name": "guitar",
"points": 3,
"il8n": "en"
}
},
"email": {
"0": {
"address": "joe.bloggs#example.com",
"name": "default",
"primary": 1,
"il8n": "en"
}
},
"updates": {
"0": {
"type": "news",
"il8n": "en"
}
},
"address": {
"0": {
"address": "1 Some street",
"city": "Somecity",
"state": "Somestate",
"postalcode": "SOM STR",
"country": "UK",
"lat": 49.4257641,
"lng": -0.0698241,
"primary": 1,
"il8n": "en"
}
},
"phone": {
"0": {
"number": "+44 (0)123 4567 890",
"name": "Home",
"primary": 1,
"il8n": "en"
},
"1": {
"number": "+44 (0)098 7654 321",
"name": "Mobile",
"il8n": "en"
}
}
}
Thanks!
In my opinion above schema not 'generally accepted', but looks like great. But i suggest some improvements thats will help you to query on your document in future:
User
Name
Url
Emails {email, emailType(primary, secondary)}
Addresses{address, city, state, postalcode, country, language}
Nesting is always good, but two or three level nesting deep can create additional troubles in quering/updating.
Hope my suggestions will help you make right choice of schema design.
You may want to take a look at schema design in MongoDB, and specifically the advice on embedding vs. references.
Embedding is preferred as "Data is then colocated on disk; client-server turnarounds to the database are eliminated". If the parent object is in RAM, then access to the nested objects will always be fast.
In my experience, I've never found any "best practices" for what a MongoDB record actually looks like. The question to really answer is, "Does this MongoDB schema allow me to do what I need to do?"
For example, if you had a list of addresses and needed to update one of them, it'd be a pain since you'd need to iterate through all of them or know which position a particular address was located. You're safe from that since there is a key-value for each address.
However, I'd say nix the basic and contact keys. What do these really give you? If you index name, it'd be basic.name rather than just name. AFAIK, there are some performance impacts to long vs. short key names.
Keep it simple enough to do what you need to do. Try something out and iterate on it...you won't get it right the first time, but the nice thing about mongo is that it's relatively easy to rework your schema as you go.
That is acceptable practice. There are some problems with nesting an array inside of an array. See SERVER-831 for one example. However, you don't seem to be using arrays in your collection at all.
Conversely, if you were to break this up into multiple collections, you would have to deal with a lack of transactions and the resulting race conditions in your data access code.