-- -- json / jsonb: both arrive as Ruby Strings holding the JSON text; Ruby's -- stdlib json parses and generates them. Returning a String into a -- json/jsonb result converts through the type's input function. -- -- jsonb arrives as a String in jsonb's canonical text form. CREATE FUNCTION jb_class(jsonb) RETURNS text LANGUAGE plruby AS $$ "#{args[0].class.name}: #{args[0]}" $$; SELECT jb_class('{"b":1, "a": [true, null, 2]}'); -- Parse and extract. CREATE FUNCTION jb_get(jsonb, text) RETURNS text LANGUAGE plruby AS $$ require 'json' JSON.parse(args[0])[args[1]].to_s $$; SELECT jb_get('{"name": "widget", "qty": 7}', 'name'); SELECT jb_get('{"name": "widget", "qty": 7}', 'qty'); -- Recursive work over a document: sum every number anywhere in it. CREATE FUNCTION jb_sum_numbers(jsonb) RETURNS numeric LANGUAGE plruby AS $$ require 'json' walk = lambda { |v| case v when Numeric then v when Array then v.sum { |e| walk.call(e) } when Hash then v.values.sum { |e| walk.call(e) } else 0 end } walk.call(JSON.parse(args[0])).to_s $$; SELECT jb_sum_numbers('{"a": 1, "b": [2, {"c": 3.5}], "d": "x", "e": null}'); -- Build a jsonb value from Ruby data; usable with jsonb operators. CREATE FUNCTION jb_build(int) RETURNS jsonb LANGUAGE plruby AS $$ require 'json' JSON.generate({'n' => args[0], 'squares' => (1..args[0]).map { |i| i * i }}) $$; SELECT jb_build(3); SELECT jb_build(4)->>'n' AS n, jsonb_array_length(jb_build(4)->'squares') AS len; -- A jsonb array unnested into a set of rows. CREATE FUNCTION jb_names(jsonb) RETURNS SETOF text LANGUAGE plruby AS $$ require 'json' JSON.parse(args[0]).each { |e| return_next e['name'] } nil $$; SELECT * FROM jb_names('[{"name": "a"}, {"name": "b"}, {"name": "c"}]'); -- Plain json (not jsonb) preserves its original text verbatim. CREATE FUNCTION j_echo(json) RETURNS text LANGUAGE plruby AS $$ args[0] $$; SELECT j_echo('{"kept": "as-is", "dup": 1, "dup": 2}'); -- Returning malformed JSON into a jsonb result is rejected cleanly. CREATE FUNCTION jb_bad() RETURNS jsonb LANGUAGE plruby AS $$ '{"unterminated": ' $$; SELECT jb_bad();