Postgres 9.4 jsonb queries basic operators - json

Is there any way to make queries on a jsonb field in some table in Postgres that are basically equitable to the Mongodb query operators (listed here https://docs.mongodb.org/manual/reference/operator/query-comparison/)
I would like to be able to store some json objects in a postgres table for example:
{"power": 200},
{"power": 400},
{"power": 0},
{"power": 146297},
If I do the current method of
SELECT * FROM mytable where json_field ->> 'power' < '2';
I get retured both the row for power 0 and power 146297...
Is there some documentation somewhere that specifies how to do
gt, gte, lt, lte, eq, not equal, in array, not in array

You need to cast ->> string result values:
WITH mytable(json_field) AS ( VALUES
('{"power": 200}'::JSONB),
('{"power": 400}'::JSONB),
('{"power": 0}'::JSONB),
('{"power": 146297}'::JSONB)
)
SELECT * FROM mytable where (json_field->>'power')::INTEGER < 2;
Result is:
json_field
--------------
{"power": 0}
(1 row)

The documentation is on the postgresql page. The documentation states that the ->> operator returns string, your right hand operand is a string too so the result is correct.
To do what you wanted to do you must cast the result returned from the json to integer:
SELECT * FROM mytable where (json_field ->> 'power')::int < '2';
Please note the brackets are needed as not to case 'power' to int.

Related

Query in json in postgres with multiple values

I have json stored [1,2,4,5].I have array s like [1,23,4]. I want to check either value of s array exist in json fields. Other options I may need to store array like ['1','2','4','5'] so i can use?| operator or their is anything else i can do ?
There is no ready-to-use function or operator to accomplish that. You can use the function:
create or replace function jsonb_int_array(jsonb)
returns integer[] language sql immutable as $function$
select array(select jsonb_array_elements_text($1)::int)
$function$;
In the query use the overlap operator:
with my_table(data) as (
values ('[1,2,4,5]'::jsonb)
)
select *
from my_table
where jsonb_int_array(data) && array[1,23,4]
data
--------------
[1, 2, 4, 5]
(1 row)

Querying a JSON array of objects in Postgres

I have a postgres db with a json data field.
The json I have is an array of objects:
[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]
I'm trying to return values for a specific key in a JSON array, so in the above example I'd like to return the values for name.
When I use the following query I just get a NULL value returned:
SELECT data->'name' AS name FROM json_test
Im assuming this is because it's an array of objects? Is it possible to directly address the name key?
Ultimately what I need to do is to return a count of every unique name, is this possible?
Thanks!
you have to unnest the array of json-objects first using the function (json_array_elements or jsonb_array_elements if you have jsonb data type), then you can access the values by specifying the key.
WITH json_test (col) AS (
values (json '[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]')
)
SELECT
y.x->'name' "name"
FROM json_test jt,
LATERAL (SELECT json_array_elements(jt.col) x) y
-- outputs:
name
--------------
"Mickey Mouse"
"Donald Duck"
To get a count of unique names, its a similar query to the above, except the count distinct aggregate function is applied to y.x->>name
WITH json_test (col) AS (
values (json '[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]')
)
SELECT
COUNT( DISTINCT y.x->>'name') distinct_names
FROM json_test jt,
LATERAL (SELECT json_array_elements(jt.col) x) y
It is necessary to use ->> instead of -> as the former (->>) casts the extracted value as text, which supports equality comparison (needed for distinct count), whereas the latter (->) extracts the value as json, which does not support equality comparison.
Alternatively, convert the json as jsonb and use jsonb_array_elements. JSONB supports the equality comparison, thus it is possible to use COUNT DISTINCT along with extraction via ->, i.e.
COUNT(DISTINCT (y.x::jsonb)->'name')
updated answer for postgresql versions 12+
It is now possible to extract / unnest specific keys from a list of objects using jsonb path queries, so long as the field queried is jsonb and not json.
example:
WITH json_test (col) AS (
values (jsonb '[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]')
)
SELECT jsonb_path_query(col, '$[*].name') "name"
FROM json_test
-- replaces this original snippet:
-- SELECT
-- y.x->'name' "name"
-- FROM json_test jt,
-- LATERAL (SELECT json_array_elements(jt.col) x) y
Do like this:
SELECT * FROM json_test WHERE (column_name #> '[{"name": "Mickey Mouse"}]');
You can use jsonb_array_elements (when using jsonb) or json_array_elements (when using json) to expand the array elements.
For example:
WITH sample_data_array(arr) AS (
VALUES ('[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]'::jsonb)
)
, sample_data_elements(elem) AS (
SELECT jsonb_array_elements(arr) FROM sample_data_array
)
SELECT elem->'name' AS extracted_name FROM sample_data_elements;
In this example, sample_data_elements is equivalent to a table with a single jsonb column called elem, with two rows (the two array elements in the initial data).
The result consists of two rows (one jsonb column, or of type text if you used ->>'name' instead):
extracted_name
----------------
"Mickey Mouse"
"Donald Duck"
(2 rows)
You should them be able to group and aggregate as usual to return the count of individual names.

Query json column based on the integer value in Postgres

I am using postgres v9.3
I have a table called temp which have a column all_data. The value looks something like below :-
{"Accountid" : "1364", "Personalid" : "4629-87c3-04e6a7a60208", "quote_number" : "QWQA62364384"}
Now, I want to query the all_data column by accountid=1364.
Could you please tell what would be the query?
Use the ->> operator
select *
from temp
where all_data ->> 'Accountid' = '1364';
Online example for 9.3: http://sqlfiddle.com/#!15/55981/1
However the above will not work if the JSON contains an array instead of a JSON object. E.g. a value like '[1,2]'::json will cause that query to fail.
With 9.4 and above you could check that using json_typeof() but with your soon to be unsupported version the only workaround I can think of is to convert the column to text and exclude those that start with a [
with valid_rows as (
select *
from temp
where all_data::text not like '[%'
)
select *
from valid_rows
where all_data ->> 'Accountid' = '1364';
Online example for 9.3: http://sqlfiddle.com/#!15/d01f43/3

Mysql 5.7 json column WHERE IN not working as expected

This sql returns correct result:
select * from `user` where `profile`->"$.year" IN ("2001")
But when I add more than one values
select * from `user` where `profile`->"$.year" IN ("2001", "1")
returns empty
It seems "In" statement not working as expected on JSON column in Mysql 5.7?
See documentation:
11.6 The JSON Data Type
...
Comparison and Ordering of JSON Values
...
The following comparison operators and functions are not yet
supported with JSON values:
BETWEEN
IN()
GREATEST()
LEAST()
A workaround for the comparison operators and functions just listed is
to cast JSON values to a native MySQL numeric or string data type so
they have a consistent non-JSON scalar type.
...
Try:
SELECT *
FROM `user`
WHERE CAST(`profile` -> "$.year" AS UNSIGNED) IN ("2001", "1");
See dbfiddle.

Trying to query a JSON array of non-objects in Postgresql 9.3

I'm trying to query a table with a JSON column which will always hold an array of "primitive" values (i.e. integers, strings, booleans -- not objects or arrays).
My query should be similar to [ref2], but I can't do ->>'id' because I'm not trying to access a JSON object but the value itself.
In the [ref1] fiddle (blatant fork from the above), there's and incomplete query... I'd like to query all things which contain 3 among its values.
Even more so, I'd like some rows to have arrays of strings, other rows to have arrays of integers, and other ones arrays of booleans... So casting is undesiderable.
I believe ->> returns the original JSON value type, but I need the "root" object... That is, my JSON value is [1,2,3,4], using json_array_elements should yield e.g. 2, but that is a JSON type according to my tests.
Upgrading to 9.4 is planned in the near future, but I haven't read anything yet that gave me a clue jsonb would help me.
UPDATE: at the moment, I'm (1) making sure all values are integers (mapping non-integers values to integers), which is suboptimal; (2) querying like this:
SELECT *
FROM things, json_array_elements(things.values) AS vals
WHERE vals.value::text::integer IN (1,2,3);
I need the double casting (otherwise it complains that cannot cast type json to integer).
ref1: http://sqlfiddle.com/#!15/5febb/1
ref2: How to query an array of JSON in PostgreSQL 9.3?
Rather than using json_array_elements you can unpack the array with generate_series, using the ->> operator to extract a text representation.
SELECT things.*
FROM things
CROSS JOIN generate_series(0, json_array_length(values) - 1) AS idx
WHERE values ->> idx = '1'
GROUP BY things.id;
This is a workaround for the lack of json_array_elements_text in 9.3.
You need an operator(=) for json to do this without either messing with casting or relying on the specific textual representations of integers, booleans, etc. operator(=) is only available for jsonb. So in 9.3 you're stuck with using the text representation (so 1.00 won't = 1) or casting to a PostgreSQL type based on the element type.
In 9.4 you could use to_json and the jsonb operator(=), e.g.:
SELECT things.*
FROM things
CROSS JOIN generate_series(0, json_array_length(values) - 1) AS idx
WHERE (values -> idx)::jsonb = to_json(1)::jsonb
GROUP BY things.id;
id | date | values
----+-------------------------------+---------
1 | 2015-08-09 04:54:38.541989+08 | [1,2,3]
(1 row)