Reputation: 1217
I have a column in jsonb
storing a map, like {'a':1,'b':2,'c':3}
where the number of keys is different in each row.
I want to count it -- jsonb_object_keys can retrieve the keys but it is in setof
Are there something like this?
(select count(jsonb_object_keys(obj) from XXX )
(this won't work as ERROR: set-valued function called in context that cannot accept a set
)
Postgres JSON Functions and Operators Document
json_object_keys(json)
jsonb_object_keys(jsonb)
setof text Returns set of keys in the outermost JSON object.
json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}')
json_object_keys
------------------
f1
f2
Crosstab isn't feasible as the number of key could be large.
Upvotes: 31
Views: 29322
Reputation: 2007
To simplify the effort, you can define a function as follows:
CREATE OR REPLACE FUNCTION json_map_length(json) RETURNS integer AS
$$ SELECT array_length(array_agg(A.key), 1) FROM (
SELECT json_object_keys($1) as key
) A; $$
LANGUAGE sql IMMUTABLE;
Upvotes: 0
Reputation: 4774
Shortest:
SELECT count(*) FROM jsonb_object_keys('{"a": 1, "b": 2, "c": 3}'::jsonb);
Returns 3
If you want all json number of keys from a table, it gives:
SELECT (SELECT COUNT(*) FROM jsonb_object_keys(myJsonField)) nbr_keys FROM myTable;
Edit: there was a typo in the second example.
Upvotes: 38
Reputation: 1636
While a sub select must be used to convert the JSON keys set to rows, the following tweaked query might run faster by skipping building the temporary array:
SELECT count(*) FROM
(SELECT jsonb_object_keys('{"a": 1, "b": 2, "c": 3}'::jsonb)) v;
and it's a bit shorter ;)
To make it a function:
CREATE OR REPLACE FUNCTION public.count_jsonb_keys(j jsonb)
RETURNS bigint
LANGUAGE sql
AS $function$
SELECT count(*) from (SELECT jsonb_object_keys(j)) v;
$function$
Upvotes: 5
Reputation: 8572
Alternately, you could simply return the upper bounds of the keys when listed as an array:
SELECT
ARRAY_UPPER( -- Grab the upper bounds of the array
ARRAY( -- Convert rows into an array.
SELECT JSONB_OBJECT_KEYS(obj)
),
1 -- The array's dimension we're interested in retrieving the count for
) AS count
FROM
xxx
Using '{"a": 1, "b": 2, "c": 3}'::jsonb
as obj, count would result in a value of three (3).
Pasteable example:
SELECT
ARRAY_UPPER( -- Grab the upper bounds of the array
ARRAY( -- Convert rows into an array.
SELECT JSONB_OBJECT_KEYS('{"a": 1, "b": 2, "c": 3}'::jsonb)
),
1 -- The array's dimension we're interested in retrieving the count for
) AS count
Upvotes: 4
Reputation: 2253
You could convert keys to array and use array_length to get this:
select array_length(array_agg(A.key), 1) from (
select json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}') as key
) A;
If you need to get this for the whole table, you can just group by primary key.
Upvotes: 14