Your table definition looks reasonable all over now. With all columns `NOT NULL` the `UNIQUE` constraint will work as expected - except for typos and minor differences in spelling, which may be rather common I am afraid. Consider [@a_horse's comment][1].

###Alternative with functional unique index

The other option would be **functional unique index** (similar to what [@Dave commented][2]). But I would use a `uuid` data type to index size and performance.

The cast from array to text is not `IMMUTABLE` (due to its generic implementation):

- [Indexing an array for full text search][3]

Hence you need a little helper function to *declare* it immutable:

    CREATE OR REPLACE FUNCTION f_movie_uuid(_title text
                                          , _runtime int2
                                          , _released_in int2
                                          , _genres text[]
                                          , _tags text[]
                                          , _origin text[])
      RETURNS uuid LANGUAGE sql IMMUTABLE AS  -- faking IMMUTABLE
    'SELECT md5(_title || _runtime::text || _released_in::text
             || _genres::text || _tags::text || _origin::text)::uuid';

Use it for the index definition:

    CREATE UNIQUE INDEX movies_uni_idx
    ON movies (f_movie_uuid(title,runtime,released_in,genres,tags,origin));

[**SQL Fiddle.**][4]

More Details:

- http://dba.stackexchange.com/questions/89429/would-index-lookup-be-noticeably-faster-with-char-vs-varchar-when-all-values-are/89433#89433
- [Convert hex in text representation to decimal number][5]

You might use the generated UUID as PK, but I would still rather use the serial column with it's 4 bytes, which is simple and cheap for FK references and all other purposes. The UUID would be a great option for distributed systems that need to generate a PK independently. Or for very huge tables, but there aren't nearly enough movies in our solar system for that.

###Pros and Cons
The ***constraint*** is implemented with a `UNIQUE` index on the involved columns. Put relevant columns in the constraint definition first and you have useful index for other purposes as collateral benefit.

There are other small benefits:

- [How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?][6]

The ***functional unique index*** is (potentially much) smaller in size, which can make it substantially faster. If your columns are not too big, the difference won't be much. There is also the small overhead cost for the calculation.

Concatenating all columns can introduce false positives (`'foo ' || 'bar' = 'foob ' || 'ar'`, but that seems *very* unlikely for this case. Typos are so much more likely that you can safely ignore it here.

Aside:  
If you are using Postgres 9.4 or later consider [`jsonb` instead of `json`][7].


  [1]: http://dba.stackexchange.com/questions/108210/is-it-reasonable-to-mark-all-columns-but-one-as-primary-key#comment197000_108210
  [2]: http://dba.stackexchange.com/questions/108210/is-it-reasonable-to-mark-all-columns-but-one-as-primary-key#comment197033_108210
  [3]: http://stackoverflow.com/a/31213069/939860
  [4]: http://sqlfiddle.com/#!15/0d370/1
  [5]: http://stackoverflow.com/a/8335376/939860
  [6]: http://stackoverflow.com/a/9067108/939860
  [7]: http://www.postgresql.org/docs/devel/static/datatype-json.html