I use Postgres JSONB columns to store translations for dynamic content of a multi-language site — locales are keys, translated strings are values ({"en": "Hello", "ru": "Привет"}
).
There're some rare cases when I want to ensure that each translation is unique per locale. I know that I can create an index for each locale:
CREATE UNIQUE INDEX ON categories ((title ->> 'en'));
CREATE UNIQUE INDEX ON categories ((title ->> 'ru'));
This solution has one downside that I would need to create a new index for every column which has such constraint if I decide to add a new locale. Is there any way to specify a uniqueness constraint that will hold for every key-value pair of a JSONB object?
EDIT 1 (in response to the Evan's answer). There seems to be some sort of confusion cased by the term "unique/duplicate translation per locale", so I will try to clarify it. "Unique translation per locale" means: for every locale of each record in the table, there is no other record that has exactly the same translation for that locale. "Locale" is the key of the JSONB object that holds translations. "Translation" is the value of the JSONB object.
Here are some examples: the code
INSERT INTO categories(title) VALUES
('{"en": "Hello"}'),
('{"en": "Hello"}');
should yield a uniqueness constraint violation error because it has duplicate "Hello" translation for the en
locale, while the code
INSERT INTO categories(title) VALUES
('{"en": "Hello"}'),
('{"en": "Hi"}');
should not, because it has different translations for the en
locale.
Best Answer
Failed attempt with Exclusion Constraints
I don't believe this is possible to do this with exclusion DDL because I don't believe PostgreSQL supports exclusion constraints on
text[]
Helper function
Exclusion constraint attempt.
I'm asking a more specific question on this right now.
Other solutions
As it pertains to your problem, I believe currently you'll have to resort to a less than optimal solution like,
text -> int
and then map overtext[]
returningint[]
and use intarray which will permit a GIST index and&&
Or, of course, you can just fix your schema.
Trigger
This method should also be able to make use of a
GIST
index oncatagories(title)
Testing