use Mojo::Base -base;
use Mojo::JSON 'decode_json';
use Mojo::Util 'decamelize';
use constant DEBUG => $ENV{SOLARBEAM_DEBUG} || 0;
has docs => sub { +[] };
has error => undef;
has facet_dates => sub { +{} };
has facet_fields => sub { +{} };
has facet_queries => sub { +{} };
has facet_ranges => sub { +{} };
has num_found => 0;
has pager => sub { Data::Page->new };
has params => sub { +{} };
has query_time => 0;
has start => 0;
has terms => sub { +{} };
has 'tx';
sub facet_fields_as_hashes {
my $self = shift;
return $self->{facet_fields_as_hashes} ||= do {
my $facet_fields = $self->facet_fields;
my %res;
for my $k (keys %$facet_fields) {
$res{$k} = {map { @$_{qw(value count)} } @{$facet_fields->{$k} || []}};
}
return \%res;
};
}
sub parse {
my ($self, $tx) = @_;
my $res = $tx->res;
my $data = $res->json || {};
my $header = $data->{responseHeader};
my $response = $data->{response};
my $facets = $data->{facet_counts};
my $terms = $data->{terms};
my $field;
$self->tx($tx);
if ($data->{error}) {
$self->error({code => $data->{error}{code} || $tx->res->code, message => $data->{error}{msg}});
return $self;
}
if ($res->code and !$header) {
my $dom = $res->dom;
if ($dom and $dom->at('title')) {
$self->error({code => $res->code, message => $dom->at('title')->text});
}
else {
$self->error({code => $res->code, message => $res->body || 'Missing response headers.'});
}
return $self;
}
if ($tx->error) {
$tx->error->{message} ||= 'Unknown error';
$self->error($tx->error);
return $self;
}
for $field (keys %$header) {
my $method = decamelize ucfirst $field;
$self->$method($header->{$field}) if $self->can($method);
}
for $field (keys %$response) {
my $method = decamelize ucfirst $field;
$self->$method($response->{$field}) if $self->can($method);
}
for $field (keys %$facets) {
$self->$field($facets->{$field}) if $self->can($field);
}
my $ff = $self->facet_fields;
if ($ff) {
for $field (keys %$ff) {
$ff->{$field} = $self->_build_count_list($ff->{$field});
}
}
if ($self->facet_ranges) {
for $field (keys %{$self->facet_ranges}) {
my $range = $self->facet_ranges->{$field};
$range->{counts} = $self->_build_count_list($range->{counts});
}
}
if ($terms) {
my $sane_terms = {};
for $field (keys %$terms) {
$sane_terms->{$field} = $self->_build_count_list($terms->{$field});
}
$self->terms($sane_terms);
}
if (!$self->error && $response) {
$self->pager->total_entries($self->num_found);
}
$self;
}
sub _build_count_list {
my ($self, $list) = @_;
my @result = ();
for (my $i = 1; $i < @$list; $i += 2) {
push @result, {value => $list->[$i - 1], count => $list->[$i]};
}
return \@result;
}
1;
=encoding utf8
=head1 NAME
SolarBeam::Response - Represents a Solr search response
=head1 SYNOPSIS
use SolarBeam::Response;
my $tx = Mojo::UserAgent->new->post($solr_url, form => \%query);
my $res = SolarBeam::Response->new->parse($tx);
if ($res->error) {
die sprintf "%s: %s", $res->error->{code} || 0, $res->error->{message};
}
for my $doc (@{$res->docs}) {
say $doc->{surname};
}
=head1 DESCRIPTION
L<SolarBeam::Response> holds the response from L<SolarBeam/autocomplete> or
L<SolarBeam/search>.
=head1 ATTRIBUTES
=head2 docs
$array_ref = $self->docs;
$self = $self->docs([{}, ...]);
Holds a list of the documents retrieved from Solr.
=head2 error
$hash_ref = $self->error;
$self = $self->error({message => "Error message", code => 500});
Holds either a hash-ref with error details or C<undef()> if no error is
detected. This attribute is modeled the same way as L<Mojo::Transaction/error>,
but can also contain detailed error messages from the Solr server.
=head2 facet_dates
$hash_ref = $self->facet_dates;
TODO.
=head2 facet_fields
$hash_ref = $self->facet_fields;
TODO.
=head2 facet_queries
$hash_ref = $self->facet_queries;
TODO.
=head2 facet_ranges
$hash_ref = $self->facet_ranges;
TODO.
=head2 num_found
$int = $self->num_found;
Holds the number of matching documents. This number can be higher than the
number of elements in L</docs>.
=head2 pager
$pager = $self->pager;
$self = $self->pager(Data::Page->new);
Holds a L<Data::Page> object.
=head2 params
$hash_ref = $self->params;
Holds the search params sent to Solr.
=head2 query_time
$int = $self->query_time;
The time the search took.
=head2 start
$int = $self->start;
Offset of the search result.
=head2 terms
$hash_ref = $self->terms;
=head2 tx
last tx passed to parse
TODO
=head1 METHODS
=head2 facet_fields_as_hashes
$hash_ref = $self->facet_fields_as_hashes;
Turns the arrays in L</facet_fields> into hashes instead. Example:
$self->facet_fields = {colors => [{value => "red", count => 42}]};
$self->facet_fields_as_hashes = {colors => {red => 42}}
=head2 parse
$self = $self->parse(Mojo::Transaction->new);
Used to parse the result from a query. Will populate the different
L</ATTRIBUTES>.
=head1 SEE ALSO
L<SolarBeam>.
=cut