Skip to content

Support Nested Collections in add and find Functions #32

@artsolo-db

Description

@artsolo-db

Description

Currently, pgCollection supports storing collections inside collections. However, reading and writing nested collections is verbose for 2-level, 3-level, or deeper structures:

DO $$
DECLARE
  arr_instance collection('collection');
  var1 text;
BEGIN
  -- Write a 3-level nested array
  arr_instance := add(arr_instance, 'A', 
    add(CASE WHEN exist(arr_instance, 'A') THEN find(arr_instance, 'A', NULL::collection) ELSE NULL::collection END, 'AA',
      add(CASE WHEN exist(arr_instance, 'A') AND exist(find(arr_instance, 'A', NULL::collection), 'AA') 
           THEN find(find(arr_instance, 'A', NULL::collection), 'AA', NULL::collection) 
           ELSE NULL::collection END, 'AAA', 'some text'::text)));

  -- Read a 3-level nested array
  var1 := (find(find(arr_instance, 'A', NULL::collection), 'AA', NULL::collection))['AAA']::text;

  RAISE NOTICE 'var1: %', var1;
END $$;

This becomes cumbersome for 2-level, 3-level, or deeper nested collections.


Proposal

Main Proposal (preferred)

Extend the existing add and find functions to accept a variadic list of keys, enabling direct read/write of nested collections:

add(collection, value, VARIADIC keys text[])
find(collection, dummy, VARIADIC keys text[])

Alternative

Create new functions (to avoid changing existing APIs):

add2(collection, value, VARIADIC keys text[])
find2(collection, dummy, VARIADIC keys text[])

Examples of Usage

Writing Nested Collections

DO $$
DECLARE
  arr collection('collection');
BEGIN
  arr := add(arr, 'Hello', 'A','B','C');  -- 3-level nested write
END $$;

Reading Nested Collections

DO $$
DECLARE
  arr collection('collection');
  val text;
BEGIN
  val := find(arr, NULL::text, 'A','B','C');  -- 3-level nested read
END $$;

Implementation (hidden)

Click to expand the proposed `add` and `find` VARIADIC implementations
-- find: traverse delegating to built-in find()
CREATE OR REPLACE FUNCTION find(coll collection, dummy anyelement, VARIADIC keys text[])
RETURNS anyelement
LANGUAGE plpgsql
AS $$
DECLARE
  cur_coll collection := coll;
  key text;
  n int := coalesce(array_length(keys,1), 0);
  i int;
BEGIN
  IF n = 0 THEN
    RETURN NULL;
  END IF;

  IF n > 1 THEN
    FOR i IN 1 .. n-1 LOOP
      key := keys[i];
      -- delegate to built-in find - preserves its error behavior
      cur_coll := find(cur_coll, key, NULL::collection);
    END LOOP;
  END IF;

  key := keys[n];
  RETURN find(cur_coll, key, dummy);
END;
$$;


-- add: recursive, builds/merges nested collections correctly
CREATE OR REPLACE FUNCTION add(coll collection, val anyelement, VARIADIC keys text[])
RETURNS collection
LANGUAGE plpgsql
AS $$
DECLARE
  n int := coalesce(array_length(keys,1),0);
  first_key text;
  rest_keys text[];
  child collection;
  base_coll collection := coalesce(coll, NULL::collection);
BEGIN
  IF n = 0 THEN
    RAISE EXCEPTION 'add: no keys provided';
  END IF;

  -- base case: single key -> use built-in add(collection, key, value)
  IF n = 1 THEN
    RETURN add(base_coll, keys[1], val);
  END IF;

  -- prepare first key and rest slice
  first_key := keys[1];
  rest_keys := keys[2:n];

  -- get existing child (if present), otherwise start with NULL::collection
  IF exist(base_coll, first_key) THEN
    child := find(base_coll, first_key, NULL::collection);
  ELSE
    child := NULL::collection;
  END IF;

  -- update the child recursively
  child := add(child, val, VARIADIC rest_keys);

  -- put updated child back into parent collection
  RETURN add(base_coll, first_key, child);
END;
$$;

Pros

  • Allows direct reading and writing of nested collections.
  • Makes code cleaner and more readable for deep nesting.

Cons

  • Changing the sequence of input parameters for add and find may break existing code (if modifying the original functions).
  • Using new functions (add2/find2) avoids breaking existing code but adds similar APIs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions