File Coverage

lib/Apache/Solr/XML.pm
Criterion Covered Total %
statement 65 153 42.4
branch 25 48 52.0
condition 3 40 7.5
subroutine 14 27 51.8
pod 5 7 71.4
total 112 275 40.7


line stmt bran cond sub pod time code
1             # Copyrights 2012-2025 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.03.
5             # This code is part of distribution Apache-Solr. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package Apache::Solr::XML;{
10             our $VERSION = '1.11';
11             }
12              
13 5     5   15862 use base 'Apache::Solr';
  5         11  
  5         1728  
14              
15 5     5   39 use warnings;
  5         21  
  5         358  
16 5     5   24 use strict;
  5         8  
  5         144  
17              
18 5     5   21 use Log::Report qw(solr);
  5         6  
  5         41  
19              
20 5     5   3639 use Apache::Solr::Result ();
  5         13  
  5         184  
21 5     5   1995 use XML::LibXML::Simple ();
  5         168578  
  5         182  
22 5     5   47 use HTTP::Message ();
  5         11  
  5         115  
23 5     5   25 use HTTP::Request ();
  5         9  
  5         197  
24 5     5   28 use Scalar::Util qw(blessed);
  5         8  
  5         306  
25              
26 5     5   29 use Data::Dumper;
  5         8  
  5         13507  
27             $Data::Dumper::Indent = 1;
28             $Data::Dumper::Quotekeys = 0;
29              
30             # See the XML::LibXML::Simple manual page
31             my @xml_decode_config = (
32             ForceArray => [],
33             ContentKey => '_',
34             KeyAttr => [],
35             );
36              
37              
38             sub init($)
39 4     4 0 13 { my ($self, $args) = @_;
40 4   50     38 $args->{format} ||= 'XML';
41              
42 4         36 $self->SUPER::init($args);
43              
44 4         60 $self->{ASX_simple} = XML::LibXML::Simple->new(@xml_decode_config);
45 4         310 $self;
46             }
47              
48             #---------------
49 10     10 1 10795 sub xmlsimple() {shift->{ASX_simple}}
50              
51             #--------------------------
52              
53             sub _select($$)
54 0     0   0 { my ($self, $args, $params) = @_;
55 0         0 my $endpoint = $self->endpoint('select', params => $params);
56 0         0 my $result = Apache::Solr::Result->new(%$args, params => $params, endpoint => $endpoint, core => $self);
57 0         0 $self->request($endpoint, $result);
58 0         0 $result;
59             }
60              
61             sub _extract($$$)
62 0     0   0 { my ($self, $params, $data, $ct) = @_;
63 0         0 my $endpoint = $self->endpoint('update/extract', params => $params);
64 0         0 my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
65 0         0 $self->request($endpoint, $result, $data, $ct);
66 0         0 $result;
67             }
68              
69             sub _add($$$)
70 0     0   0 { my ($self, $docs, $attrs, $params) = @_;
71 0   0     0 $attrs ||= {};
72 0   0     0 $params ||= [];
73              
74 0         0 my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
75 0         0 my $add = $doc->createElement('add');
76 0         0 $add->setAttribute($_ => $attrs->{$_}) for sort keys %$attrs;
77              
78             $add->addChild($self->_doc2xml($doc, $_))
79 0         0 for @$docs;
80              
81 0         0 $doc->setDocumentElement($add);
82              
83 0         0 my $endpoint = $self->endpoint('update', params => $params);
84 0         0 my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
85 0         0 $self->request($endpoint, $result, $doc);
86 0         0 $result;
87             }
88              
89             sub _doc2xml($$$)
90 0     0   0 { my ($self, $doc, $this) = @_;
91              
92 0         0 my $node = $doc->createElement('doc');
93 0   0     0 my $boost = $this->boost || 1.0;
94 0 0       0 $node->setAttribute(boost => $boost) if $boost != 1.0;
95              
96 0         0 foreach my $field ($this->fields)
97 0         0 { my $fnode = $doc->createElement('field');
98 0         0 $fnode->setAttribute(name => $field->{name});
99              
100 0   0     0 my $boost = $field->{boost} || 1.0;
101 0 0 0     0 $fnode->setAttribute(boost => $boost)
102             if $boost < 0.9999 || $boost > 1.0001;
103              
104             $fnode->setAttribute(update => $field->{update})
105 0 0       0 if defined $field->{update};
106              
107 0         0 $fnode->appendText($field->{content});
108 0         0 $node->addChild($fnode);
109             }
110 0         0 $node;
111             }
112              
113 0     0   0 sub _commit($) { my ($s, $attr) = @_; $s->simpleUpdate(commit => $attr) }
  0         0  
114 0     0   0 sub _optimize($) { my ($s, $attr) = @_; $s->simpleUpdate(optimize => $attr) }
  0         0  
115 0     0   0 sub _delete($$) { my $self = shift; $self->simpleUpdate(delete => @_) }
  0         0  
116 0     0   0 sub _rollback() { shift->simpleUpdate('rollback') }
117              
118             sub _terms($)
119 0     0   0 { my ($self, $terms) = @_;
120 0         0 my $endpoint = $self->endpoint('terms', params => $terms);
121 0         0 my $result = Apache::Solr::Result->new(params => $terms, endpoint => $endpoint, core => $self);
122              
123 0         0 $self->request($endpoint, $result);
124              
125 0   0     0 my $table = $result->decoded->{terms} || {};
126 0         0 while(my ($field, $terms) = each %$table)
127             { my @terms = map [ $_ => $terms->{$_} ],
128 0         0 sort {$terms->{$b} <=> $terms->{$a}} keys %$terms;
  0         0  
129 0         0 $result->terms($field => \@terms);
130             }
131              
132 0         0 $result;
133             }
134              
135             #--------------------------
136              
137             sub request($$;$$)
138 0     0 1 0 { my ($self, $url, $result, $body, $body_ct) = @_;
139              
140 0 0 0     0 if(blessed $body && $body->isa('XML::LibXML::Document'))
141 0   0     0 { $body_ct ||= 'text/xml; charset=utf-8';
142 0         0 $body = \$body->toString;
143             }
144              
145 0         0 $self->SUPER::request($url, $result, $body, $body_ct);
146             }
147              
148             sub _cleanup_parsed($);
149             sub decodeResponse($)
150 0     0 0 0 { my ($self, $resp) = @_;
151              
152 0 0       0 $resp->content_type =~ m/xml/i
153             or return undef;
154              
155 0   0     0 my $dec = $self->xmlsimple->XMLin(
156             $resp->decoded_content || $resp->content,
157             parseropts => { huge => 1 },
158             );
159              
160 0         0 _cleanup_parsed $dec;
161             }
162              
163             sub _cleanup_parsed($)
164 92     92   36108 { my $data = shift;
165              
166 92 100       241 if(!ref $data) { return $data }
  17 100       86  
    50          
    0          
167             elsif(ref $data eq 'HASH')
168 68         308 { my %d = %$data; # start with shallow copy
169              
170             # Hash
171 68 100       239 if(my $lst = delete $d{lst})
172 23 100       67 { foreach (ref $lst eq 'ARRAY' ? @$lst : $lst)
173 34         90 { my $name = delete $_->{name};
174 34         101 $d{$name} = $_;
175             }
176             }
177              
178             # Array
179 68 100       174 if(my $arr = delete $d{arr})
180 3 50       13 { foreach (ref $arr eq 'ARRAY' ? @$arr : $arr)
181 6         12 { my $name = delete $_->{name};
182 6         18 my ($type, $values) = %$_;
183 6 100       16 $values = [$values] if ref $values ne 'ARRAY';
184 6         17 $d{$name} = $values;
185             }
186             }
187              
188             # XXX haven't found a clear list of what can be expected here
189 68         120 foreach my $type (qw/int long float double bool date str text/)
190 544 100       1143 { my $items = delete $d{$type} or next;
191 30 100       83 foreach (ref $items eq 'ARRAY' ? @$items : $items)
192 82 100       233 { my ($name, $value) = ref $_ eq 'HASH' ? ($_->{name}, $_->{_}) : ('', $_);
193              
194 82 100 33     205 $value = $value eq 'true' || $_->{_} eq 1
195             if $type eq 'bool';
196              
197 82         233 $d{$name} = $value;
198             }
199             }
200              
201 68         260 foreach my $key (keys %d)
202 186 100       520 { $d{$key} = _cleanup_parsed($d{$key}) if ref $d{$key};
203             }
204 68         374 return \%d;
205             }
206             elsif(ref $data eq 'ARRAY')
207 7         22 { return [ map _cleanup_parsed($_), @$data ];
208             }
209             elsif(ref $data eq 'DateTime')
210 0         0 { return $data;
211             }
212 0   0     0 else {panic ref $data || $data}
213             }
214              
215              
216             sub simpleUpdate($$;$)
217 0     0 1 0 { my ($self, $command, $attrs, $content) = @_;
218 0   0     0 $attrs ||= {};
219 0         0 my $params = [ commit => delete $attrs->{commit} ];
220 0         0 my $endpoint = $self->endpoint('update', params => $params);
221 0         0 my $result = Apache::Solr::Result->new(params => $params, endpoint => $endpoint, core => $self);
222 0         0 my $doc = $self->simpleDocument($command, $attrs, $content);
223 0         0 $self->request($endpoint, $result, $doc);
224 0         0 $result;
225             }
226              
227              
228             sub simpleDocument($;$$)
229 0     0 1 0 { my ($self, $command, $attrs, $content) = @_;
230 0         0 my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
231 0         0 my $top = $doc->createElement($command);
232 0         0 $doc->setDocumentElement($top);
233              
234 0   0     0 $attrs ||= {};
235 0         0 $top->setAttribute($_ => $attrs->{$_}) for sort keys %$attrs;
236              
237 0 0 0     0 if(!defined $content) {}
    0          
238             elsif(ref $content eq 'HASH' || ref $content eq 'ARRAY')
239 0 0       0 { my @c = ref $content eq 'HASH' ? %$content : @$content;
240 0         0 while(@c)
241 0         0 { my ($name, $values) = (shift @c, shift @c);
242 0 0       0 foreach my $value (ref $values eq 'ARRAY' ? @$values : $values)
243 0         0 { my $node = $doc->createElement($name);
244 0         0 $node->appendText($value);
245 0         0 $top->addChild($node);
246             }
247             }
248             }
249             else
250 0         0 { $top->appendText($content);
251             }
252 0         0 $doc;
253             }
254              
255             sub endpoint($@)
256 1     1 1 733 { my ($self, $action, %args) = @_;
257 1   50     3 my $params = $args{params} ||= [];
258              
259 1 50 0     3 if(ref $params eq 'HASH') { $params->{wt} ||= 'xml' }
  0         0  
260 1         3 else { $args{params} = [ wt => 'xml', @$params ] }
261              
262 1         7 $self->SUPER::endpoint($action => %args);
263             }
264              
265             1;