The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Catmandu::Store::Solr::Bag;

use Catmandu::Sane;
use Moo;
use Catmandu::Util qw(:is);
use Catmandu::Hits;
use Catmandu::Store::Solr::Searcher;
use Catmandu::Store::Solr::CQL;

with 'Catmandu::Bag';
with 'Catmandu::Searchable';
with 'Catmandu::Buffer';

sub generator {
    my ($self) = @_;
    my $store = $self->store;
    my $name  = $self->name;
    my $limit = $self->buffer_size;
    my $query = qq/_bag:"$name"/;
    sub {
        state $start = 0;
        state $hits;
        unless ($hits && @$hits) {
            $hits = $store->solr->search($query, {start => $start, rows => $limit})->content->{response}{docs};
            $start += $limit;
        }
        my $hit = shift(@$hits) || return;
        delete $hit->{_bag};
        $hit;
    };
}

sub count {
    my ($self) = @_;
    my $name = $self->name;
    my $res  = $self->store->solr->search(qq/_bag:"$name"/, {rows => 0});
    $res->content->{response}{numFound};
}

sub get {
    my ($self, $id) = @_;
    my $name = $self->name;
    my $res  = $self->store->solr->search(qq/_bag:"$name" AND _id:"$id"/, {rows => 1});
    my $hit  = $res->content->{response}{docs}->[0] || return;
    delete $hit->{_bag};
    $hit;
}

sub add {
    my ($self, $data) = @_;

    my @fields = (WebService::Solr::Field->new(_bag => $self->name));

    for my $key (keys %$data) {
        my $val = $data->{$key};
        if (is_array_ref($val)) {
            is_value($_) && push @fields, WebService::Solr::Field->new($key => $_) foreach @$val;
        } elsif (is_value($val)) {
            push @fields, WebService::Solr::Field->new($key => $val);
        }
    }

    $self->buffer_add(WebService::Solr::Document->new(@fields));

    if ($self->buffer_is_full) {
        $self->commit;
    }
}

sub delete {
    my ($self, $id) = @_;
    my $name = $self->name;
    $self->store->solr->delete_by_query(qq/_bag:"$name" AND _id:"$id"/);
}

sub delete_all {
    my ($self) = @_;
    my $name = $self->name;
    $self->store->solr->delete_by_query(qq/_bag:"$name"/);
}

sub delete_by_query {
    my ($self, %args) = @_;
    my $name = $self->name;
    $self->store->solr->delete_by_query(qq/_bag:"$name" AND ($args{query})/);
}

sub commit { # TODO better error handling
    my ($self) = @_;
    my $solr = $self->store->solr;
    my $err;
    if ($self->buffer_used) {
        eval { $solr->add($self->buffer) } or push @{$err ||= []}, $@;
        $self->clear_buffer;
    }
    eval { $solr->commit } or push @{$err ||= []}, $@;
    !defined $err, $err;
}

sub search {
    my ($self, %args) = @_;

    my $query = delete $args{query};
    my $start = delete $args{start};
    my $limit = delete $args{limit};
    my $bag   = delete $args{reify};

    my $name = $self->name;

    if ($args{fq}) {
        $args{fq} = qq/_bag:"$name" AND ($args{fq})/;
    } else {
        $args{fq} = qq/_bag:"$name"/;
    }

    my $res = $self->store->solr->search($query, {%args, start => $start, rows => $limit});

    my $set = $res->content->{response}{docs};

    if ($bag) {
        $set = [map { $bag->get($_->{_id}) } @$set];
    } else {
        delete $_->{_bag} for @$set;
    }

    my $hits = Catmandu::Hits->new({
        limit => $limit,
        start => $start,
        total => $res->content->{response}{numFound},
        hits  => $set,
    });

    if ($res->facet_counts) {
        $hits->{facets} = $res->facet_counts;
    }

    $hits;
}

sub searcher {
    my ($self, %args) = @_;
    Catmandu::Store::Solr::Searcher->new(%args, bag => $self);
}

sub translate_sru_sortkeys {
    confess 'TODO';
}

sub translate_cql_query {
    Catmandu::Store::Solr::CQL->parse($_[1]);
}

sub normalize_query {
    $_[1] || "*:*";
}

=head1 SEE ALSO

L<Catmandu::Bag>, L<Catmandu::Searchable>

=cut

1;