view release on metacpan or search on metacpan
lib/Apache/Solr.pm view on Meta::CPAN
LATEST_SOLR_VERSION => '9.8', # newest support by this module
ENETDOWN => 100, # error codes may not be available on all platforms
ENETUNREACH => 101, # so cannot use Errno.
};
# overrule this when your host has a different unique field
our $uniqueKey = 'id';
my $mimetypes = MIME::Types->new;
my $http_agent;
sub _to_bool($)
{ my $b = shift;
!defined $b ? undef
: ($b && $b ne 'false' && $b ne 'off') ? 'true'
: 'false';
}
sub new(@)
{ my ($class, %args) = @_;
if($class eq __PACKAGE__)
{ my $format = delete $args{format} || 'XML';
$format eq 'XML' || $format eq 'JSON'
or panic "unknown communication format '$format' for solr";
$class .= '::' . $format;
eval "require $class"; panic $@ if $@;
}
(bless {}, $class)->init(\%args)
}
sub init($)
{ my ($self, $args) = @_;
$self->server($args->{server});
$self->{AS_core} = $args->{core};
$self->{AS_commit} = exists $args->{autocommit} ? $args->{autocommit} : 1;
$self->{AS_sversion} = $args->{server_version} || LATEST_SOLR_VERSION;
$self->{AS_retry_wait} = $args->{retry_wait} // 5; # seconds
$self->{AS_retry_max} = $args->{retry_max} // 60;
$http_agent = $self->{AS_agent} =
$args->{agent} || $http_agent || LWP::UserAgent->new(keep_alive=>1);
weaken $http_agent;
$self;
}
#---------------
sub core(;$) { my $s = shift; @_ ? $s->{AS_core} = shift : $s->{AS_core} }
sub autocommit(;$) { my $s = shift; @_ ? $s->{AS_commit} = shift : $s->{AS_commit} }
sub agent() {shift->{AS_agent}}
sub serverVersion() {shift->{AS_sversion}}
sub server(;$)
{ my ($self, $uri) = @_;
$uri or return $self->{AS_server};
$uri = URI->new($uri)
unless blessed $uri && $uri->isa('URI');
$self->{AS_server} = $uri;
}
#--------------------------
sub select(@)
{ my $self = shift;
my $args = @_ && ref $_[0] eq 'HASH' ? shift : {};
$self->_select($args, scalar $self->expandSelect(@_));
}
sub _select($$) {panic "not extended"}
sub queryTerms(@)
{ my $self = shift;
$self->_terms(scalar $self->expandTerms(@_));
}
sub _terms(@) {panic "not implemented"}
#-------------------------------------
sub addDocument($%)
{ my ($self, $docs, %args) = @_;
$docs = [ $docs ] if ref $docs ne 'ARRAY';
my $sv = $self->serverVersion;
my (%attrs, %params);
$params{commit} = _to_bool(exists $args{commit} ? $args{commit} : $self->autocommit);
if(my $cw = $args{commitWithin})
{ if($sv lt '3.4') { $attrs{commit} = 'true' }
lib/Apache/Solr.pm view on Meta::CPAN
{ if($sv ge '4.0') { $self->removed("add($depr)"); delete $args{$depr} }
elsif($sv ge '1.0') { $self->deprecated("add($depr)") }
else { $attrs{$depr} = _to_bool delete $args{$depr} }
}
}
$self->_add($docs, \%attrs, \%params);
}
sub commit(%)
{ my ($self, %args) = @_;
my $sv = $self->serverVersion;
my %attrs;
if(exists $args{waitFlush})
{ if($sv ge '4.0')
{ $self->removed("commit(waitFlush)"); delete $args{waitFlush} }
elsif($sv ge '1.4') { $self->deprecated("commit(waitFlush)") }
else { $attrs{waitFlush} = _to_bool delete $args{waitFlush} }
}
lib/Apache/Solr.pm view on Meta::CPAN
else { $attrs{softCommit} = _to_bool delete $args{softCommit} }
}
if(exists $args{expungeDeletes})
{ if($sv lt '1.4') { $self->ignored("commit(expungeDeletes)") }
else { $attrs{expungeDeletes} = _to_bool delete $args{expungeDeletes} }
}
$self->_commit(\%attrs);
}
sub _commit($) {panic "not implemented"}
sub optimize(%)
{ my ($self, %args) = @_;
my $sv = $self->serverVersion;
my %attrs;
if(exists $args{waitFlush})
{ if($sv ge '4.0') { $self->removed("commit(waitFlush)"); delete $args{waitFlush} }
elsif($sv ge '1.4') { $self->deprecated("optimize(waitFlush)") }
else { $attrs{waitFlush} = _to_bool delete $args{waitFlush} }
}
lib/Apache/Solr.pm view on Meta::CPAN
else { $attrs{softCommit} = _to_bool delete $args{softCommit} }
}
if(exists $args{maxSegments})
{ if($sv lt '1.3') { $self->ignored("optimize(maxSegments)") }
else { $attrs{maxSegments} = delete $args{maxSegments} }
}
$self->_optimize(\%attrs);
}
sub _optimize($) {panic "not implemented"}
sub delete(%)
{ my ($self, %args) = @_;
my %attrs;
$attrs{commit} = _to_bool(exists $args{commit} ? $args{commit} : $self->autocommit);
if(exists $args{fromPending})
{ $self->deprecated("delete(fromPending)");
$attrs{fromPending} = _to_bool delete $args{fromPending};
}
if(exists $args{fromCommitted})
lib/Apache/Solr.pm view on Meta::CPAN
my $result;
if($self->serverVersion ge '1.4' && !$self->isa('Apache::Solr::JSON'))
{ $result = $self->_delete(\%attrs, \@which);
}
else
{ # old servers accept only one id or query per delete
$result = $self->_delete(\%attrs, [splice @which, 0, 2]) while @which;
}
$result;
}
sub _delete(@) {panic "not implemented"}
sub rollback()
{ my $self = shift;
$self->serverVersion ge '1.4'
or error __x"Rollback not supported by solr server";
$self->_rollback;
}
sub extractDocument(@)
{ my $self = shift;
$self->serverVersion ge '1.4'
or error __x"extractDocument() requires Solr v1.4 or higher";
my %p = $self->expandExtract(@_);
my $data;
# expand* changes '_' into '.'
my $ct = delete $p{'content.type'};
lib/Apache/Solr.pm view on Meta::CPAN
$ct ||= $mimetypes->mimeTypeOf($fn);
}
}
else
{ error __x"Extract requires document as file or string";
}
$self->_extract([%p], $data, $ct);
}
sub _extract($) { panic "not implemented" }
#-------------------------
sub _core_admin($@)
{ my ($self, $action, $params) = @_;
$params->{core} ||= $self->core;
my $endpoint = $self->endpoint('cores', core => 'admin', params => $params);
my @params = %$params;
my $result = Apache::Solr::Result->new(params => [ %$params ], endpoint => $endpoint, core => $self);
$self->request($endpoint, $result);
$result;
}
sub coreStatus(%)
{ my ($self, %args) = @_;
$self->_core_admin('STATUS', \%args);
}
sub coreReload(%)
{ my ($self, %args) = @_;
$self->_core_admin('RELOAD', \%args);
}
sub coreUnload($%)
{ my ($self, %args) = @_;
$self->_core_admin('UNLOAD', \%args);
}
#--------------------------
sub _calling_sub()
{ for(my $i=0;$i <10; $i++)
{ my $sub = (caller $i)[3];
return $sub if !$sub || index($sub, 'Apache::Solr::') < 0;
}
}
sub _simpleExpand($$$)
{ my ($self, $p, $prefix) = @_;
my @p = ref $p eq 'HASH' ? %$p : @$p;
my $sv = $self->serverVersion;
my @t;
while(@p)
{ my ($k, $v) = (shift @p, shift @p);
$k =~ s/_/./g;
$k = $prefix.$k if defined $prefix && index($k, $prefix)!=0;
my $param = $k =~ m/^f\.[^\.]+\.(.*)/ ? $1 : $k;
lib/Apache/Solr.pm view on Meta::CPAN
next;
}
push @t, $k => $boolparams{$param} ? _to_bool($_) : $_
for ref $v eq 'ARRAY' ? @$v : $v;
}
@t;
}
sub expandTerms(@)
{ my $self = shift;
my $p = @_==1 ? shift : [@_];
my @t = $self->_simpleExpand($p, 'terms.');
wantarray ? @t : \@t;
}
sub _expand_flatten($$)
{ my ($self, $v, $prefix) = @_;
my @l = ref $v eq 'HASH' ? %$v : @$v;
my @s;
push @s, $prefix.(shift @l) => (shift @l) while @l;
@s;
}
sub expandExtract(@)
{ my $self = shift;
my @p = @_==1 ? @{(shift)} : @_;
my @s;
while(@p)
{ my ($k, $v) = (shift @p, shift @p);
if(!ref $v || ref $v eq 'SCALAR')
{ push @s, $k => $v }
elsif($k eq 'literal' || $k eq 'literals')
{ push @s, $self->_expand_flatten($v, 'literal.') }
elsif($k eq 'fmap' || $k eq 'boost' || $k eq 'resource')
lib/Apache/Solr.pm view on Meta::CPAN
my %sets = #also-per-field?
( expand => [0]
, facet => [1]
, hl => [1]
, mlt => [0]
, stats => [0]
, suggest => [0]
, group => [0]
);
sub expandSelect(@)
{ my $self = shift;
my @s;
my (@flat, %seen_set);
while(@_)
{ my ($k, $v) = (shift, shift);
$k =~ s/_/./g;
my @p = split /\./, $k;
# fields are $set.$more or f.$field.$set.$more
my $per_field = $p[0] eq 'f' && @p > 2;
lib/Apache/Solr.pm view on Meta::CPAN
else
{ push @flat, $k => $v;
}
}
push @flat, %seen_set;
unshift @s, $self->_simpleExpand(\@flat);
wantarray ? @s : \@s;
}
sub deprecated($)
{ my ($self, $msg) = @_;
return if $self->{AS_depr_msg}{$msg}++; # report only once
warning __x"Deprecated solr {message}", message => $msg;
}
sub ignored($)
{ my ($self, $msg) = @_;
return if $self->{AS_ign_msg}{$msg}++; # report only once
warning __x"Ignored solr {message}", message => $msg;
}
sub removed($)
{ my ($self, $msg) = @_;
return if $self->{AS_rem_msg}{$msg}++; # report only once
warning __x"Removed solr {message}", message => $msg;
}
#------------------------
sub endpoint($@)
{ my ($self, $action, %args) = @_;
my $core = $args{core} || $self->core;
my $take = $self->server->clone; # URI
$take->path($take->path . (defined $core ? "/$core" : '') . "/$action");
# make parameters ordered
my $params = $args{params} || [];
$params = [ %$params ] if ref $params eq 'HASH';
@$params or return $take;
lib/Apache/Solr.pm view on Meta::CPAN
while(@p)
{ push @params, $p[0] => $p[1] if defined $p[1];
shift @p, shift @p;
}
$take->query_form(@params) if @params;
$take;
}
sub request($$;$$)
{ my ($self, $url, $result, $body, $body_ct) = @_;
my $req;
if($body)
{ # request with 'form' payload
$req = HTTP::Request->new
( POST => $url
, [ Content_Type => $body_ct
, Content_Disposition => 'form-data; name="content"'
]
lib/Apache/Solr.pm view on Meta::CPAN
unless($resp->is_success)
{ $! = $resp->code==500 ? ENETDOWN : ENETUNREACH;
fault __x"Solr request failed after {elapse} seconds after {retries} retries",
elapse => time - $start, retries => $self->{AS_retry_max} - $retries -1, result => $result;
}
$resp;
}
sub decodeResponse($) { undef }
#----------------------------------
1;
lib/Apache/Solr/Document.pm view on Meta::CPAN
our $VERSION = '1.11';
}
use warnings;
use strict;
use Log::Report qw(solr);
sub new(@) { my $c = shift; (bless {}, $c)->init({@_}) }
sub init($)
{ my ($self, $args) = @_;
$self->{ASD_boost} = $args->{boost} || 1.0;
$self->{ASD_fields} = []; # ordered
$self->{ASD_fields_h} = {}; # grouped by name
$self->addFields($args->{fields});
$self;
}
sub fromResult($$)
{ my ($class, $data, $rank) = @_;
my (@f, %fh);
while(my($k, $v) = each %$data)
{ my @v = map +{name => $k, content => $_}, ref $v eq 'ARRAY' ? @$v : $v;
push @f, @v;
$fh{$k} = \@v;
}
my $self = $class->new;
$self->{ASD_rank} = $rank;
$self->{ASD_fields} = \@f;
$self->{ASD_fields_h} = \%fh;
$self;
}
#---------------
sub boost(;$)
{ my $self = shift;
@_ or return $self->{ASD_boost};
my $f = $self->field(shift) or return;
@_ ? $f->{boost} = shift : $f->{boost};
}
sub fieldNames() { my %c; $c{$_->{name}}++ for shift->fields; sort keys %c }
sub uniqueId() {shift->content($Apache::Solr::uniqueKey)}
sub rank() {shift->{ASD_rank}}
sub fields(;$)
{ my $self = shift;
my $f = $self->{ASD_fields};
@_ or return @$f;
my $name = shift;
my $fh = $self->{ASD_fields_h}{$name}; # grouped by name
$fh ? @$fh : ();
}
sub field($)
{ my $fh = $_[0]->{ASD_fields_h}{$_[1]};
$fh ? $fh->[0] : undef;
}
sub content($)
{ my $f = $_[0]->field($_[1]);
$f ? $f->{content} : undef;
}
our $AUTOLOAD;
sub AUTOLOAD
{ my $self = shift;
(my $fn = $AUTOLOAD) =~ s/.*\:\://;
$fn =~ /^_(.*)/ ? $self->content($1)
: $fn eq 'DESTROY' ? undef
: panic "Unknown method $AUTOLOAD (hint: fields start with '_')";
}
sub addField($$%)
{ my $self = shift;
my $name = shift;
defined $_[0] or return;
my $field = { # important to minimalize copying of content
name => $name,
content => (
!ref $_[0] ? shift
: ref $_[0] eq 'SCALAR' ? ${shift()}
: shift
lib/Apache/Solr/Document.pm view on Meta::CPAN
my %args = @_;
$field->{boost} = $args{boost} || 1.0;
$field->{update} = $args{update};
push @{$self->{ASD_fields}}, $field;
push @{$self->{ASD_fields_h}{$name}}, $field;
$field;
}
sub addFields($%)
{ my ($self, $h, @args) = @_;
# pass content by ref to avoid a copy of potentially huge field.
if(ref $h eq 'ARRAY')
{ for(my $i=0; $i < @$h; $i+=2)
{ $self->addField($h->[$i] => \$h->[$i+1], @args);
}
}
else
{ $self->addField($_ => \$h->{$_}, @args) for sort keys %$h;
}
lib/Apache/Solr/JSON.pm view on Meta::CPAN
use strict;
use Log::Report qw(solr);
use Apache::Solr::Result ();
use HTTP::Request ();
use JSON ();
use Scalar::Util qw(blessed);
sub init($)
{ my ($self, $args) = @_;
$args->{format} ||= 'JSON';
$self->SUPER::init($args);
$self->{ASJ_json} = $args->{json} || JSON->new->utf8;
$self;
}
#---------------
sub json() {shift->{ASJ_json}}
#--------------------------
sub _select($$)
{ my ($self, $args, $params) = @_;
# select may be called more than once, but do not add wt each time
# again.
my $endpoint = $self->endpoint('select', params => $params);
my $result = Apache::Solr::Result->new(%$args, params => $params, endpoint => $endpoint, core => $self);
$self->request($endpoint, $result);
if(my $dec = $result->decoded)
{ # JSON uses different names!
my $r = $dec->{result} = delete $dec->{response};
$r->{doc} = delete $r->{docs};
}
$result;
}
sub _extract($$$)
{ my ($self, $params, $data, $ct) = @_;
my $endpoint = $self->endpoint('update/extract', params => $params);
my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
$self->request($endpoint, $result, $data, $ct);
$result;
}
sub _add($$$)
{ my ($self, $docs, $attrs, $params) = @_;
$attrs ||= {};
$params ||= [];
my $sv = $self->serverVersion;
$sv ge '3.1' or error __x"Solr version too old for updates in JSON syntax";
# We cannot create HASHes with twice the same key in Perl, so cannot
# produce the syntax for adding multiple documents. Try to save it.
delete $attrs->{boost}
lib/Apache/Solr/JSON.pm view on Meta::CPAN
error __x"Unable to add more than one doc with JSON interface";
}
else
{ $add = [ map $self->_doc2json($_), @$docs ];
}
$self->request($endpoint, $result, $add);
$result;
}
sub _doc2json($)
{ my ($self, $this) = @_;
my %doc;
foreach my $fieldname ($this->fieldNames)
{ my @f;
foreach my $field ($this->fields($fieldname))
{ my $update = $field->{update} || 'value';
my $boost = $field->{boost} || 1.0;
undef $boost
if $boost > 0.9999 && $boost < 1.0001;
lib/Apache/Solr/JSON.pm view on Meta::CPAN
? +{ boost => $boost, $update => $field->{content} }
: +{ $update => $field->{content} };
}
# we have to combine multi-fields into ARRAYS
$doc{$fieldname} = @f > 1 ? \@f : $f[0];
}
\%doc;
}
sub _commit($) { my ($s, $attr) = @_; $s->simpleUpdate(commit => $attr) }
sub _optimize($) { my ($s, $attr) = @_; $s->simpleUpdate(optimize => $attr) }
sub _delete($$) { my $self = shift; $self->simpleUpdate(delete => @_) }
sub _rollback() { shift->simpleUpdate('rollback') }
sub _terms($)
{ my ($self, $terms) = @_;
my $endpoint = $self->endpoint('terms', params => $terms);
my $result = Apache::Solr::Result->new(params => $terms, endpoint => $endpoint, core => $self);
$self->request($endpoint, $result);
my $table = $result->decoded->{terms} || {};
$table = {@$table} if ref $table eq 'ARRAY'; # bug in Solr 1.4
while(my ($field, $terms) = each %$table)
{ # repack array-of-pairs into array-of-arrays-of-pair
lib/Apache/Solr/JSON.pm view on Meta::CPAN
my @terms;
push @terms, [shift @pairs, shift @pairs] while @pairs;
$result->terms($field => \@terms);
}
$result;
}
#--------------------------
sub request($$;$$)
{ my ($self, $url, $result, $body, $body_ct) = @_;
if(ref $body && ref $body ne 'SCALAR')
{ $body_ct ||= 'application/json; charset=utf-8';
$body = \$self->json->encode($body);
}
# Solr server 3.6.2 seems not to detect the JSON input from the
# body content, so requires this work-around
# https://solr.apache.org/guide/6_6/uploading-data-with-index-handlers.html#UploadingDatawithIndexHandlers-JSONUpdateConveniencePaths
$url =~ s!/update\?!/update/json?!;
$self->SUPER::request($url, $result, $body, $body_ct);
}
sub decodeResponse($)
{ my ($self, $resp) = @_;
# At least until Solr 4.0 response ct=text/plain while producing JSON
my $ct = $resp->content_type;
$ct =~ m/json/i
or error __x"Answer from solr server is not json but {type}", type => $ct;
$self->json->decode($resp->decoded_content || $resp->content);
}
sub simpleUpdate($$;$)
{ my ($self, $command, $attrs, $content) = @_;
my $sv = $self->serverVersion;
$sv ge '3.1' or error __x"Solr version too old for updates in JSON syntax";
$attrs ||= {};
my $params = [ commit => delete $attrs->{commit} ];
my $endpoint = $self->endpoint(($sv lt '4.0' ? 'update/json' : 'update'), params => $params);
my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
my %params = (%$attrs, (!$content ? () : ref $content eq 'HASH' ? %$content : @$content));
my $doc = $self->simpleDocument($command, \%params);
$self->request($endpoint, $result, $doc);
$result;
}
sub simpleDocument($;$$)
{ my ($self, $command, $attrs, $content) = @_;
$attrs ||= {};
$content ||= {};
+{ $command => { %$attrs, %$content } }
}
sub endpoint($@)
{ my ($self, $action, %args) = @_;
my $params = $args{params} ||= [];
if(ref $params eq 'HASH') { $params->{wt} ||= 'json' }
else { $args{params} = [ wt => 'json', @$params ] }
$self->SUPER::endpoint($action, %args);
}
1;
lib/Apache/Solr/Result.pm view on Meta::CPAN
$Data::Dumper::Indent = 1;
$Data::Dumper::Quotekeys = 0;
use overload
'""' => 'endpoint'
, bool => 'success';
#----------------------
sub new(@) { my $c = shift; (bless {}, $c)->init({@_}) }
sub init($)
{ my ($self, $args) = @_;
my $p = $args->{params} || [];
($p, my $params) = ref $p eq 'HASH' ? ( +[%$p], $p ) : ($p, +{@$p});
$self->{ASR_params} = $p;
$self->{ASR_endpoint} = $args->{endpoint} or panic;
$self->{ASR_start} = time;
$self->request($args->{request});
$self->response($args->{response});
lib/Apache/Solr/Result.pm view on Meta::CPAN
weaken $self->{ASR_pages}[0]; # no reference loop!
if($self->{ASR_core} = $args->{core}) { weaken $self->{ASR_core} }
$self->{ASR_next} = $params->{start} || 0;
$self->{ASR_seq} = $args->{sequential} || 0;
$self->{ASR_fpz} = $args->{_fpz};
$self;
}
# replace the pageset with a shared set.
sub _pageset($)
{ $_[0]->{ASR_pages} = $_[1];
weaken $_[0]->{ASR_pages}; # otherwise memory leak
}
#---------------
sub start() {shift->{ASR_start}}
sub endpoint() {shift->{ASR_endpoint}}
sub params() {@{shift->{ASR_params}}}
sub core() {shift->{ASR_core}}
sub sequential() {shift->{ASR_seq}}
sub request(;$)
{ my $self = shift;
@_ && $_[0] or return $self->{ASR_request};
$self->{ASR_req_out} = time;
$self->{ASR_request} = shift;
}
sub response(;$)
{ my $self = shift;
@_ && $_[0] or return $self->{ASR_response};
$self->{ASR_resp_in} = time;
$self->{ASR_response} = shift;
}
sub decoded(;$)
{ my $self = shift;
@_ or return $self->{ASR_decoded};
$self->{ASR_dec_done} = time;
$self->{ASR_decoded} = shift;
}
sub elapse()
{ my $self = shift;
my $done = $self->{ASR_dec_done} or return;
$done = $self->{ASR_start};
}
sub success() { my $s = shift; $s->{ASR_success} ||= $s->solrStatus==0 }
sub solrStatus()
{ my $dec = shift->decoded or return 500;
$dec->{responseHeader}{status};
}
sub solrQTime()
{ my $dec = shift->decoded or return;
my $qtime = $dec->{responseHeader}{QTime};
defined $qtime ? $qtime/1000 : undef;
}
sub solrError()
{ my $dec = shift->decoded or return;
my $err = $dec->{error} || {};
my $msg = $err->{msg} || '';
$msg =~ s/\s*$//s;
length $msg ? $msg : ();
}
sub httpError()
{ my $resp = shift->response or return;
$resp->status_line;
}
sub serverError()
{ my $resp = shift->response or return;
$resp->code != 200 or return;
my $ct = $resp->content_type;
$ct eq 'text/html' or return;
my $body = $resp->decoded_content || $resp->content;
$body =~ s!.*<body>!!;
$body =~ s!</body>.*!!;
$body =~ s!</h[0-6]>|</p>!\n!g; # cheap reformatter
$body =~ s!</b>\s*!: !g;
$body =~ s!<[^>]*>!!gs;
$body;
}
sub errors()
{ my $self = shift;
my @errors;
if(my $h = $self->httpError) { push @errors, "HTTP error:", " $h" }
if(my $a = $self->serverError)
{ $a =~ s/^/ /gm;
push @errors, "Server error:", $a;
}
if(my $s = $self->solrError) { push @errors, "Solr error:", " $s" }
join "\n", @errors, '';
}
#--------------------------
sub _responseData()
{ my $dec = shift->decoded;
$dec->{result} // $dec->{response};
}
sub nrSelected()
{ my $results = shift->_responseData
or panic "there are no results (yet)";
$results->{numFound};
}
sub _docs($)
{ my ($self, $data) = @_;
my $docs = $data->{doc} // $data->{docs} // [];
# Decoding XML without schema may give unexpect results
$docs = [ $docs ] if ref $docs eq 'HASH'; # when only one result
$docs;
}
sub selected($%)
{ my ($self, $rank, %options) = @_;
my $data = $self->_responseData
or panic __x"There are no results in the answer";
# start for next
$self->{ASR_next} = $rank +1;
# in this page?
my $startnr = $data->{start};
if($rank >= $startnr)
lib/Apache/Solr/Result.pm view on Meta::CPAN
$rank < $data->{numFound} # outside answer range
or return ();
my $pagenr = $self->selectedPageNr($rank);
my $page = $self->selectedPage($pagenr) || $self->selectedPageLoad($pagenr, $self->core);
$page->selected($rank);
}
sub nextSelected(%)
{ my $self = shift;
$self->selected($self->{ASR_next}, @_);
}
sub highlighted($)
{ my ($self, $doc) = @_;
my $rank = $doc->rank;
my $pagenr = $self->selectedPageNr($rank);
my $hl = $self->selectedPage($pagenr)->decoded->{highlighting}
or error __x"There is no highlighting information in the result";
Apache::Solr::Document->fromResult($hl->{$doc->uniqueId}, $rank);
}
#--------------------------
sub terms($;$)
{ my ($self, $field) = (shift, shift);
return $self->{ASR_terms}{$field} = shift if @_;
my $r = $self->{ASR_terms}{$field}
or error __x"No search for terms on field {field} requested", field => $field;
$r;
}
#--------------------------
sub _to_msec($) { sprintf "%.1f", $_[0] * 1000 }
sub showTimings(;$)
{ my ($self, $fh) = @_;
$fh ||= select;
my $req = $self->request;
my $to = $req ? $req->uri : '(not set yet)';
my $start = localtime $self->{ASR_start};
$fh->print("endpoint: $to\nstart: $start\n");
if($req)
{ my $reqsize = length($req->as_string);
lib/Apache/Solr/Result.pm view on Meta::CPAN
}
if(my $error = $self->solrError)
{ $fh->print(" solr reported error: '$error'\n");
}
my $total = _to_msec($self->{ASR_dec_done} - $self->{ASR_start});
$fh->print("elapse: $total ms total\n");
}
}
sub selectedPageNr($) { my $pz = shift->fullPageSize; $pz ? int(shift() / $pz) : 0 }
sub selectPages() { @{shift->{ASR_pages}} }
sub selectedPage($) { my $pages = shift->{ASR_pages}; $pages->[shift()] }
# The reloads page 0, which may have been purged by sequential reading. Besided,
# the name does not cover its content: it's not the size of the select page but
# the first page.
sub selectedPageSize()
{ my $result = shift->selectedPage(0)->_responseData || {};
my $docs = $result->{doc} // $result->{docs};
ref $docs eq 'HASH' ? 1 : ref $docs eq 'ARRAY' ? scalar @$docs : 50;
}
sub fullPageSize() { my $self = shift; $self->{ASR_fpz} ||= $self->_calc_page_size }
sub _calc_page_size()
{ my $self = shift;
my $docs = $self->_docs($self->selectedPage(0)->_responseData);
#warn "CALC PZ=", scalar @$docs;
scalar @$docs;
}
sub selectedPageLoad($;$)
{ my ($self, $pagenr, $client) = @_;
$client
or error __x"Cannot autoload page {nr}, no client provided", nr => $pagenr;
my $fpz = $self->fullPageSize;
my @params = $self->replaceParams( { start => $pagenr * $fpz, rows => $fpz }, $self->params);
my $seq = $self->sequential;
my $page = $client->select({sequential => $seq, _fpz => $fpz}, @params);
my $pages = $self->{ASR_pages};
lib/Apache/Solr/Result.pm view on Meta::CPAN
# purge cached previous pages when in sequential mode
if($seq && $pagenr != 0)
{ $pages->[$_] = undef for 0..$pagenr-1;
}
$page;
}
sub replaceParams($@)
{ my ($self, $new) = (shift, shift);
my @out;
while(@_)
{ my ($k, $v) = (shift, shift);
$v = delete $new->{$k} if $new->{$k};
push @out, $k => $v;
}
(@out, %$new);
}
lib/Apache/Solr/XML.pm view on Meta::CPAN
$Data::Dumper::Quotekeys = 0;
# See the XML::LibXML::Simple manual page
my @xml_decode_config = (
ForceArray => [],
ContentKey => '_',
KeyAttr => [],
);
sub init($)
{ my ($self, $args) = @_;
$args->{format} ||= 'XML';
$self->SUPER::init($args);
$self->{ASX_simple} = XML::LibXML::Simple->new(@xml_decode_config);
$self;
}
#---------------
sub xmlsimple() {shift->{ASX_simple}}
#--------------------------
sub _select($$)
{ my ($self, $args, $params) = @_;
my $endpoint = $self->endpoint('select', params => $params);
my $result = Apache::Solr::Result->new(%$args, params => $params, endpoint => $endpoint, core => $self);
$self->request($endpoint, $result);
$result;
}
sub _extract($$$)
{ my ($self, $params, $data, $ct) = @_;
my $endpoint = $self->endpoint('update/extract', params => $params);
my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
$self->request($endpoint, $result, $data, $ct);
$result;
}
sub _add($$$)
{ my ($self, $docs, $attrs, $params) = @_;
$attrs ||= {};
$params ||= [];
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $add = $doc->createElement('add');
$add->setAttribute($_ => $attrs->{$_}) for sort keys %$attrs;
$add->addChild($self->_doc2xml($doc, $_))
for @$docs;
$doc->setDocumentElement($add);
my $endpoint = $self->endpoint('update', params => $params);
my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
$self->request($endpoint, $result, $doc);
$result;
}
sub _doc2xml($$$)
{ my ($self, $doc, $this) = @_;
my $node = $doc->createElement('doc');
my $boost = $this->boost || 1.0;
$node->setAttribute(boost => $boost) if $boost != 1.0;
foreach my $field ($this->fields)
{ my $fnode = $doc->createElement('field');
$fnode->setAttribute(name => $field->{name});
lib/Apache/Solr/XML.pm view on Meta::CPAN
$fnode->setAttribute(update => $field->{update})
if defined $field->{update};
$fnode->appendText($field->{content});
$node->addChild($fnode);
}
$node;
}
sub _commit($) { my ($s, $attr) = @_; $s->simpleUpdate(commit => $attr) }
sub _optimize($) { my ($s, $attr) = @_; $s->simpleUpdate(optimize => $attr) }
sub _delete($$) { my $self = shift; $self->simpleUpdate(delete => @_) }
sub _rollback() { shift->simpleUpdate('rollback') }
sub _terms($)
{ my ($self, $terms) = @_;
my $endpoint = $self->endpoint('terms', params => $terms);
my $result = Apache::Solr::Result->new(params => $terms, endpoint => $endpoint, core => $self);
$self->request($endpoint, $result);
my $table = $result->decoded->{terms} || {};
while(my ($field, $terms) = each %$table)
{ my @terms = map [ $_ => $terms->{$_} ],
sort {$terms->{$b} <=> $terms->{$a}} keys %$terms;
$result->terms($field => \@terms);
}
$result;
}
#--------------------------
sub request($$;$$)
{ my ($self, $url, $result, $body, $body_ct) = @_;
if(blessed $body && $body->isa('XML::LibXML::Document'))
{ $body_ct ||= 'text/xml; charset=utf-8';
$body = \$body->toString;
}
$self->SUPER::request($url, $result, $body, $body_ct);
}
sub _cleanup_parsed($);
sub decodeResponse($)
{ my ($self, $resp) = @_;
$resp->content_type =~ m/xml/i
or return undef;
my $dec = $self->xmlsimple->XMLin(
$resp->decoded_content || $resp->content,
parseropts => { huge => 1 },
);
_cleanup_parsed $dec;
}
sub _cleanup_parsed($)
{ my $data = shift;
if(!ref $data) { return $data }
elsif(ref $data eq 'HASH')
{ my %d = %$data; # start with shallow copy
# Hash
if(my $lst = delete $d{lst})
{ foreach (ref $lst eq 'ARRAY' ? @$lst : $lst)
{ my $name = delete $_->{name};
lib/Apache/Solr/XML.pm view on Meta::CPAN
elsif(ref $data eq 'ARRAY')
{ return [ map _cleanup_parsed($_), @$data ];
}
elsif(ref $data eq 'DateTime')
{ return $data;
}
else {panic ref $data || $data}
}
sub simpleUpdate($$;$)
{ my ($self, $command, $attrs, $content) = @_;
$attrs ||= {};
my $params = [ commit => delete $attrs->{commit} ];
my $endpoint = $self->endpoint('update', params => $params);
my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
my $doc = $self->simpleDocument($command, $attrs, $content);
$self->request($endpoint, $result, $doc);
$result;
}
sub simpleDocument($;$$)
{ my ($self, $command, $attrs, $content) = @_;
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $top = $doc->createElement($command);
$doc->setDocumentElement($top);
$attrs ||= {};
$top->setAttribute($_ => $attrs->{$_}) for sort keys %$attrs;
if(!defined $content) {}
elsif(ref $content eq 'HASH' || ref $content eq 'ARRAY')
lib/Apache/Solr/XML.pm view on Meta::CPAN
$top->addChild($node);
}
}
}
else
{ $top->appendText($content);
}
$doc;
}
sub endpoint($@)
{ my ($self, $action, %args) = @_;
my $params = $args{params} ||= [];
if(ref $params eq 'HASH') { $params->{wt} ||= 'xml' }
else { $args{params} = [ wt => 'xml', @$params ] }
$self->SUPER::endpoint($action => %args);
}
1;
t/12facet.t view on Meta::CPAN
$Data::Dumper::Indent = 1;
$Data::Dumper::Quotekeys = 0;
# the server will not be called in this script.
my $server = 'http://localhost:8080/solr';
my $core = 'my-core';
my $solr = Apache::Solr::XML->new(server => $server, core => $core);
ok(defined $solr, 'instantiated client');
sub decode_xml($)
{ my $xml = shift;
my $tree = $solr->xmlsimple->XMLin($xml);
Apache::Solr::XML::_cleanup_parsed($tree);
}
sub check_get($$$)
{ my ($url, $params, $test) = @_;
# take the parameters from the url
$url =~ s/.*\?//;
my @url = map { split /\=/, $_, 2 } split /\&/, $url;
s/\+/ /g,s/%([a-zA-Z0-9]{2})/chr hex $1/ge for @url;
# the order may be important, but ignored for these tests
my $expanded = $solr->expandSelect(%$params);
#warn Dumper \@url, $expanded;
t/30result.t view on Meta::CPAN
$Data::Dumper::Indent = 1;
$Data::Dumper::Quotekeys = 0;
# the server will not be called in this script.
my $server = 'http://localhost:8080/solr';
my $core = 'my-core';
my $solr = Apache::Solr::XML->new(server => $server, core => $core);
ok(defined $solr, 'instantiated client');
sub decode_xml($)
{ my $xml = shift;
my $tree = $solr->xmlsimple->XMLin($xml);
Apache::Solr::XML::_cleanup_parsed($tree);
}
### Results
my $f1 = <<'_RESULT1';
<?xml version="1.0" encoding="UTF-8"?>
<response>