vincentlcy
vincentlcy

Reputation: 1217

How to count setof / number of keys of JSON in postgresql?

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

Answers (5)

Tom Yeh
Tom Yeh

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

Le Droid
Le Droid

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

mindex
mindex

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

Joshua Burns
Joshua Burns

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

hruske
hruske

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

Related Questions