PostgreSQL extract keys from jsonb, exception "cannot call jsonb_object_keys on a scalar" - sql

I am trying to get my head around with jsonb in Postgres. There are quite a few issues here, What I wanted to do was something like:
SELECT table.column->>'key_1' as a FROM "table"
I tried with -> and also some combinations of brackets as well, but I was always getting nil in a.
So I tried to get all keys first to see if it is even recognizing jsonb or not.
SELECT jsonb_object_keys(table.column) as a FROM "table"
This threw an error:
cannot call jsonb_object_keys on a scalar
So, to check the column type(which I created, so I know it IS jsonb, but anyway)
SELECT pg_typeof(column) as a FROM "table" ORDER BY "table"."id" ASC LIMIT 1
This correctly gave me "jsonb" in the result.
values in the column are similar to {"key_1":"New York","key_2":"Value of key","key_3":"United States"}
So, I am really confused on what actually is going on here and why is it calling my json data to be scalar? What does it actually means and how to solve this problem?
Any help in this regard will be greatly helpful.
PS: I am using rails, posted this as a general question for the problem. Any rails specific solution would also work.

So the issue turned out to be OTHER than only SQL.
As I mentioned I am using rails(5.1), I had used default value '{}' for the jsonb column. And I was using a two-way serializer for the column by defining it in my model for the table.
Removing this serializer and adjusting the default value to {} actually solved the problem.
I think my serializer was doing something to the values, but still, in the database, it had correct value like i mentioned in the question.
It is still not 100% clear to me what was the problem. But it is solved anyway. If anyone can shed some light on what exactly the problem was, that will be great.
Hope this might help someone.

In my case the ORM layer somehow managed to wrote a null string into the JSON column and Postgres was happy with it. Trying to execute json_object_keys on such value resulted in the OP error.
I have managed to track down the place that allow such null strings and after fixing the code, I have also fixed the data with the following query:
UPDATE tbl SET col = '{}'::jsonb WHERE jsonb_typeof(col) <> 'object';
If you intentionally mix the types stored in the column (e.g. sometimes it is an object, sometimes array etc), you might want to filter out all rows that don't contain objects with a simple WHERE:
SELECT jsonb_object_keys(tbl.col) as a FROM tbl WHERE jsonb_typeof(col) = 'object';

Related

SQL CASE How do I correct assign a value?

First, I would like to say I have already spent the due time and diligence on my part by watching videos, using other sources, and reading other posts and answers on SOF before asking this and have been unable to find a solution.
The issue I am running into, in a particular case, is a certain type is being passed in, which would require the use of LIKE as the specific type itself will not match anything as three types use the one type, say 'painting' in this situation. The database has a 'painting small' and 'painting large.'
Code
// I tried this
CASE WHEN type = 'painting' THEN inventory.type LIKE '%'+type+'%' ELSE inventory.type = type END
I keep running into the "An expression of a non-boolean type specified in a context where a condition is expected. There are a few other variations I have tried as well as IF ELSE, however, I run into the same issue. Someone else may be able to word this question better.
I mainly want to be pointed in the right direction and receive clarification on what I am doing wrong.
Thank you
There are a few problems with your query. Rather than the CASE expression itself I'm going to address the less obvious problem, your lack of prefixing. Take this clause:
inventory.type LIKE '%'+type+'%'
This could likely either error, due to an ambiguous column name, or resolve to inventory.type LIKE '%'+inventory.type+'%'; obviously the latter is going to always be true unless the column type has the value NULL. Always prefix your column names, especially when your query contains 2+ tables.
As for the actual problem, this is presumably part of a WHERE, therefore use OR and AND logic:
WHERE (({Other Table Prefix}.[type] = 'painting' AND inventory.[type] LIKE '%' + {Other Table Prefix}.[Type] + '%')
OR ({Other Table Prefix}.[type] != 'painting' AND inventory.[type] = {Other Table Prefix}.[Type]))
Obviously, you need to appropriately replace {Other Table Prefix} with the correct prefix.
The problem seems to be in the
LIKE '%'+type+'%'
where LIKE may be returning a boolean value.

BigQuery - Nested and Repeated not allowing to insert Null values

I have a field with mutiplt Nested and Repeated fields inside it. It is not allowing me to Insert null values or null array for any child field with RECORD data type.
Is this a limitation or is there any workaround to this?
Just the question I've been looking for.. with no answer
Luckily, I've managed to find this question which address the same issue - only on update statement
The solution there works but it's a dirty hack - put the null inside an expression which address a field with the target type
IF(false, struct_to_set_null, NULL)
Where struct_to_set_null is the actual field name from your table
Unfortunately, it didn't work on my scenario because I used an inline select statement that is not on the same scope as my table
Instead, I simply used cast function (thanks to this answer). The downside is that I ended up with pretty much complex expressions such as:
# this represents a repeated record with a single field of type string
cast(null as Array<struct<STRING>>)
# same thing but with multiple values
cast(null as Array<struct<STRING, INTEGER>>)
On the bright side - it will always work + it's seems more clear and straight-forward (at least for me)
Hope this post will help someone out there

grafana multi value query in timestream

i have some problems displaying my aws timestream data in grafana. I added as a global dashboard variable DevEUI with 3 different specific values. But when i am using the multivalue syntax ${DevEUI} in my query with more then one value i get everytime a error.
hope somebody can give me a hint.
Regards and thanks in advance
You are most probably having a list of values as the value of your multivalue Grafana variable, but you are still using the = operator in your query. Try ... and DevEUI IN ('${DevEUI}'). Or maybe without the single quotes or the parantheses... the exact syntax depends on your Grafana variable.
But, this is just an educated guess, since I cannot see neither your database schema nor the definition of this Grafana variable (both of which are important details in a question like yours, for future reference).
This is how I did it for a multivalued string value:
timestream_variable_name = ANY(VALUES ${grafana_variable_name:singlequote})
You might have to adjust the formatting Grafana applies to the concatenated variable value it generates, depending on your data type.
I know this is long after the original question but #alparius pointed me in the right direction so I wanted to update the fix for the problem Joe reported.
Use formatting to get the proper quotes/values when formatting your query. Something like this:
Select * from database where searchiterm IN (${Multi-Value_Variable:sqlstring})

Google Bigquery, WHERE clause based on JSON item

I've got a bigquery import from a firestore database where I want to query on a particular field from a document. This was populated via the firestore-bigquery extension and the document data is stored as a JSON string.
I'm trying to use a WHERE clause in my query that uses one of the fields from the JSON data. However this doesn't seem to work.
My query is as follows:
SELECT json_extract(data,'$.title') as title,p
FROM `table`
left join unnest(json_extract_array(data, '$.tags')) as p
where json_extract(data,'$.title') = 'technology'
data is the JSON object and title is an attribute of all of the items. The above query will run but yield 'no results' (There are definitely results there for the title in question as they appear in the table preview).
I've tried using WHERE title = 'technology' as well but this returns an error that title is an unrecognized field (hence the json_extract).
From my research this should work as a standard SQL JSON query but doesn't seem to work on Bigquery. Does anyone know of a way around this?
All I can think of is if I put the results in another table, but I don't know if that's a workable solution as the data is updated via the extension on an update, so I would need to constantly refresh my second table as well.
Edit
I'm wondering if configuring a view would help with this? Though ultimately I would like to query this based on different parameters and the docs here https://cloud.google.com/bigquery/docs/views suggest you can't reference query parameters in a view
I've since managed to work this out, and will share the solution for anyone else with the same problem.
The solution was to use JSON_VALUE in the WHERE clause instead e.g:
where JSON_VALUE(data,'$.title') = 'technology';
I'm still not sure if this is the best way to do this in terms of performance and cost so I will wait to see if anyone else leaves a better answer.

How to make criteria with array field in Hibernate

I'm using Hibernate and Postgres and defined a character(1)[] column type.
So I donĀ“t know how to make this criteria to find a value in the array.
Like this query
SELECT * FROM cpfbloqueado WHERE bloqueados #> ARRAY['V']::character[]
I am not familiar with Postgres and its types but you can define your own type using custom basic type mapping. That could simplify the query.
There are many threads here on SO regarding Postres array types and Hibernate, for instance, this one. Another array mapping example that could be useful is here. At last, here is an example of using Criteria with user type.
Code example could be
List result = session.createCriteria(Cpfbloqueado.class)
.setProjection(Projections.projectionList()
.add(Projections.property("characterColumn.attribute"), PostgresCharArrayType.class)
)
.setResultTransformer(Transformer.aliasToBean(Cpfbloqueado.class))
.add(...) // add where restrictions here
.list()
Also, if it is not important for the implementation, you can define max length in the entity model, annotating your field with #Column(length = 1).
Or if you need to store an array of characters with length of 1 it is possible to use a collection type.
I hope I got the point right, however, it would be nice if the problem domain was better described.
So you have array of single characters... Problem is that in PG that is not fixed length. I had this problem, but around 10 years ago. At that time I had that column mapped as string, and that way I was able to process internal data - simply slice by comma, and do what is needed.
If you hate that way, as I did... Look for columns with text[] type - that is more common, so it is quite easy to find out something. Please look at this sample project:
https://github.com/phstudy/jpa-array-converter-sample