Now working full non-blocking (maybe leaky here and there)
diff --git a/lib/Korap/API.pm b/lib/Korap/API.pm
index 59ea869..70ec277 100644
--- a/lib/Korap/API.pm
+++ b/lib/Korap/API.pm
@@ -8,7 +8,8 @@
# Todo: Add fixtures
# Todo: Support search in corpus and virtualcollection
-
+# Todo: Support caching everywhere!
+# Todo: Correct use of stash info everywhere!
# Register the plugin
sub register {
@@ -34,43 +35,30 @@
my $self = shift;
my $index = shift;
+ # Get controller
my $c = $index->controller;
- # No query defined
- return unless $index->query;
-
# If there is a callback, do async
my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
- my %param = @_;
+ # No query defined
+ unless ($index->query) {
+ return $cb->($index) if $cb;
+ return;
+ };
- # Set cutoff from param
- $index->cutoff(delete $param{cutoff});
-
- # Set query language
- $index->query_language(delete $param{query_language} // 'poliqarp');
-
- # TODO: This should also be a query parameter
- $index->no_cache(1) if $param{no_cache} or $c->param('no_cache');
-
- my %query;
- $query{q} = $index->query;
- $query{ql} = $index->query_language;
- $query{page} = $index->start_page;
- $query{count} = $index->items_per_page;
- $query{cutoff} = 'true' if $index->cutoff;
-
- # Todo: support corpus and collection
- # Create query url
- my $url = Mojo::URL->new($index->api);
- $url->path('search');
- $url->query(\%query);
+ # Get query url
+ my $url = _query_url($index, @_);
# Cache based on URL
$index->_api_cache('total-' . $url->to_string);
+ my %param = @_;
# Set context based on parameter
- $url->query({ context => $c->param('context') // 'paragraph' });
+ $url->query({ context => $param{'context'} // 'paragraph' });
+
+ # Set path to search
+ $url->path('search');
# Check cache for total results
my $total_results;
@@ -90,43 +78,186 @@
$index->api_request($url->to_string);
# Create new user agent and set timeout to 2 minutes
- my $ua = Mojo::UserAgent->new;
+ my $ua = $c->ua; # Mojo::UserAgent->new;
$ua->inactivity_timeout(120);
- # Denugging
+ # Debugging
$c->app->log->debug('Search for ' . $index->api_request);
# Search non-blocking
if ($cb) {
- # Non-blocking request
$ua->get(
- $url->to_string => sub {
- my ($ua, $tx) = @_;
- unless ($tx->success) {
- if (my $e = $tx->error) {
- warn 'Problem: ' . $e->{message};
- return $cb->($index);
- };
- };
-
- $self->_process_response($index, pop);
+ $url => sub {
+ my $tx = pop;
+ $self->_process_response('matches', $index, $tx);
return $cb->($index);
});
-
- Mojo::IOLoop->wait unless Mojo::IOLoop->is_running;
}
-
# Search blocking
else {
my $tx = $ua->get($url);
- return $self->_process_response($index, $tx);
+ return $self->_process_response('matches', $index, $tx);
};
};
+# Trace query serialization
+sub trace {
+ my $self = shift;
+ my $index = shift;
+
+ # Get controller
+ my $c = $index->controller;
+
+ # If there is a callback, do async
+ my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
+
+ my %param = @_;
+
+ # No query defined
+ unless ($index->query(delete $param{query})) {
+ return $cb->($index) if $cb;
+ return;
+ };
+
+ # Get query url
+ my $url = _query_url($index, @_);
+
+ $url->path('search');
+
+ # Create new user agent and set timeout to 30 seconds
+ my $ua = $c->ua; # Mojo::UserAgent->new;
+ $ua->inactivity_timeout(30);
+
+ # Build transaction
+ my $tx = $ua->build_tx(TRACE => $url);
+
+ # non-blocking
+ if ($cb) {
+
+ # Trace non-blocking
+ $ua->start(
+ $tx => sub {
+ $self->_process_response('trace', $index, pop);
+ return $cb->($index);
+ });
+ }
+ # Trace blocking
+ else {
+ my $tx = $ua->start($url);
+ return $self->_process_response('trace', $index, $tx);
+ };
+};
+
+
+# Get match info
+sub match {
+ my $self = shift;
+ my $index = shift;
+
+ # Get controller
+ my $c = $index->controller;
+
+ # If there is a callback, do async
+ my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
+
+ my %param = @_;
+
+ my $url = Mojo::URL->new($index->api);
+
+ # Use hash slice to create path
+ $url->path(join('/', 'corpus', @param{qw/corpus_id doc_id match_id/}, 'matchInfo'));
+
+ # Build match id
+ # $match = 'match-' . $corpus . '!' . $corpus . '_' . $doc . '-' . $match;
+
+ my %query;
+ $query{foundry} = $param{foundry};
+ $query{layer} = $param{layer} if defined $param{layer};
+ $query{spans} = $param{spans} ? 'true' : 'false';
+
+ # Add query
+ $url->query(\%query);
+
+ $c->app->log->debug('Match info: ' . $url);
+
+ # Create new user agent and set timeout to 30 seconds
+ my $ua = $c->ua; # Mojo::UserAgent->new;
+ $ua->inactivity_timeout(30);
+
+ # non-blocking
+ if ($cb) {
+ $ua->get(
+ $url => sub {
+ my $tx = pop;
+ $self->_process_response('match', $index, $tx);
+ return $cb->($index);
+ });
+ }
+
+ # Match info blocking
+ else {
+ my $tx = $ua->get($url);
+ return $self->_process_response('match', $index, $tx);
+ };
+};
+
+
+# Trace query serialization
+sub resource {
+ my $self = shift;
+ my $index = shift;
+
+ # Get controller
+ my $c = $index->controller;
+
+ # If there is a callback, do async
+ my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
+
+ my %param = @_;
+
+ # Rename info endpoints regarding resource
+ my $type = $param{type} // 'collection';
+ $type = 'virtualcollection' if $type eq 'collection';
+
+ my $url = Mojo::URL->new($index->api)->path($type);
+
+ $c->app->log->debug('Get resource info on '. $url);
+
+ # Check for cached information
+ if (my $json = $c->chi->get($url->to_string)) {
+
+ # TODO: That's unfortunate, as it prohibits multiple resources
+ $c->app->log->debug('Get resource info from cache');
+ $c->stash('search.resource' => $json);
+ return $cb->($index) if $cb;
+ return $json;
+ };
+
+ $c->stash('search._resource_cache' => $url->to_string);
+
+ # Create new user agent and set timeout to 30 seconds
+ my $ua = $c->ua; # Mojo::UserAgent->new;
+ $ua->inactivity_timeout(30);
+
+ if ($cb) {
+ $ua->get(
+ $url => sub {
+ $self->_process_response('resource', $index, pop);
+ return $cb->($index);
+ })
+ }
+ else {
+ my $tx = $ua->get($url);
+ $self->_process_response('resource', $index, $tx);
+ };
+};
+
+
+# Process response - especially error messages etc.
sub _process_response {
- my ($self, $index, $tx) = @_;
+ my ($self, $type, $index, $tx) = @_;
my $c = $index->controller;
# An error has occurded
@@ -152,33 +283,21 @@
return;
};
- # Reformat benchmark counter
- my $benchmark = $json->{benchmark};
- if ($benchmark && $benchmark =~ s/\s+(m)?s$//) {
- $benchmark = sprintf("%.2f", $benchmark) . ($1 ? $1 : '') . 's';
- };
- # Set benchmark
- $index->benchmark($benchmark);
-
- # Set time exceeded
- if ($json->{timeExceeded} && $json->{timeExceeded} eq Mojo::JSON::true) {
- $index->time_exceeded(1);
+ # expected response for matches
+ if ($type eq 'matches') {
+ $self->_process_response_matches($index, $json);
+ }
+ elsif ($type eq 'trace') {
+ $self->_process_response_trace($index, $json);
+ }
+ elsif ($type eq 'match') {
+ $self->_process_response_match($index, $json);
+ }
+ elsif ($type eq 'resource') {
+ $self->_process_response_resource($index, $json);
};
- # Set result values
- $index->items_per_page($json->{itemsPerPage});
- $index->query_jsonld($json->{request}->{query});
- $index->results(_map_matches($json->{matches}));
-
- # Total results not set by stash
- if ($index->total_results == -1) {
-
- if ($json->{totalResults} && $json->{totalResults} > -1) {
- $c->app->log->debug('Cache total result');
- $c->chi->set($index->_api_cache => $json->{totalResults}, '120min');
- $index->total_results($json->{totalResults});
- };
- };
+ return 1 if ref $json ne 'HASH';
# Add warnings (Legacy)
if ($json->{warning}) {
@@ -197,19 +316,79 @@
};
+sub _process_response_matches {
+ my ($self, $index, $json) = @_;
+ # Reformat benchmark counter
+ my $benchmark = $json->{benchmark};
+ if ($benchmark && $benchmark =~ s/\s+(m)?s$//) {
+ $benchmark = sprintf("%.2f", $benchmark) . ($1 ? $1 : '') . 's';
+ };
+
+ # Set benchmark
+ $index->benchmark($benchmark);
+
+ # Set time exceeded
+ if ($json->{timeExceeded} && $json->{timeExceeded} eq Mojo::JSON::true) {
+ $index->time_exceeded(1);
+ };
+
+ # Set result values
+ $index->items_per_page($json->{itemsPerPage});
+ $index->query_jsonld($json->{request}->{query});
+ $index->results(_map_matches($json->{matches}));
+
+ # Total results not set by stash
+ if ($index->total_results == -1) {
+
+ if ($json->{totalResults} && $json->{totalResults} > -1) {
+ my $c = $index->controller;
+
+ $c->app->log->debug('Cache total result');
+ $c->chi->set($index->_api_cache => $json->{totalResults}, '120min');
+ $index->total_results($json->{totalResults});
+ };
+ };
+};
+
+
+# Process query serialization response
+sub _process_response_match {
+ my ($self, $index, $json) = @_;
+ $index->results(_map_match($json));
+};
+
+
+# Process query serialization response
+sub _process_response_trace {
+ my ($self, $index, $json) = @_;
+ $index->query_jsonld($json);
+};
+
+sub _process_response_resource {
+ my ($self, $index, $json) = @_;
+ my $c = $index->controller;
+
+ # TODO: That's unfortunate, as it prohibits multiple resources
+ $c->stash('search.resource' => $json);
+ $c->app->log->debug('Cache resource info');
+ $c->chi->set($c->stash('search._resource_cache') => $json, '24 hours');
+};
+
+
+# Parse the error messages
sub _notify_on_error {
my ($self, $c, $failure, $res) = @_;
my $json = $res;
my $log = $c->app->log;
+ # Check if the response is already json
if (blessed $res) {
- if (blessed $res ne 'Mojo::JSON') {
- $json = $res->json;
- };
+ $json = $res->json if blessed $res ne 'Mojo::JSON';
};
+ # Chec json response error message
if ($json) {
if ($json->{error}) {
# Temp
@@ -233,6 +412,7 @@
};
};
+ # Doesn't matter what - there is a failure!
if ($failure) {
$c->notify(error => (
($res->{code} ? $res->{code} . ': ' : '') .
@@ -243,18 +423,53 @@
};
+# Cleanup array of matches
sub _map_matches {
return () unless $_[0];
- map {
- $_->{ID} =~ s/^match\-[^!]+![^-]+-//;
- $_->{docID} =~ s/^[^_]+_//;
- $_;
- } @{ shift() };
+ map { _map_match($_) } @{ shift() };
+};
+
+
+# Cleanup single match
+sub _map_match {
+ my $x = shift or return;
+ $x->{ID} =~ s/^match\-[^!]+![^-]+-//;
+ $x->{docID} =~ s/^[^_]+_//;
+ $x;
+};
+
+
+sub _query_url {
+ my ($index, %param) = @_;
+
+ # Set cutoff from param
+ $index->cutoff(delete $param{cutoff});
+
+ # Set query language
+ $index->query_language(delete $param{query_language} // 'poliqarp');
+
+ # Should results be cached? Defaults to "yes"
+ $index->no_cache(1) if $param{no_cache};
+
+ # Init the query with stuff coming from the index
+ my %query;
+ $query{q} = $index->query;
+ $query{ql} = $index->query_language;
+ $query{page} = $index->start_page if $index->start_page;
+ $query{count} = $index->items_per_page if $index->items_per_page;
+ $query{cutoff} = 'true' if $index->cutoff;
+
+ # Todo: support corpus and collection
+ # Create query url
+ my $url = Mojo::URL->new($index->api);
+ $url->query(\%query);
+ return $url;
};
1;
+
__END__
=pod