#
# This file is part of ElasticSearchX-Model
#
# This software is Copyright (c) 2016 by Moritz Onken.
#
# This is free software, licensed under:
#
#   The (three-clause) BSD License
#
package ElasticSearchX::Model::Document;
$ElasticSearchX::Model::Document::VERSION = '1.0.0';
use strict;
use warnings;

use Moose 1.15 ();
use Moose::Exporter;
use ElasticSearchX::Model::Document::Trait::Class;
use ElasticSearchX::Model::Document::Trait::Attribute;
use ElasticSearchX::Model::Document::Types ();

my ( undef, undef, $init_meta ) = Moose::Exporter->build_import_methods(
    install   => [qw(import unimport)],
    with_meta => [qw(has)],
    class_metaroles =>
        { class => ['ElasticSearchX::Model::Document::Trait::Class'] },
);

sub has { shift->add_property(@_) }

sub init_meta {
    my $class = shift;
    my %p     = @_;
    Moose::Util::ensure_all_roles( $p{for_class},
        qw(ElasticSearchX::Model::Document::Role) );
    $class->$init_meta(%p);
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

ElasticSearchX::Model::Document

=head1 VERSION

version 1.0.0

=head1 SYNOPSIS

 package MyClass;
 use Moose;
 use ElasticSearchX::Model::Document;
 use ElasticSearchX::Model::Document::Types qw(Location);
 use MooseX::Types -declare => ['Resources'];
 use MooseX::Types::Structured qw(Dict Optional);
 use MooseX::Types::Moose qw(Str ArrayRef);

 subtype Resources,
  as Dict [ license => Optional [ ArrayRef [Str] ],
            homepage => Optional [Str],
            bugtracker => Optional [ Dict [ web => Str, mailto => Str ] ] ];

 has default  => ( is => 'ro' );
 has date     => ( is => 'ro', isa => 'DateTime' );
 has location => ( is => 'ro', isa => Location );
 has res      => ( is => 'ro', isa => Resources );
 has abstract => ( is => 'ro', analyzer => 'lowercase' );

=head1 DESCRIPTION

This class extends Moose to include meta information for ElasticSearch.
By default, each attribute is treated as property of an ElasticSearch
type (i.e. the L<ElasticSearchX::Model::Document::Trait::Attribute>
trait is applied). The type name is derived from the class name. See
L<ElasticSearchX::Model::Document::Trait::Class>.

See L<ElasticSearchX::Model::Document::Trait::Attribute/ATTRIBUTES> for a full 
list of attribute options available.

=head1 ATTRIBUTES

=head2 index

 $document->index($model->index('index_v2'));
 $document->put;

The L<ElasticSearchX::Model::Index> object this document belongs to.
This attribute is writeable, which allows you to reindex a document
to a different index. Make sure that the type is part of the new
index. Otherwise indexing is likely to fail.

=head2 _id

This is a read-only attribute that holds the value of the id of
the document. If you want to set the id by yourself, create an
attribute in your document class which has the C<id> attribute
set to C<1>. Otherwise the id will be generated by ElasticSearch.

 has id => ( is => 'ro', id => 1 );

Setting the C<id> property to an ArrayRef of property names will
build the id for you as a digest of these values.

 has id => ( is => 'ro', id => [qw(firstname lastname)] );

A document with the firstname set to John and the lastname to Doe
will be stored with an id of C<Gwc_dwDeSaQunLeq73JHz5k9jns>. The
ID is generated in L<ElasticSearchX::Model::Util/digest>.

Think of it as a multi-value primary key. Quite useful if you don't
want to worry about duplicate records since the document with the
same values for the id will always generate the same id.

=head1 METHODS

=head2 create

=head2 create( { %qs } )

Create a new document. If the document already exists (based
on the id), an L<ElasticSearch::Error::Conflict> expection is thrown.

=head2 update

=head2 update( { %qs } )

Update an existing document. Throws an L<ElasticSearch::Error::Conflict>
if there is a version mismatch. This happens if a new version of the
document was pushed by someone else. If you don't care about a version
mismatch, either pass C<< version => undef >> or use L</put>. You can
also set a version explicitly with C<< version => $version >>.

If the document was built from a query that did not include all fields
(i.e. L<ElasticSearchX::Model::Set/fields> was set), then L</update> will
fail since partial updates to a document are not yet supported in
ElasticSearch. You can either pull the full document from ElasticSearch
and update then or call L</put>.

=head2 put

=head2 put( { %qs } )

B<< Consider using L</update> or L</create> instead. >> They provide sanity
checks and are generally what you want to use.

This puts a document to the ElasticSearch server. Use this
to create a document or force an update. It calls
L<ElasticSearchX::Model::Document::Trait::Class/get_data> to retrieve the
data from an L<ElasticSearchX::Model::Document> object.

C<%qs> are optional parameters that are passed on to L<index()/ElasticSearch>.

=head2 delete

=head2 delete( { %qs } )

Delete the document from the index.

=head1 AUTHOR

Moritz Onken

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2016 by Moritz Onken.

This is free software, licensed under:

  The (three-clause) BSD License

=cut
