Search code examples
postgresqljsonbpostgresql-9.5

How to push a JSON object to a nested array in a JSONB column


I need to somehow push a JSON object to a nested array of potentionally existing JSON objects - see "pages" in the below JSON snippet.

{
    "session_id": "someuuid",
    "visitor_ui": 1,
    "pages": [
        {
            "datetime": "2016-08-13T19:45:40.259Z",
            "duration,": 0,
            "device_id": 1,
            "url": {
                "path": "/"
            }
        },
        {
            "datetime": "2016-08-14T19:45:40.259Z",
            "duration,": 0,
            "device_id": 1,
            "url": {
                "path": "/test"
            }
        },
        // how can i push a new value (page) here??
    ]
    "visit_page_count": 2
}

I'm aware of the jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean]) (although still finding it a bit hard to comprehend) but I guess using that, would require that I first SELECT the whole JSONB column, in order to find out how many elements inside "pages" already exists and what index to push it to using jsonb_set, right? I'm hoping theres a way in Postgres 9.5 / 9.6 to achieve the equivalent of what we know in programming languages eg. pages.push({"key": "val"}).

What would be the best and easiest way to do this with Postgresql 9.5 or 9.6?


Solution

  • The trick to jsonb_set() is that it modifies part of a jsonb object, but it returns the entire object. So you pass it the current value of the column and the path you want to modify ("pages" here, as a string array), then you take the existing array (my_column->'pages') and append || the new object to it. All other parts of the jsonb object remain as they were. You are effectively assigning a completely new object to the column but that is irrelevant because an UPDATE writes a new row to the physical table anyway.

    UPDATE my_table
    SET my_column = jsonb_set(my_column, '{pages}', my_column->'pages' || new_json, true);
    

    The optional create_missing parameter set to true here adds the "pages" object if it does not already exist.