The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Elastic::Manual::Attributes::Unique - Making attributes unique

VERSION

version 0.27

INTRODUCTION

The only unique constraint available in Elasticsearch is the document ID. Typically, if you want a document to be unique, you use the unique value as the ID.

However, sometimes you don't want to do this. For instance, you may want to use the email address as a unique constraint for your user accounts, but you also want to be able to link to a user account without exposing their email address, and let the user change their email address without having to update the ID of their user account wherever it is used.

In this case, we want the ID of the user document to be auto-generated, but we also want the value of the email attribute to be unique.

STORING UNIQUE KEYS

Elastic::Model uses ElasticSearchX::UniqueKey to enable unique constraints. Your unique attributes are tracked in a special index which defaults to "unique_key", but which can be specified in your Model class:

    package MyApp;

    use Elastic::Model;

    has_namespace 'foo' .....;

    has_unique_index 'myapp_uniques';

The index will be created automatically.

APPLYING UNIQUE CONSTRAINTS

Any attribute whose value is a string (including numeric attributes) can have a unique constraint applied:

    has 'email' => (
        is          => 'rw',
        isa         => 'Str',
        unique_key  => 'myapp_email'
    );

The unique_key value will be used as the type in the unique keys index. For instance, if the email is john@foo.com, then the unique entry for this document will be stored in index:myapp_uniques, under type:myapp_email with id:john@foo.com.

The unique_key can only be used once in a doc class. You can't have (eg) the attributes email_1 and email_2 both using a unique_key of myapp_email.

COMPOUND KEYS

It is easy to make a compound key a unique constraint. For instance, to combine the attributes account_type and account_name you could do:

    has 'account_type' => (
        is         => 'rw',
        isa        => 'Str',
        required   => 1,
        trigger    => sub { shift->clear_account_key }
    );

    has 'account_name' => (
        is         => 'rw',
        isa        => 'Str',
        required   => 1,
        trigger    => sub { shift->clear_account_key }
    );

    has 'account_key' => (
        is         => 'ro',
        isa        => 'Str',
        init_arg   => undef,
        lazy       => 1,
        unique_key => 'account_key',
        builder    => '_build_account_key',
        clearer    => 'clear_account_key',
    );

    sub _build_account_key {
        my $self = shift;
        return $self->account_type . ':' .$self->account_name
    }

When the docs is saved after either the account_type or account_name is changed, the account_key will be checked for uniqueness.

HANDLING CONFLICTS

When you save a doc, any unique keys will be checked for uniqueness, and an error will be thrown if there is a conflict.

You can handle these error gracefully using the on_unique parameter:

    $doc->save(
        on_unique => sub {
            my ($doc,$failed) = @_;
            # do something
        }
    )

The $failed hashref will contain a hashref whose keys are the name of the unique_keys that have conflicts, and whose values are the values of those keys which already exist, and so cannot be overwritten. For instance:

    {
        account_key => 'facebook:joe_bloggs'
    }

INCOMPATIBILITIES

$doc->overwrite()

You can't overwrite a doc with unique keys that hasn't already been loaded from Elasticsearch. For instance, you can do:

    $user = $domain->get( user => 1 );
    $user->email('jack@foo.com');
    $user->overwrite;

But not:

    $user = $domain->new_doc( user => { id => 1, email => 'jack@foo.com' });
    $user->overwrite;

The reason for this is that, if that user already exists, then overwriting that doc will leave any old unique keys in place. "save()" in Elastic::Model::Role::Doc will handle the old unique values correctly.

$view->delete

If you use "delete()" in Elastic::Model::View then you are responsible for removing the related keys yourself.

Changing unique key names and reindexing

The unique keys index will not be updated if you change the unique_key name, and reindexing does not take unique keys into account at all. It is up to you to manage any changes.

SEE ALSO

AUTHOR

Clinton Gormley <drtech@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by Clinton Gormley.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.