blob: 2a8806c3ab5792b84c770afc0f6c79ed2c1fec2a [file] [log] [blame]
Nils Diewald7148c6f2015-05-04 15:07:53 +00001package Kalamar::API;
Nils Diewald996aa552014-12-02 03:26:44 +00002use Mojo::Base 'Mojolicious::Plugin';
Nils Diewald791b5902014-12-04 04:47:24 +00003use Scalar::Util qw/blessed weaken/;
Nils Diewald996aa552014-12-02 03:26:44 +00004use strict;
5use warnings;
6
7# KorAP Search engine for Mojolicious::Plugin::Search
8
Nils Diewald7148c6f2015-05-04 15:07:53 +00009# TODO: Add fixtures
10# TODO: Support search in corpus and virtualcollection
11# TODO: Support caching everywhere!
12# TODO: Correct use of stash info everywhere!
Akronf809da22015-06-18 22:08:19 +020013# TODO: Alot is now underneath "meta"
14
Nils Diewald996aa552014-12-02 03:26:44 +000015
16# Register the plugin
17sub register {
18 my ($plugin, $mojo, $index_class, $param) = @_;
19 $param ||= {};
20
21 # Add attributes to the index class
22 $index_class->attr(api => $param->{api});
23 $index_class->attr([qw/cutoff
24 query_language
25 time_exceeded
26 api_request
Akronbc213c02017-04-20 16:45:55 +020027 authorized
Nils Diewald996aa552014-12-02 03:26:44 +000028 _api_cache
29 api_response
30 benchmark
Akron9cc3eaf2015-06-10 22:15:52 +020031 query_jsonld
Akron27ae9ec2015-06-23 00:43:21 +020032 collection
Akron9cc3eaf2015-06-10 22:15:52 +020033 collection_jsonld/]);
Nils Diewald996aa552014-12-02 03:26:44 +000034 $index_class->attr(no_cache => 0);
Akron515851a2017-05-02 12:53:17 +020035 $index_class->attr(status => 200);
Nils Diewald996aa552014-12-02 03:26:44 +000036};
37
38
39# Search the index
40sub search {
41 my $self = shift;
42 my $index = shift;
43
Nils Diewald8f4b5da2014-12-03 22:13:39 +000044 # Get controller
Nils Diewald996aa552014-12-02 03:26:44 +000045 my $c = $index->controller;
46
Nils Diewald996aa552014-12-02 03:26:44 +000047 # If there is a callback, do async
48 my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
49
Nils Diewald8f4b5da2014-12-03 22:13:39 +000050 # No query defined
51 unless ($index->query) {
52 return $cb->($index) if $cb;
53 return;
54 };
Nils Diewald996aa552014-12-02 03:26:44 +000055
Nils Diewald8f4b5da2014-12-03 22:13:39 +000056 # Get query url
57 my $url = _query_url($index, @_);
Nils Diewald996aa552014-12-02 03:26:44 +000058
59 # Cache based on URL
60 $index->_api_cache('total-' . $url->to_string);
Akron27ae9ec2015-06-23 00:43:21 +020061
Nils Diewald8f4b5da2014-12-03 22:13:39 +000062 my %param = @_;
Nils Diewald996aa552014-12-02 03:26:44 +000063
64 # Set context based on parameter
Akron30ee5142015-06-26 01:50:14 +020065 # base/s:p
Akronb7bae282016-02-10 01:17:25 +010066 $url->query({ context => $param{'context'} // '40-t,40-t' });
67 # 'base/s:p'/'paragraph'
Nils Diewald8f4b5da2014-12-03 22:13:39 +000068
69 # Set path to search
70 $url->path('search');
Nils Diewald996aa552014-12-02 03:26:44 +000071
72 # Check cache for total results
73 my $total_results;
74
Akron1b0c2652017-04-27 15:28:49 +020075 # In case the user is not known, it is assumed, the user is not logged in
76 my $user = $c->stash('user') // 'not_logged_in';
Akron3cd391e2017-03-29 23:42:54 +020077
Nils Diewald996aa552014-12-02 03:26:44 +000078 if (!$index->no_cache &&
Akron3cd391e2017-03-29 23:42:54 +020079 defined ($total_results = $c->chi->get($user . $index->_api_cache))) {
Nils Diewald996aa552014-12-02 03:26:44 +000080
81 # Set total results from cache
82 $index->total_results($total_results);
83 $c->app->log->debug('Get total result from cache');
84
85 # Set cutoff unless already set
86 $url->query({cutoff => 'true'}) unless defined $index->cutoff;
87 };
88
89 # Set api request for debugging
90 $index->api_request($url->to_string);
91
92 # Create new user agent and set timeout to 2 minutes
Akronbc213c02017-04-20 16:45:55 +020093 #my $ua = $c->user->ua;
94 #$tx = $plugin->ua->start($tx);
95
96 #$ua->inactivity_timeout(120);
Nils Diewald996aa552014-12-02 03:26:44 +000097
Nils Diewald8f4b5da2014-12-03 22:13:39 +000098 # Debugging
Nils Diewald996aa552014-12-02 03:26:44 +000099 $c->app->log->debug('Search for ' . $index->api_request);
100
101 # Search non-blocking
102 if ($cb) {
Akronbc213c02017-04-20 16:45:55 +0200103 $c->user->auth_request(
104 get => $url => sub {
Akron3cd391e2017-03-29 23:42:54 +0200105 my $tx = pop;
Akronbc213c02017-04-20 16:45:55 +0200106
Akron3cd391e2017-03-29 23:42:54 +0200107 $self->_process_response('matches', $index, $tx);
108 weaken $index;
109 return $cb->($index);
Nils Diewald996aa552014-12-02 03:26:44 +0000110 });
Nils Diewald996aa552014-12-02 03:26:44 +0000111 }
Nils Diewald87507832015-05-01 23:36:41 +0000112
Nils Diewald996aa552014-12-02 03:26:44 +0000113 # Search blocking
114 else {
Akroneb250162017-04-28 17:26:49 +0200115 my $tx = $c->user->auth_request(get => $url);
Nils Diewald034ea702015-01-16 19:41:52 +0000116 $self->_process_response('matches', $index, $tx);
117 return $index;
Nils Diewald996aa552014-12-02 03:26:44 +0000118 };
119};
120
121
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000122# Trace query serialization
123sub trace {
124 my $self = shift;
125 my $index = shift;
126
127 # Get controller
128 my $c = $index->controller;
129
130 # If there is a callback, do async
131 my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
132
133 my %param = @_;
134
135 # No query defined
136 unless ($index->query(delete $param{query})) {
137 return $cb->($index) if $cb;
138 return;
139 };
140
141 # Get query url
142 my $url = _query_url($index, @_);
143
144 $url->path('search');
145
146 # Create new user agent and set timeout to 30 seconds
Akron3cd391e2017-03-29 23:42:54 +0200147 my $ua = $c->user->ua; # Mojo::UserAgent->new;
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000148 $ua->inactivity_timeout(30);
149
150 # Build transaction
151 my $tx = $ua->build_tx(TRACE => $url);
152
153 # non-blocking
154 if ($cb) {
Nils Diewald791b5902014-12-04 04:47:24 +0000155 weaken $index;
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000156
157 # Trace non-blocking
158 $ua->start(
159 $tx => sub {
Akron1b0c2652017-04-27 15:28:49 +0200160 $self->_process_response('trace', $index, pop);
161 return $cb->($index);
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000162 });
163 }
164 # Trace blocking
165 else {
166 my $tx = $ua->start($url);
167 return $self->_process_response('trace', $index, $tx);
168 };
169};
170
171
172# Get match info
173sub match {
174 my $self = shift;
175 my $index = shift;
176
177 # Get controller
178 my $c = $index->controller;
179
180 # If there is a callback, do async
181 my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
182
183 my %param = @_;
184
185 my $url = Mojo::URL->new($index->api);
186
Nils Diewald87507832015-05-01 23:36:41 +0000187 # Legacy: In old versions, doc_id contained text_id
Akroneb250162017-04-28 17:26:49 +0200188 # $param{doc_id} .= '.' . $param{text_id} if $param{text_id};
Nils Diewald87507832015-05-01 23:36:41 +0000189
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000190 # Use hash slice to create path
Akroneb250162017-04-28 17:26:49 +0200191 $url->path(join('/', 'corpus', @param{qw/corpus_id doc_id text_id match_id/}, 'matchInfo'));
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000192
193 # Build match id
194 # $match = 'match-' . $corpus . '!' . $corpus . '_' . $doc . '-' . $match;
195
196 my %query;
197 $query{foundry} = $param{foundry};
198 $query{layer} = $param{layer} if defined $param{layer};
199 $query{spans} = $param{spans} ? 'true' : 'false';
200
201 # Add query
202 $url->query(\%query);
203
204 $c->app->log->debug('Match info: ' . $url);
205
206 # Create new user agent and set timeout to 30 seconds
Akron515851a2017-05-02 12:53:17 +0200207 # my $ua = $c->user->ua; # Mojo::UserAgent->new;
208 # $ua->inactivity_timeout(30);
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000209
210 # non-blocking
211 if ($cb) {
Akroneb250162017-04-28 17:26:49 +0200212 # $c->u
213 $c->user->auth_request(get =>
214 # $ua->get(
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000215 $url => sub {
Akron3cd391e2017-03-29 23:42:54 +0200216 my $tx = pop;
217 $self->_process_response('match', $index, $tx);
Akron515851a2017-05-02 12:53:17 +0200218 weaken $index;
Akron3cd391e2017-03-29 23:42:54 +0200219 return $cb->($index);
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000220 });
221 }
222
223 # Match info blocking
224 else {
Akroneb250162017-04-28 17:26:49 +0200225 my $tx = $c->user->auth_request(get => $url);
Akron515851a2017-05-02 12:53:17 +0200226 # my $tx = $ua->get($url);
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000227 return $self->_process_response('match', $index, $tx);
228 };
229};
230
231
Nils Diewald87507832015-05-01 23:36:41 +0000232# Get resource information
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000233sub resource {
234 my $self = shift;
235 my $index = shift;
236
237 # Get controller
238 my $c = $index->controller;
239
Akron1b0c2652017-04-27 15:28:49 +0200240 my $user = $c->stash('user') // 'not_logged_in';
Akron3cd391e2017-03-29 23:42:54 +0200241
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000242 # If there is a callback, do async
243 my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
244
245 my %param = @_;
246
247 # Rename info endpoints regarding resource
248 my $type = $param{type} // 'collection';
249 $type = 'virtualcollection' if $type eq 'collection';
250
Nils Diewald87507832015-05-01 23:36:41 +0000251 # Create resource URL
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000252 my $url = Mojo::URL->new($index->api)->path($type);
253
Nils Diewald87507832015-05-01 23:36:41 +0000254 # Debugging
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000255 $c->app->log->debug('Get resource info on '. $url);
256
257 # Check for cached information
Akron3cd391e2017-03-29 23:42:54 +0200258 if (my $json = $c->chi->get($user . $url->to_string)) {
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000259
Nils Diewald87507832015-05-01 23:36:41 +0000260 # TODO: That's unfortunate, as it prohibits caching of multiple resources
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000261 $c->app->log->debug('Get resource info from cache');
262 $c->stash('search.resource' => $json);
263 return $cb->($index) if $cb;
264 return $json;
265 };
266
267 $c->stash('search._resource_cache' => $url->to_string);
268
269 # Create new user agent and set timeout to 30 seconds
Akroneb250162017-04-28 17:26:49 +0200270 #my $ua = $c->ua; # Mojo::UserAgent->new;
271 #$ua->inactivity_timeout(30);
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000272
Nils Diewald87507832015-05-01 23:36:41 +0000273 # Get resource information async
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000274 if ($cb) {
Akroneb250162017-04-28 17:26:49 +0200275 $c->user->auth_request(get =>
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000276 $url => sub {
Akron3cd391e2017-03-29 23:42:54 +0200277 $self->_process_response('resource', $index, pop);
Akroneb250162017-04-28 17:26:49 +0200278 weaken $index;
Akron3cd391e2017-03-29 23:42:54 +0200279 return $cb->($index);
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000280 })
281 }
Nils Diewald87507832015-05-01 23:36:41 +0000282
283 # Get resource information blocking
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000284 else {
Akroneb250162017-04-28 17:26:49 +0200285 my $tx = $c->user->auth_request(get => $url);
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000286 $self->_process_response('resource', $index, $tx);
287 };
288};
289
290
291# Process response - especially error messages etc.
Nils Diewald996aa552014-12-02 03:26:44 +0000292sub _process_response {
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000293 my ($self, $type, $index, $tx) = @_;
Nils Diewald996aa552014-12-02 03:26:44 +0000294 my $c = $index->controller;
295
296 # An error has occurded
297 if (my $e = $tx->error) {
Akron515851a2017-05-02 12:53:17 +0200298
299 # Send error
300 $self->_notify_on_error($c, 0, $tx->res->json);
301
302 # $c->notify(
303 # error =>
304 # ($e->{code} ? $e->{code} . ': ' : '') .
305 # $e->{message} . ' for ' . $type . ' (remote)'
306 # );
307 $index->status($e->{code} // 0);
Nils Diewald996aa552014-12-02 03:26:44 +0000308 return;
309 };
310
311 # Response was fine
312 if (my $res = $tx->success) {
313
Nils Diewald996aa552014-12-02 03:26:44 +0000314 # Json failure
315 my $json;
316 unless ($json = $res->json) {
317 $c->notify(error => 'JSON response is invalid');
Akron515851a2017-05-02 12:53:17 +0200318 $index->status(0);
Nils Diewald996aa552014-12-02 03:26:44 +0000319 return;
320 };
321
Akron27ae9ec2015-06-23 00:43:21 +0200322 # Set api response as jsonld
323 $index->api_response($json);
324
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000325 # expected response for matches
326 if ($type eq 'matches') {
327 $self->_process_response_matches($index, $json);
328 }
329 elsif ($type eq 'trace') {
330 $self->_process_response_trace($index, $json);
331 }
332 elsif ($type eq 'match') {
333 $self->_process_response_match($index, $json);
334 }
335 elsif ($type eq 'resource') {
336 $self->_process_response_resource($index, $json);
Nils Diewald996aa552014-12-02 03:26:44 +0000337 };
338
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000339 return 1 if ref $json ne 'HASH';
Nils Diewald996aa552014-12-02 03:26:44 +0000340
Akronf55504a2015-06-18 16:42:55 +0200341 $self->_notify_on_warnings($c, $json);
Nils Diewald996aa552014-12-02 03:26:44 +0000342 $self->_notify_on_error($c, 0, $json);
343 }
344
345 # Request failed
346 else {
Akron515851a2017-05-02 12:53:17 +0200347 $index->status(0);
Nils Diewald996aa552014-12-02 03:26:44 +0000348 $self->_notify_on_error($c, 1, $tx->res);
349 };
350 return 1;
351};
352
353
Nils Diewald87507832015-05-01 23:36:41 +0000354# Handle match results
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000355sub _process_response_matches {
356 my ($self, $index, $json) = @_;
Nils Diewald996aa552014-12-02 03:26:44 +0000357
Akron27ae9ec2015-06-23 00:43:21 +0200358 # Process meta
359 my $meta = $json->{meta};
360
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000361 # Reformat benchmark counter
Akron27ae9ec2015-06-23 00:43:21 +0200362 my $benchmark = $meta->{benchmark};
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000363 if ($benchmark && $benchmark =~ s/\s+(m)?s$//) {
364 $benchmark = sprintf("%.2f", $benchmark) . ($1 ? $1 : '') . 's';
365 };
366
367 # Set benchmark
368 $index->benchmark($benchmark);
369
370 # Set time exceeded
Akron27ae9ec2015-06-23 00:43:21 +0200371 if ($meta->{timeExceeded} && $meta->{timeExceeded} eq Mojo::JSON::true) {
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000372 $index->time_exceeded(1);
373 };
374
375 # Set result values
Akron27ae9ec2015-06-23 00:43:21 +0200376 $index->items_per_page($meta->{itemsPerPage});
377
Akronbc213c02017-04-20 16:45:55 +0200378 # Set authorization
379 $index->authorized($meta->{authorized}) if $meta->{authorized};
Akron9cc3eaf2015-06-10 22:15:52 +0200380
Akronc1457bf2015-06-11 19:24:00 +0200381 # Bouncing query
Akronbc213c02017-04-20 16:45:55 +0200382 # if ($json->{query}) {
383 # $index->query_jsonld($json->{query});
384 # };
Akron27ae9ec2015-06-23 00:43:21 +0200385
Akronc1457bf2015-06-11 19:24:00 +0200386 # Legacy
Akron27ae9ec2015-06-23 00:43:21 +0200387 # elsif ($json->{request}->{query}) {
388 # $index->query_jsonld($json->{request}->{query});
389 # };
Akronc1457bf2015-06-11 19:24:00 +0200390
391 # Bouncing collection query
392 if ($json->{collection}) {
393 $index->collection_jsonld($json->{collection});
394 }
395
396 # Legacy
Akron27ae9ec2015-06-23 00:43:21 +0200397 # elsif ($json->{request}->{collection}) {
398 # $index->collection_jsonld($json->{request}->{collection});
399 # };
Akron48b1e4d2015-06-17 18:47:01 +0200400
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000401 $index->results(_map_matches($json->{matches}));
402
403 # Total results not set by stash
404 if ($index->total_results == -1) {
405
Akron27ae9ec2015-06-23 00:43:21 +0200406 if ($meta->{totalResults} && $meta->{totalResults} > -1) {
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000407 my $c = $index->controller;
Akron089ae2c2017-04-17 17:59:23 +0200408
409 # TODO: Cache on auth_keys!
Akron1b0c2652017-04-27 15:28:49 +0200410 my $user = $c->stash('user') // 'not_logged_in';
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000411
412 $c->app->log->debug('Cache total result');
Akron3cd391e2017-03-29 23:42:54 +0200413 $c->chi->set($user . $index->_api_cache => $meta->{totalResults}, '120min');
Akron27ae9ec2015-06-23 00:43:21 +0200414 $index->total_results($meta->{totalResults});
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000415 };
416 };
417};
418
419
420# Process query serialization response
421sub _process_response_match {
422 my ($self, $index, $json) = @_;
423 $index->results(_map_match($json));
424};
425
426
Nils Diewald87507832015-05-01 23:36:41 +0000427# Process trace response
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000428sub _process_response_trace {
429 my ($self, $index, $json) = @_;
430 $index->query_jsonld($json);
431};
432
Nils Diewald87507832015-05-01 23:36:41 +0000433
434# Process resource response
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000435sub _process_response_resource {
436 my ($self, $index, $json) = @_;
437 my $c = $index->controller;
438
Akron1b0c2652017-04-27 15:28:49 +0200439 my $user = $c->stash('user') // 'not_logged_in';
Akron3cd391e2017-03-29 23:42:54 +0200440
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000441 # TODO: That's unfortunate, as it prohibits multiple resources
442 $c->stash('search.resource' => $json);
443 $c->app->log->debug('Cache resource info');
Akron3cd391e2017-03-29 23:42:54 +0200444 $c->chi->set($user . $c->stash('search._resource_cache') => $json, '24 hours');
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000445};
446
447
Nils Diewald87507832015-05-01 23:36:41 +0000448# Parse error messages and forward them to the user
Nils Diewald996aa552014-12-02 03:26:44 +0000449sub _notify_on_error {
450 my ($self, $c, $failure, $res) = @_;
451 my $json = $res;
452
453 my $log = $c->app->log;
454
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000455 # Check if the response is already json
Nils Diewald996aa552014-12-02 03:26:44 +0000456 if (blessed $res) {
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000457 $json = $res->json if blessed $res ne 'Mojo::JSON';
Nils Diewald996aa552014-12-02 03:26:44 +0000458 };
459
Akronf55504a2015-06-18 16:42:55 +0200460 # Check json response error message
Nils Diewald996aa552014-12-02 03:26:44 +0000461 if ($json) {
Akron27ae9ec2015-06-23 00:43:21 +0200462
463 # Legacy, but still in use by Kustvakt
Nils Diewald996aa552014-12-02 03:26:44 +0000464 if ($json->{error}) {
Akron27ae9ec2015-06-23 00:43:21 +0200465
Nils Diewald996aa552014-12-02 03:26:44 +0000466 # Temp
467 $json->{error} =~ s/;\s+null$//;
468 $c->notify(error => $json->{error});
469 return;
470 }
471
472 # New error messages
473 elsif ($json->{errstr}) {
474 # Temp
475 $json->{errstr} =~ s/;\s+null$//;
476 $c->notify(error => $json->{errstr});
477 return;
478 }
479
Akronf55504a2015-06-18 16:42:55 +0200480 elsif ($json->{errors}) {
481 my $errors = $json->{errors};
482 # TODO: Check for ref!
483 foreach (@$errors) {
Akronbc213c02017-04-20 16:45:55 +0200484 $c->notify(
485 error =>
486 ($_->[0] ? $_->[0] . ': ' : '') .
Akron515851a2017-05-02 12:53:17 +0200487 ($_->[1] || 'Unknown')
Akronbc213c02017-04-20 16:45:55 +0200488 );
Akronf55504a2015-06-18 16:42:55 +0200489 };
490 }
491
Nils Diewald996aa552014-12-02 03:26:44 +0000492 # policy service error messages
493 elsif ($json->{status}) {
494 $c->notify(error => 'Middleware error ' . $json->{status});
495 return;
496 };
497 };
498
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000499 # Doesn't matter what - there is a failure!
Nils Diewald996aa552014-12-02 03:26:44 +0000500 if ($failure) {
501 $c->notify(error => (
502 ($res->{code} ? $res->{code} . ': ' : '') .
503 ($res->{message} ? $res->{message} : 'Unknown error') .
504 ' (remote)'
505 ));
506 };
507};
508
509
Akronf55504a2015-06-18 16:42:55 +0200510sub _notify_on_warnings {
511 my ($self, $c, $json) = @_;
512
513 # Add warnings (Legacy)
514 if ($json->{warning}) {
515 $json->{warning} =~ s/;\s+null$//;
516 $c->notify(warn => $json->{warning});
517 }
518
519 # Add warnings
520 elsif ($json->{warnings}) {
521
522 my $warnings = $json->{warnings};
523 # TODO: Check for ref!
524 foreach (@$warnings) {
525 $c->notify(
Akron46b9f212017-05-01 13:55:17 +0200526 warn =>
527 ($_->[0] ? $_->[0] . ': ' : '') .
528 $_->[1]
529 );
Akronf55504a2015-06-18 16:42:55 +0200530 };
531 };
532};
533
534
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000535# Cleanup array of matches
Nils Diewald996aa552014-12-02 03:26:44 +0000536sub _map_matches {
537 return () unless $_[0];
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000538 map { _map_match($_) } @{ shift() };
539};
540
541
542# Cleanup single match
543sub _map_match {
544 my $x = shift or return;
Akron7f613e02016-11-07 02:50:44 +0100545 $x->{matchID} =~ s/^match\-(?:[^!]+!|[^_]+_)[^\.]+?\.[^-]+?-// or
546 $x->{matchID} =~ s!^match\-(?:[^\/]+\/){2}[^-]+?-!!;
Nils Diewald87507832015-05-01 23:36:41 +0000547
Akron30ee5142015-06-26 01:50:14 +0200548 (
549 $x->{corpusID},
550 $x->{docID},
551 $x->{textID}
552 ) = ($x->{textSigle} =~ /^([^_]+?)_+([^\.]+?)\.(.+?)$/);
553
554 # $x->{docID} =~ s/^[^_]+_//;
Nils Diewald87507832015-05-01 23:36:41 +0000555 # Legacy: In old versions the text_id was part of the doc_id
Akron30ee5142015-06-26 01:50:14 +0200556 # unless ($x->{textID}) {
557 # ($x->{docID}, $x->{textID}) = split '\.', $x->{docID};
558 # };
559
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000560 $x;
561};
562
Nils Diewald87507832015-05-01 23:36:41 +0000563# Build query url
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000564sub _query_url {
565 my ($index, %param) = @_;
566
567 # Set cutoff from param
568 $index->cutoff(delete $param{cutoff});
569
Akron27ae9ec2015-06-23 00:43:21 +0200570 # Set collection from param
571 $index->collection(delete $param{collection});
572
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000573 # Set query language
574 $index->query_language(delete $param{query_language} // 'poliqarp');
575
576 # Should results be cached? Defaults to "yes"
577 $index->no_cache(1) if $param{no_cache};
578
579 # Init the query with stuff coming from the index
580 my %query;
581 $query{q} = $index->query;
582 $query{ql} = $index->query_language;
583 $query{page} = $index->start_page if $index->start_page;
584 $query{count} = $index->items_per_page if $index->items_per_page;
Akron27ae9ec2015-06-23 00:43:21 +0200585 $query{cq} = $index->collection if $index->collection;
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000586 $query{cutoff} = 'true' if $index->cutoff;
587
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000588 # Create query url
589 my $url = Mojo::URL->new($index->api);
590 $url->query(\%query);
591 return $url;
Nils Diewald996aa552014-12-02 03:26:44 +0000592};
593
594
5951;
596
Nils Diewald8f4b5da2014-12-03 22:13:39 +0000597
Nils Diewald996aa552014-12-02 03:26:44 +0000598__END__
599
600=pod
601
Nils Diewald9dfe0102015-05-19 16:14:06 +0000602=encoding utf8
Nils Diewald996aa552014-12-02 03:26:44 +0000603
Nils Diewald9dfe0102015-05-19 16:14:06 +0000604=head1 NAME
605
606Kalamar::API
607
608=head1 DESCRIPTION
609
610L<Kalamar::API> is a search engine class for L<Mojolicious::Plugin::Search>
611that uses the KorAP Web API.
612
613B<The Web API as well as L<Mojolicious::Plugin::Search> are not stable yet,
614so this class is expected to change in the near future. Do not rely on its API!>
615
616
617=head1 METHODS
618
619L<Kalamar::API> inherits all methods from L<Mojolicious::Plugin> and
620implements the following new ones.
621
622
623=head2 register
624
625See L<Mojolicious::Plugin::Search> for registering search engines.
626In addition to the mentioned query parameters, the following parameters are supported:
627
628
629=over 2
630
631=item B<query_language>
632
633One of the supported query languages, like C<poliqarp> or C<annis>.
634
635
636=item B<cutoff>
637
638Cut off results following the current page (i.e. don't count the number of results).
639
640
641=item B<no_cache>
642
643Do not cache search results. Defaults to C<0>.
644
645
646=back
647
648In addition to the mentioned index attributes, the following attributes are supported:
649
Akron456abd92015-06-02 15:07:21 +0200650=over 2
Nils Diewald9dfe0102015-05-19 16:14:06 +0000651
652=item B<api>
653
654The API address.
655
656
657=item B<time_exceeded>
658
659Report on time outs, that may mean, not all results were retrieved.
660
661
662=item B<api_request>
663
664Report the whole API request.
665
666
667=item B<api_response>
668
669Report the whole API response (a KoralQuery object).
670
671
672=item B<benchmarks>
673
674Report on processing time for benchmarking.
675
676
677=item B<query_jsonld>
678
679The KoralQuery realization of the C<query> object.
680
681=back
682
683=head2 search
684
685Search the index.
686
687=head2 trace
688
689Trace query serializations.
690
691=head2 match
692
693Get match information.
694
695=head2 resource
696
697Get resource information.
698
699
700=head1 COPYRIGHT AND LICENSE
701
Akron46b9f212017-05-01 13:55:17 +0200702Copyright (C) 2015-2017, L<IDS Mannheim|http://www.ids-mannheim.de/>
Nils Diewald9dfe0102015-05-19 16:14:06 +0000703Author: L<Nils Diewald|http://nils-diewald.de/>
704
705Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
706Corpus Analysis Platform at the
707L<Institute for the German Language (IDS)|http://ids-mannheim.de/>,
708member of the
709L<Leibniz-Gemeinschaft|http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/>
710and supported by the L<KobRA|http://www.kobra.tu-dortmund.de> project,
711funded by the
712L<Federal Ministry of Education and Research (BMBF)|http://www.bmbf.de/en/>.
713
714Kalamar is free software published under the
Akron456abd92015-06-02 15:07:21 +0200715L<BSD-2 License|https://raw.githubusercontent.com/KorAP/Kalamar/master/LICENSE>.
Nils Diewald9dfe0102015-05-19 16:14:06 +0000716
717=cut