I have a table with the following structure -
Column Name | Data Type
--------------------------
user_id | uuid
profile | jsonb
An example profile field would be something like -
{ "data": { "races": [ "white", "asian" ] } }
I want to query this table for users contain one of the following races (for example) - "white", "african american"
I would expect my example user above to be returned since their races field contains "white".
I have tried something like this with no success -
SELECT user_id from table
WHERE profile -> 'data' ->> 'races' = ANY('{"white", "african american"}')
Using Postgres 13.x
Thank you!
Use the ?| operator:
select user_id
from my_table
where profile -> 'data' -> 'races' ?| array['white', 'african american']
According to the documentation:
jsonb ?| text[] -> boolean
Do any of the strings in the text array exist as top-level keys or array elements?
tl;dr use the ?| operator.
There's two problems with your query.
->> returns text not jsonb. So you're asking if the text ["white", "asian"] matches white or african american.
You probably did that because otherwise you got type errors trying to use any with JSONB. any wants a Postgres array of things to compare, and it has to be an array of jsonb. We can do that...
select user_id
from user
where profile -> 'data' -> 'races' = ANY(array['"white"', '"african american"']::jsonb[]);
But this has the same problem as before, it's checking if the json array [ "white", "asian" ] equals "white" or "african american".
You need an operator which will match against each element of the JSON. Use the ?| operator.
select user_id
from users
where profile -> 'data' -> 'races' ?| array['white', 'african american'];
Related
I have a table that has cast as jsonb column, which looks like this:
cast: [
{ name: 'Clark Gable', role: 'Rhett Butler' },
{ name: 'Vivien Leigh', role: 'Scarlett' },
]
I'm trying to query name in the jsonb array of objects. This is my query:
SELECT DISTINCT actor as name
FROM "Title",
jsonb_array_elements_text(jsonb_path_query_array("Title".cast,'$[*].name')) actor
WHERE actor ILIKE 'cla%';
Is there a way to index a query like this? I've tried using BTREE, GIN, GIN with gin_trgm_ops with no success.
My attempts:
CREATE INDEX "Title_cast_idx_jsonb_path" ON "Title" USING GIN ("cast" jsonb_path_ops);
CREATE INDEX "Title_cast_idx_on_expression" ON "Title" USING GIN(jsonb_array_elements_text(jsonb_path_query_array("Title".cast, '$[*].name')) gin_trgm_ops);
One of the issues is that jsonb_array_elements_text(jsonb_path_query_array())returns a set which can't be indexed. Using array_agg doesn't seem useful, since I need to extract name value, and not just check for existence.
I have a table column in BigQuery with JSON array format with a row like {"role":"SuperAdmin","_id":"abcd","userId":"efgh"}. This column schema in BigQuery is REPEATED mode. My goal is to extract the userId value for all the rows in that column.
I have tried using JSON functions like json_value and json_extract:
select json_value(column_name, '$.users.userId') as userId, from table_name
but get the following error :
No matching signature for function JSON_VALUE for argument types: ARRAY<STRING>, STRING. Supported signature: JSON_VALUE(STRING, [STRING]) at [2:3]
Please how do I go about it?
Because it is repeated you will need to unnest it first.
Given the following example:
with sample_data as (
select ['{"role":"SuperAdmin","_id":"abcd","userId":"efgh"}','{"role":"SuperAdmin","_id":"abcd","userId":"efgh"}','{"role":"SuperAdmin","_id":"abcd","userId":"efgh"}'] as users
)
select json_value(user, '$.userId') as userId
from sample_data , UNNEST(users) user
The return is:
I have an employee table in postgres having a JSON column "mobile" in it. It stores JSON Array value ,
e_id(integer) name(char) mobile(jsonb)
1 John [{\"mobile\": \"1234567891\", \"status\": \"verified\"},{\"mobile\": \"1265439872\",\"status\": \"verified\"}]
2 Ben [{\"mobile\": \"6453637238\", \"status\": \"verified\"},{\"mobile\": \"4437494900\",\"status\": \"verified\"}]
I have a search api which queries this table to search for employee using mobile number.
How can I query mobile numbers directly ?
How should I create index on the jsonb column to make query work faster ?
*updated question
You can query like this:
SELECT e_id, name
FROM employees
WHERE mobile #> '[{"mobile": "1234"}]';
The following index would help:
CREATE INDEX ON employees USING gin (mobile);
I have tricky query that attempts to find matches that compare a list of JSON arrays against a list of JSON values in a column
The "Things" table with "Keywords" column would contain something such as:
'["car", "house", "boat"]'::JSONB
The query would contain the values:
'["car", "house"]'::JSONB
I'd like to find all the rows that have BOTH "car" and "house" contained in the listing. Here's my (mostly) feeble attempt:
SELECT
*
FROM
"Things"
WHERE
"Keywords"::JSONB ?| ARRAY(
SELECT * FROM JSONB_ARRAY_ELEMENTS('["house","car"]'::JSONB)
)::TEXT[]
Also when it comes to indexing, I'm assuming adding a GIST index would be my best option.
I'd like to find all the rows that have BOTH "car" and "house"
So the right operator is ?& - Do all of these array strings exist as top-level keys?
You query is almost correct, change the operator and use jsonb_array_elements_text():
WITH "Things"("Keywords") AS (
VALUES
('["car", "house", "boat"]'::jsonb),
('["car", "boat"]'),
('["car", "house"]'),
('["house", "boat"]')
)
SELECT
*
FROM
"Things"
WHERE
"Keywords" ?& array(
SELECT jsonb_array_elements_text('["house","car"]')
)
Keywords
--------------------------
["car", "house", "boat"]
["car", "house"]
(2 rows)
The query would be simpler if the argument could be written down as a regular array of text:
SELECT
*
FROM
"Things"
WHERE
"Keywords" ?& array['house', 'car']
In both cases you can use a GIN index:
The default GIN operator class for jsonb supports queries with top-level key-exists operators ?, ?& and ?| operators (...)
I have an array of jsonb elements (jsonb[]), with id and text. To remove an element I could use:
UPDATE "Users" SET chats = array_remove(chats, '{"id": 2, "text": "my message"')
But I want to delete the message just by the id, cause getting the message will cost me another query.
Assuming missing information:
Your table has a PK called user_id.
You want to remove all elements with id = 2 across the whole table.
You don't want to touch other rows.
id is unique within each array of chats.
UPDATE "Users" u
SET chats = array_remove(u.chats, d.chat)
FROM (
SELECT user_id, chat
FROM "Users", unnest(chats) chat
WHERE chat->>'id' = '2'
) d
WHERE d.user_id = u.user_id;
The following explanation matches the extent of provided information in the question: