The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Elastic::Model::Role::Doc - The role applied to your Doc classes

VERSION

version 0.02

SYNOPSIS

Creating a doc

    $doc = $domain->new_doc(
        user => {
            id      => 123,                 # auto-generated if not specified
            email   => 'clint@domain.com',
            name    => 'Clint'
        }
    );

    $doc->save;
    $uid = $doc->uid;

Retrieving a doc

    $doc = $domain->get( user => 123 );
    $doc = $model->get_doc( uid => $uid );

Updating a doc

    $doc->name('John');

    print $doc->has_changed();              # 1
    print $doc->has_changed('name');        # 1
    print $doc->has_changed('email');       # 0
    print $doc->old_value('name');          # Clint

    $doc->save;
    print $doc->has_changed();              # 0

Deleting a doc

    $doc->delete;
    print $doc->has_been_deleted            # 1

DESCRIPTION

Elastic::Model::Role::Doc is applied to your "doc" classes (ie those classes that you want to be stored in ElasticSearch), when you include this line:

    use Elastic::Doc;

This document explains the changes that are made to your class by applying the Elastic::Model::Role::Doc role. Also see Elastic::Doc.

ATTRIBUTES

The following attributes are added to your class:

uid

The uid is the unique identifier for your doc in ElasticSearch. It contains an index, a type, an id and possibly a routing. This is what is required to identify your document uniquely in ElasticSearch.

The UID is created when you create your document, eg:

    $doc = $domain->new_doc(
        user    => {
            id      => 123,
            other   => 'foobar'
        }
    );
  • index : initially comes from the $domain->name - this is changed to the actual domain name when you save your doc.

  • type : comes from the first parameter passed to new_doc() (user in this case).

  • id : is optional - if you don't provide it, then it will be auto-generated when you save it to ElasticSearch.

Note: the namespace_name/type/ID of a document must be unique. ElasticSearch can enforce uniqueness for a single index, but when your namespace contains multiple indices, it is up to you to ensure uniqueness. Either leave the ID blank, in which case ElasticSearch will generate a unique ID, or ensure that the way you generate IDs will not cause a collision.

timestamp

    $timestamp = $doc->timestamp($timestamp);

This stores the last-modified time (in epoch seconds with milli-seconds), which is set automatically when your doc is saved. The timestamp is indexed and can be used in queries.

Private attributes

These private attributes are also added to your class, and are documented here so that you don't override them without knowing what you are doing:

_can_inflate

A boolean indicating whether the object has had its attributes values inflated already or not.

_source

The raw uninflated source value as loaded from ElasticSearch.

_old_value

If any attribute value has changed, the original value will be stored in _old_value. See "old_value()" and "has_changed()".

METHODS

save()

    $doc->save( %args );

Saves the $doc to ElasticSearch. If this is a new doc, and a doc with the same type and ID already exists in the same index, then ElasticSearch will throw an exception.

If the doc was previously loaded from ElasticSearch, then that doc will be updated. However, because ElasticSearch uses optimistic locking (ie the doc version number is incremented on every change), it is possible that another process has already updated the $doc while the current process has been working, in which case it will throw a conflict error.

For instance:

    ONE                         TWO
    --------------------------------------------------
                                get doc 1-v1
    get doc 1-v1
                                save doc 1-v2
    save doc1-v2
     -> # conflict error

on_conflict

If you don't care, and you just want to overwrite what is stored in ElasticSearch with the current values, then use "overwrite()" instead of "save()". If you DO care, then you can handle this situation gracefully, using the on_conflict parameter:

    $doc->save(
        on_conflict => sub {
            my ($original_doc,$new_doc) = @_;
            # resolve conflict

        }
    );

See "has_been_deleted()" for a fuller example of an "on_conflict" callback.

The doc will only be saved if it has changed. If you want to force saving on a doc that hasn't changed, then you can do:

    $doc->touch->save;

overwrite()

    $doc->overwrite( %args );

"overwrite()" is exactly the same as "save()" except it will overwrite any previous doc, regardless of whether another process has updated the same doc in the meantime.

delete()

    $doc->delete;

This will delete the current doc. If the doc has already been updated to a new version by another process, it will throw a conflict error. You can override this and delete the document anyway with:

    $doc->delete( version => 0 );

The $doc will be reblessed into the Elastic::Model::Deleted class, and any attempt to access its attributes will throw an error.

has_been_deleted()

    $bool = $doc->has_been_deleted();

As a rule, you shouldn't delete docs that are currently in use elsewhere in your application, otherwise you have to wrap all of your code in evals to ensure that you're not accessing a stale doc.

However, if you do need to delete current docs, then "has_been_deleted()" checks if the doc exists in ElasticSearch. For instance, you might have an "on_conflict" handler which looks like this:

    $doc->save(
        on_conflict => sub {
            my ($original, $new) = @_;

            return $original->overwrite
                if $new->has_been_deleted;

            for my $attr ( keys %{ $old->old_value }) {
                $new->$attr( $old->$attr ):
            }

            $new->save
        }
    );

It is a much better approach to remove docs from the main flow of your application (eg, set a status attribute to "deleted") then physically delete the docs only after some time has passed.

touch()

    $doc = $doc->touch()

Updates the "timestamp" to the current time.

has_changed()

Has the value for any attribute changed?

    $bool = $doc->has_changed;

Has the value of attribute $attr_name changed?

    $bool = $doc->has_changed($attr_name);

Mark the object as changed without specifying an attribute:

    $doc->has_changed(1);

old_values()

    \%old_vals  = $doc->old_values();

Returns a hashref containing the original values of any attributes that have been changed.

old_value()

    $old_val    = $doc->old_value($attr_name);

Returns the original value that an attribute had before being changed, or undef.

Private methods

These private methods are also added to your class, and are documented here so that you don't override them without knowing what you are doing:

_inflate_doc

Inflates the attribute values from the hashref stored in "_source".

_get_source / _set_source

The raw doc source from ElasticSearch.

_set_old_value / _clear_old_value / _has_old_value

Accessors for "_old_value".

AUTHOR

Clinton Gormley <drtech@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2012 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.