2

I wonder if it is possible to achieve uniqueness in an array of objects, only using one field of the object as the determinant if the object is unique or not with jsonb.

An example of what I mean:

I want to ensure that if the field of type jsonb looks like this:

"[{"x":"a", "timestamp": "2016-12-26T12:09:43.901Z"}]"

then I want to have a constraint that forbids me to put another entry with "x":"a" regardless of what the timestamp(or any other field for that matter) is on the new object I'm trying to enter

6
  • Try to use unique indexes postgresql.org/docs/current/indexes-expressional.html Commented Nov 15, 2022 at 8:05
  • CREATE UNIQUE INDEX your_index ON table_name( (data->>'x') ); Commented Nov 15, 2022 at 8:09
  • 1
    It's unclear to me if you want to prevent duplicates in a single array (=column value), or duplicates across rows in the table. Commented Nov 15, 2022 at 8:20
  • prevent duplicates(based on only one field in an object) in a single array @a_horse_with_no_name Commented Nov 15, 2022 at 8:20
  • But another array element with "x": "bla" would be ok? Commented Nov 15, 2022 at 8:24

3 Answers 3

4

This can be achieved with a check constraint.

create table the_table
(
  id int primary key, 
  data jsonb,
  constraint check_single_xa 
     check (jsonb_array_length(jsonb_path_query_array(data, '$[*] ? (@.x == "a").x')) <= 1)
);

This prevents multiple array elements with "x": "a". If you want to prevent multiple array elements with the key x (regardless of the value), you can use:

  constraint check_single_xa 
     check (jsonb_array_length(jsonb_path_query_array(data, '$[*].x')) <= 1)
Sign up to request clarification or add additional context in comments.

4 Comments

Ok, I will try this out a bit later and get back to you :)
Looking at this though I want to allow the same key "x" just no duplicate values for it among any of the objects of the array. I don't specifically care about "a", I just don't want multiple "a"'s. Does this constraint do that?
@D.O.: that's the second check constraint in my ansser
Ok I checked it now, your second constraint seems to do what I want. I'll accept this as answer :)
3

You can do this using a CHECK constraint and a helper function:

CREATE FUNCTION is_x_unique(arr jsonb) RETURNS boolean
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT
    RETURN NOT EXISTS(SELECT * FROM jsonb_array_elements(arr) GROUP BY value->'x' HAVING COUNT(*) > 1);

(online fiddle)

ALTER TABLE example ADD CONSTRAINT unique_x_in_data CHECK is_x_unique(data);

Comments

-1

The unique way to do so is to have a JSON typed datatype for your column with a JSON schema with "uniqueItems": true .

Unfortunately PostgreSQL does not accepts such syntax to enforce checks. You have to do it outside the PG Cluster...

7 Comments

This is wrong, this can easily be expressed using a check constraint. And there are multiple extensions that provide JSON schema validation.
Of course you can externalise some JSON data and add a SQL CONSTRAINT on it. But it is not the easiest way nor the performer one... And passing by a function will lead by some extraoverhead and no possibilities to parallelise the execution of the INSERT/UPDATE queries. The example given by Bergi encapsulates one of the worst function in terme of performances (jsonb_array_elements) inside another function...
There is no need to pass anything to a function. A plain CHECK constraint will do this. A check constraint will most certainly not prevent concurrent inserts or updates on the table, not even with the function from Bergi's answer. So how would this be done in your beloved sql serVER?
My answer contains such a constraint
Yes, two built-in functions. Why would that be a problem? It's no different to a check constraint like say lower(name) = name or trim(name) <> ''
|

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.