postgres how to compare jsonb array and array - postgresql

This my jonb data
contact:{
"name": "Jonh",
"country": ["USA", "UK"],
}
And my query:
SELECT * FROM public.product where contact -> 'country' = ARRAY['USA','UK'];
Executed the query and got this ERROR: operator does not exist: jsonb = text[]
So how do I fix this error?

You need to compare it with a JSONB array:
select *
from product
where contact -> 'country' = '["USA","UK"]'::jsonb;
But this depends on the order of the elements in the array. If you want to test all keys regardless of the order, the ?& operator might be better:
where contact -> 'country' ?& array['UK','USA']
That would however also return rows that contain additional elements in the array. If you need to match all elements exactly regardless of the order you could use the #> operator twice:
where contact -> 'country' #> '["USA","UK"]'::jsonb
and contact -> 'country' <# '["USA","UK"]'::jsonb

Use to_jsonb():
SELECT (('{"a": ["x","y","z"]}'::jsonb)->'a') = to_jsonb(array['x','y','z']);
^^ Returns true.

Related

How to query inside a postgres db having one column as json value

I have a table named Test and in that one column (Subject) contains JSON values.
This is the query which i am using
select Name,Subject
from Test
where id =1;
And the following are the JSON values present inside table.
{
"subject":{
"Maths":"20",
"Physics":"21",
"English":"22"
},
"Staff":{
"English":"Anna",
"maths":"Rahul",
"Physics":"John"
}
}
Now my question is how to write a query to get English mark from JSON value.
Expected o/p is 22.
I am new to postgres, can any one help me in this thanks in advance
You can combine the -> and ->> operators
select Name,Subject, subject -> 'subject' ->> 'English' as english_mark
from Test
where id =1;
Alternatively use the #>> operator where you provide the path to the element you want as an array of keys:
select Name,Subject, subject #>> '{subject, English}' as english_mark
from Test
where id =1;

Postgres - How to perform a LIKE query on a JSONB field?

I have a jsonb field called passengers, with the following structure:
note that persons is an array
{
"adults": {
"count": 2,
"persons": [
{
"age": 45,
"name": "Prof. Kyleigh Walsh II",
"birth_date": "01-01-1975"
},
{
"age": 42,
"name": "Milford Wiza",
"birth_date": "02-02-1978"
}
]
}
}
How may I perform a query against the name field of this JSONB? For example, to select all rows which match the name field Prof?
Here's my rudimentary attempt:
SELECT passengers from opportunities
WHERE 'passengers->adults' != NULL
AND 'passengers->adults->persons->name' LIKE '%Prof';
This returns 0 rows, but as you can see I have one row with the name Prof. Kyleigh Walsh II
This: 'passengers->adults->persons->name' LIKE '%Prof'; checks if the string 'passengers->adults->persons->name' ends with Prof.
Each key for the JSON operator needs to be a separate element, and the column name must not be enclosed in single quotes. So 'passengers->adults->persons->name' needs to be passengers -> 'adults' -> 'persons' -> 'name'
The -> operator returns a jsonb value, you want a text value, so the last operator should be ->>
Also != null does not work, you need to use is not null.
SELECT passengers
from opportunities
WHERE passengers -> 'adults' is not NULL
AND passengers -> 'adults' -> 'persons' ->> 'name' LIKE 'Prof%';
The is not null condition isn't really necessary, because that is implied with the second condition. The second condition could be simplified to:
SELECT passengers
from opportunities
WHERE passengers #>> '{adults,persons,name}' LIKE 'Prof%';
But as persons is an array, the above wouldn't work and you need to use a different approach.
With Postgres 9.6 you will need a sub-query to unnest the array elements (and thus iterate over each one).
SELECT passengers
from opportunities
WHERE exists (select *
from jsonb_array_elements(passengers -> 'adults' -> 'persons') as p(person)
where p.person ->> 'name' LIKE 'Prof%');
To match a string at the beginning with LIKE, the wildcard needs to be at the end. '%Prof' would match 'Some Prof' but not 'Prof. Kyleigh Walsh II'
With Postgres 12, you could use a SQL/JSON Path expression:
SELECT passengers
from opportunities
WHERE passengers #? '$.adults.persons[*] ? (#.name like_regex "Prof.*")'

Indexing a josnb column in postgresql

I have a column in postgresql table with type jsonb.
{
.....
"type": "car",
"vehicleIds": [
"980e3761-935a-4e52-be77-9f9461dec4d1","980e3761-935a-4e52-be77-9f9461dec4d2"
]
.....
}
Application runs queries against these fields to fetch records. I need to index this column only for these fields.
How can this be done?
This is query structure with properties as the column name:
SELECT *
FROM Vehicle f
WHERE f.properties::text ## CONCAT('$.vehicleIds[*] >', :vehicleId )= true
AND f.properties::text ## CONCAT('$.type >', :type ) = true
The query you are using is highly confusing, as it boils down to be a text search query, as the ## is applied on a text value.
I also don't understand the '$.type > ... condition. With values like car I would expect an equality operator, rather than "greater than". Using > together with a UUID also doesn't seem to make sense.
If you want to search for values of type car and contain a list of IDs, using the "contains" operator #> is a better way to do that:
SELECT *
FROM Vehicle f
WHERE f.properties #> '{"type": "car", "vehicleIds": ["980e3761-935a-4e52-be77-9f9461dec4d1"]}'
The above could make use of a GIN index on the properties column:
create index on vehicles using gin (properties);
If the type key is always queried with equality (which I assume), a combined index might be more efficient:
create index on vehicles using gin ( (properties ->> 'type'), (properties -> 'vehicleIds') );
You need to install the btree_gin extension in order to create that index.
That index would be a bit smaller but needs a different query:
SELECT *
FROM Vehicle f
WHERE f.properties ->> 'type' = 'car'
AND f.properties -> 'vehicleIds' #> '["980e3761-935a-4e52-be77-9f9461dec4d1"]'
You will need to validate if the indexes are used and which ones is more efficient by looking at the execution plan

PostgreSQL - jsonb_each

I have just started to play around with jsonb on postgres and finding examples hard to find online as it is a relatively new concept.I am trying to use jsonb_each_text to printout a table of keys and values but get a csv's in a single column.
I have the below json saved as as jsonb and using it to test my queries.
{
"lookup_id": "730fca0c-2984-4d5c-8fab-2a9aa2144534",
"service_type": "XXX",
"metadata": "sampledata2",
"matrix": [
{
"payment_selection": "type",
"offer_currencies": [
{
"currency_code": "EUR",
"value": 1220.42
}
]
}
]
}
I can gain access to offer_currencies array with
SELECT element -> 'offer_currencies' -> 0
FROM test t, jsonb_array_elements(t.json -> 'matrix') AS element
WHERE element ->> 'payment_selection' = 'type'
which gives a result of "{"value": 1220.42, "currency_code": "EUR"}", so if i run the below query I get (I have to change " for ')
select * from jsonb_each_text('{"value": 1220.42, "currency_code": "EUR"}')
Key | Value
---------------|----------
"value" | "1220.42"
"currency_code"| "EUR"
So using the above theory I created this query
SELECT jsonb_each_text(data)
FROM (SELECT element -> 'offer_currencies' -> 0 AS data
FROM test t, jsonb_array_elements(t.json -> 'matrix') AS element
WHERE element ->> 'payment_selection' = 'type') AS dummy;
But this prints csv's in one column
record
---------------------
"(value,1220.42)"
"(currency_code,EUR)"
The primary problem here, is that you select the whole row as a column (PostgreSQL allows that). You can fix that with SELECT (jsonb_each_text(data)).* ....
But: don't SELECT set-returning functions, that can often lead to errors (or unexpected results). Instead, use f.ex. LATERAL joins/sub-queries:
select first_currency.*
from test t
, jsonb_array_elements(t.json -> 'matrix') element
, jsonb_each_text(element -> 'offer_currencies' -> 0) first_currency
where element ->> 'payment_selection' = 'type'
Note: function calls in the FROM clause are implicit LATERAL joins (here: CROSS JOINs).
WITH testa AS(
select jsonb_array_elements
(t.json -> 'matrix') -> 'offer_currencies' -> 0 as jsonbcolumn from test t)
SELECT d.key, d.value FROM testa
join jsonb_each_text(testa.jsonbcolumn) d ON true
ORDER BY 1, 2;
tetsa get the temporal jsonb data. Then using lateral join to transform the jsonb data to table format.

How to search in SphinxQL by sql_attr_multi field

This is my query to sphinxQL:
SELECT option_id FROM items WHERE cat IN (10,11) GROUP BY option_id LIMIT 100000 OPTION max_matches=100000
cat is sql_attr_multi field, this query not return to me correct result. Anybody knows how to search by fields by this sphinx attribute?
That query looks for items where cat attribute contains either 10 OR 11, is that what you trying to do?
If its not, would help to know what you are trying to query!
I had similar problem.
When I pass array to IN condition for MVA attribute I have no result, however there is several ones in index.
When I debug condition (attribute array(10, 11) in you case) I see that array values is string integer instead.
array(
0 => "10",
1 => "11"
)
For every single value in condition uses quoteArr() function
https://github.com/FoolCode/SphinxQL-Query-Builder/blob/master/src/SphinxQL.php#L518
wich escape value according with https://github.com/FoolCode/SphinxQL-Query-Builder/blob/master/src/Drivers/ConnectionBase.php#L95
The quote function use is_int() PHP internal function:
$a = "1";
var_dump(is_int($a)); // return bool(false)
It mean, thet instead
cat IN (10, 11)
you have
cat IN ("10", "11")
But sphinx can't filter MVA attribute by not integer (string) values no metter IN or OR WHERE notation you use.
[1064] index document : unsupported filter type 'string' on MVA column [ SELECT * FROM `document` WHERE MATCH('(some query)') AND `_category` = '5' LIMIT 0, 10]
You should use strict value type:
foreach ($category as &$item) {
$item = (int)$item;
} unset($item);
I am not sure that it is your incindent. Unfortunately, there isn't enough data to say it for sure in this case.