1

I'm confused by how path uses different formats depending on the function in the PostgreSQL JSONB documentation.

If I had a PostgreSQL table foo that looks like

pk json_obj
0 {"values": [{"id": "a_b", "value": 5}, {"id": "c_d", "value": 6]}
1 {"values": [{"id": "c_d", "value": 7}, {"id": "e_f", "value": 8]}

Why does this query give me these results?

SELECT json_obj,                                          -- {"values": [{"id": "a_b", "value": 5}, {"id": "c_d", "value": 6]}
       json_obj @? '$.values[*].id',                      -- true
       json_obj #> '$.values[*].id',                      -- ERROR: malformed array literal
       json_obj #> '{values, 0, id}',                     -- "a_b"
       JSONB_SET(json_obj, '$.annotations[*].id', '"hi"') -- ERROR: malformed array literal
FROM foo;

Specifically, why does @? support $.values[*].id (described on that page in another section) but JSONB_SET uses some other path format {bar,3,baz}?

Ultimately, what I would like to do and don't know how, is to remove non-alphanumeric characters (e.g. underscores in this example) in all id values represented by the path $.values[*].id.

1
  • 1
    Because @? supports SQL/JSON path and #> does not. jsonb_set() and #> were introduced before Postgres supported SQL/JSON path (I think even before SQL/JSON path was included into the SQL standard) Commented Feb 25, 2021 at 6:45

2 Answers 2

4

The reason is that the operators have different data types on the right hand side.

SELECT oprname, oprright::regtype
FROM pg_operator
WHERE oprleft = 'jsonb'::regtype
  AND oprname IN ('@?', '#>');

 oprname | oprright 
---------+----------
 #>      | text[]
 @?      | jsonpath
(2 rows)

Similarly, the second argument of jsonb_set is a text[].

Now '$.values[*].id' is a valid jsonpath, but not a valid text[] literal.

Sign up to request clarification or add additional context in comments.

3 Comments

What operators are valid for the test[] parameter of jsonb_set function and how do they work? I couldn't find a list, only saw in examples that a number seems to mean item at index and a string a property in an object, anything else?
@FireEmerald That looks like a new question.
0

Thanks for the answers and comments about why the data types were different.

I wanted to post how I solved my problem:

Ultimately, what I would like to do and don't know how, is to remove non-alphanumeric characters (e.g. underscores in this example) in all id values represented by the path $.values[*].id.

WITH unnested AS (
    SELECT f.pk, JSONB_ARRAY_ELEMENTS(f.json_obj -> 'values') AS value
    FROM foo f
),
updated_values AS (
    SELECT un.pk, JSONB_SET(un.value, '{id}', TO_JSONB(LOWER(REGEXP_REPLACE(un.value ->> 'id', '[^a-zA-Z0-9]', '', 'g'))), FALSE) AS new_value
    FROM unnested un
    WHERE value -> 'id' IS NOT NULL -- Had some values that didn't have 'id' keys
)
UPDATE foo f2
SET json_obj = JSONB_SET(f2.json_obj, '{values}', (SELECT JSONB_AGG(uv.new_value) FROM updated_values uv WHERE uv.pk = f2.pk), FALSE)
WHERE JSONB_PATH_EXISTS(f2.json_obj, '$.values[*].id') -- Had some values that didn't have 'id' keys

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.