Skip to content

Instantly share code, notes, and snippets.

@ppKrauss
Created August 16, 2018 07:34
Show Gist options
  • Save ppKrauss/679cea825002076c8697e734763076b9 to your computer and use it in GitHub Desktop.
Save ppKrauss/679cea825002076c8697e734763076b9 to your computer and use it in GitHub Desktop.
Tool-kit for JSONb key-number objects
/**
* object_summable and "Merge sum" functions are for JSONb key-number objects,
* most usually key-integer for counting, like json-bags.
* They are "key counters", so, to merge two keys, the intValues must be added.
* Change the core of jsonb_merge_sum(jsonb,jsonb) to the correct datatype.
* The JSON "number" is equivalent to the SQL's ::numeric.
* Any invalid or empty JSONb object will be represented as SQL NULL.
*/
CREATE or replace FUNCTION jsonb_is_summable(jsonb) RETURNS boolean AS $f$
SELECT not($1 IS NULL OR jsonb_typeof($1)!='object' OR $1='{}'::jsonb)
AND (SELECT bool_and(jsonb_typeof(value)='number') FROM jsonb_each($1))
-- for bigint use jsonb_each_text() with regex, value ~ '^\d+$'.
$f$ language SQL IMMUTABLE;
CREATE or replace FUNCTION jsonb_merge_sum( jsonb, jsonb ) RETURNS jsonb AS $f$
SELECT CASE
WHEN emp1 AND emp2 THEN NULL
WHEN emp2 THEN $1
WHEN emp1 THEN $2
ELSE $1 || (
-- CHANGE this core operation to enforce ::number or ::float or ::bigint
SELECT jsonb_object_agg( key, value::int + COALESCE(($1->>key)::int,0) )
FROM jsonb_each_text($2)
) END
FROM (
SELECT $1 IS NULL OR jsonb_typeof($1)!='object' OR $1='{}'::jsonb emp1,
$2 IS NULL OR jsonb_typeof($2)!='object' OR $2='{}'::jsonb emp2
) t
$f$ language SQL IMMUTABLE;
CREATE or replace FUNCTION jsonb_merge_sum( jsonb[] ) RETURNS jsonb AS $f$
DECLARE
x JSONb;
j JSONb;
BEGIN
IF $1 IS NULL OR array_length($1,1)=0 THEN
RETURN NULL;
ELSEIF array_length($1,1)=1 THEN
RETURN $1[1];
END IF;
x := $1[1];
FOREACH j IN ARRAY $1[2:] LOOP
x:= jsonb_merge_sum(x,j);
END LOOP;
RETURN x;
END
$f$ LANGUAGE plpgsql IMMUTABLE;
CREATE or replace FUNCTION jsonb_merge_sum( jsonb[], jsonb[] ) RETURNS jsonb[] AS $f$
SELECT CASE
WHEN $2 IS NULL THEN $1
WHEN $1 IS NULL THEN $2
ELSE (
SELECT array_agg( jsonb_merge_sum(j1,j2) )
FROM (
SELECT unnest(a) j1, unnest(b) j2
FROM array_fillto_duo($1,$2) t(a,b)
) t
) END
$f$ language SQL IMMUTABLE;
DROP AGGREGATE IF EXISTS jsonb_agg_mergesum(jsonb);
CREATE AGGREGATE jsonb_agg_mergesum(jsonb) ( -- important!
SFUNC=jsonb_merge_sum,
STYPE=jsonb,
INITCOND=NULL
);
/*
under review for use with initcond=NULL
DROP AGGREGATE IF EXISTS jsonb_agg_mergesum(jsonb[]);
CREATE AGGREGATE jsonb_agg_mergesum(jsonb[]) ( -- low use
SFUNC=jsonb_merge_sum,
STYPE=jsonb[],
INITCOND='{}' -- test with null
);
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment