Still some problems with non-blocking search
diff --git a/lib/Korap.pm b/lib/Korap.pm
index 49922b2..05135f2 100644
--- a/lib/Korap.pm
+++ b/lib/Korap.pm
@@ -26,8 +26,9 @@
TagHelpers::Pagination
Notifications
Number::Commify
- KorapSearch
+ Search
KorapInfo
+ KorapHelpers
KorapTagHelpers
/) {
# Oro::Account
@@ -35,7 +36,8 @@
$self->plugin($_);
};
- $self->plugin(AssetPack => { minify => 1 });
+# $self->plugin(AssetPack => { minify => 1 });
+ $self->plugin('AssetPack');
$self->plugin('AssetPack::LibSass');
$self->plugin('MailException' => $self->config('MailException'));
@@ -60,10 +62,11 @@
$self->asset(
'korap.js' => (
-# '/js/d3.v3.min.js',
+ '/js/d3.v3.min.js',
# '/js/dagre-d3.min.js',
-# '/js/translateTree.js',
-# '/js/jquery-2.0.0.min.js',
+ '/js/dagre-d3.js',
+ '/js/translateTree.js',
+ '/js/jquery-2.0.0.min.js', # Temp
'/js/tutorialCookie.js',
'/js/translateTable.js',
'/js/hint.js',
@@ -103,15 +106,28 @@
$collection->to('info#about_collection');
my $collection_id = $collection->bridge('/:collection_id');
# stats
- $collection_id->search;
+};
+
+
+1;
+
+
+__END__
+
+
+
+# No shortcut!
+
+# $collection_id->search;
# Corpus data
my $corpus_res = $r->route('/corpus');
my $corpus = $corpus_res->route('/:corpus_id');
+
# Todo: Stats
- $corpus->search->name('search_corpus');
- my $doc = $corpus->route('/#doc_id');
- $doc->search->name('search_document');
+# $corpus->search->name('search_corpus');
+# my $doc = $corpus->route('/#doc_id');
+# $doc->search->name('search_document');
# Match data
my $match = $doc->route('/:match_id');
diff --git a/lib/Korap/API.pm b/lib/Korap/API.pm
new file mode 100644
index 0000000..59ea869
--- /dev/null
+++ b/lib/Korap/API.pm
@@ -0,0 +1,273 @@
+package Korap::API;
+use Mojo::Base 'Mojolicious::Plugin';
+use Scalar::Util 'blessed';
+use strict;
+use warnings;
+
+# KorAP Search engine for Mojolicious::Plugin::Search
+
+# Todo: Add fixtures
+# Todo: Support search in corpus and virtualcollection
+
+
+# Register the plugin
+sub register {
+ my ($plugin, $mojo, $index_class, $param) = @_;
+ $param ||= {};
+
+ # Add attributes to the index class
+ $index_class->attr(api => $param->{api});
+ $index_class->attr([qw/cutoff
+ query_language
+ time_exceeded
+ api_request
+ _api_cache
+ api_response
+ benchmark
+ query_jsonld/]);
+ $index_class->attr(no_cache => 0);
+};
+
+
+# Search the index
+sub search {
+ my $self = shift;
+ my $index = shift;
+
+ 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 = @_;
+
+ # 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);
+
+ # Cache based on URL
+ $index->_api_cache('total-' . $url->to_string);
+
+ # Set context based on parameter
+ $url->query({ context => $c->param('context') // 'paragraph' });
+
+ # Check cache for total results
+ my $total_results;
+
+ if (!$index->no_cache &&
+ defined ($total_results = $c->chi->get($index->_api_cache))) {
+
+ # Set total results from cache
+ $index->total_results($total_results);
+ $c->app->log->debug('Get total result from cache');
+
+ # Set cutoff unless already set
+ $url->query({cutoff => 'true'}) unless defined $index->cutoff;
+ };
+
+ # Set api request for debugging
+ $index->api_request($url->to_string);
+
+ # Create new user agent and set timeout to 2 minutes
+ my $ua = Mojo::UserAgent->new;
+ $ua->inactivity_timeout(120);
+
+ # Denugging
+ $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);
+ 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);
+ };
+};
+
+
+sub _process_response {
+ my ($self, $index, $tx) = @_;
+ my $c = $index->controller;
+
+ # An error has occurded
+ if (my $e = $tx->error) {
+ $c->notify(
+ error =>
+ ($e->{code} ? $e->{code} . ': ' : '') .
+ $e->{message} . ' (remote)'
+ );
+ return;
+ };
+
+ # Response was fine
+ if (my $res = $tx->success) {
+
+ # Set api response for debugging
+ $index->api_response($res->body) if $c->korap_test_port;
+
+ # Json failure
+ my $json;
+ unless ($json = $res->json) {
+ $c->notify(error => 'JSON response is invalid');
+ 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);
+ };
+
+ # 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});
+ };
+ };
+
+ # Add warnings (Legacy)
+ if ($json->{warning}) {
+ $json->{warning} =~ s/;\s+null$//;
+ $c->notify(warn => $json->{warning});
+ };
+
+ $self->_notify_on_error($c, 0, $json);
+ }
+
+ # Request failed
+ else {
+ $self->_notify_on_error($c, 1, $tx->res);
+ };
+ return 1;
+};
+
+
+
+sub _notify_on_error {
+ my ($self, $c, $failure, $res) = @_;
+ my $json = $res;
+
+ my $log = $c->app->log;
+
+ if (blessed $res) {
+ if (blessed $res ne 'Mojo::JSON') {
+ $json = $res->json;
+ };
+ };
+
+ if ($json) {
+ if ($json->{error}) {
+ # Temp
+ $json->{error} =~ s/;\s+null$//;
+ $c->notify(error => $json->{error});
+ return;
+ }
+
+ # New error messages
+ elsif ($json->{errstr}) {
+ # Temp
+ $json->{errstr} =~ s/;\s+null$//;
+ $c->notify(error => $json->{errstr});
+ return;
+ }
+
+ # policy service error messages
+ elsif ($json->{status}) {
+ $c->notify(error => 'Middleware error ' . $json->{status});
+ return;
+ };
+ };
+
+ if ($failure) {
+ $c->notify(error => (
+ ($res->{code} ? $res->{code} . ': ' : '') .
+ ($res->{message} ? $res->{message} : 'Unknown error') .
+ ' (remote)'
+ ));
+ };
+};
+
+
+sub _map_matches {
+ return () unless $_[0];
+ map {
+ $_->{ID} =~ s/^match\-[^!]+![^-]+-//;
+ $_->{docID} =~ s/^[^_]+_//;
+ $_;
+ } @{ shift() };
+};
+
+
+1;
+
+__END__
+
+=pod
+
+Additionally supported query parameters:
+- query_language
+- cutoff
+- no_cache
+
+Additional index attributes:
+- api
+- time_exceeded
+- api_request
+- api_response
+- benchmark
+- query_jsonld
diff --git a/lib/Korap/Plugin/KorapHelpers.pm b/lib/Korap/Plugin/KorapHelpers.pm
new file mode 100644
index 0000000..45cf3cb
--- /dev/null
+++ b/lib/Korap/Plugin/KorapHelpers.pm
@@ -0,0 +1,25 @@
+package Korap::Plugin::KorapHelpers;
+use Mojo::Base 'Mojolicious::Plugin';
+
+sub register {
+ my ($plugin, $mojo) = @_;
+
+ $mojo->helper(
+ korap_test_port => sub {
+ my $c = shift;
+ if (defined $c->stash('korap.test_port')) {
+ return $c->stash('korap.test_port');
+ };
+
+ if ($c->req->url->to_abs->port == 6666 ||
+ $c->app->mode =~ m/^development|test$/) {
+ $c->stash('korap.test_port' => 1);
+ return 1;
+ };
+
+ $c->stash('korap.test_port' => 0);
+ return 0;
+ });
+};
+
+1;
diff --git a/lib/Korap/Plugin/KorapSearch.pm b/lib/Korap/Plugin/KorapSearch.pm
index 0ad520e..7aab520 100644
--- a/lib/Korap/Plugin/KorapSearch.pm
+++ b/lib/Korap/Plugin/KorapSearch.pm
@@ -156,7 +156,7 @@
$c->stash('search.itemsPerPage' => $count);
# Only set this when on test port
- if ($c->stash('test_port')) {
+ if ($c->korap_test_port) {
$c->stash('search.apirequest' => $url->to_string);
};
diff --git a/lib/Korap/Plugin/KorapTagHelpers.pm b/lib/Korap/Plugin/KorapTagHelpers.pm
index 0cd0364..2c1cd6c 100644
--- a/lib/Korap/Plugin/KorapTagHelpers.pm
+++ b/lib/Korap/Plugin/KorapTagHelpers.pm
@@ -26,7 +26,7 @@
# Tutorial wasn't embedded - but opened for testing
elsif ($c->param('testing') &&
- $c->stash('test_port') &&
+ $c->korap_test_port &&
$param{tests}) {
my $tests = $param{tests} // [];
diff --git a/lib/Korap/Search.pm b/lib/Korap/Search.pm
index 5918fd9..5980b3f 100644
--- a/lib/Korap/Search.pm
+++ b/lib/Korap/Search.pm
@@ -1,6 +1,9 @@
package Korap::Search;
use Mojo::Base 'Mojolicious::Controller';
+# Add X-Forwarded-For to user agent call everywhere
+
+
# This action will render a template
sub remote {
my $c = shift;
@@ -10,11 +13,6 @@
my $api = $c->config('KorAP')->{'api-0.1'};
# Todo: Support query building directly
-
- $c->stash(test_port => (
- $c->req->url->to_abs->port == 6666 ||
- $c->app->mode =~ m/^development|test$/) ? 1 : 0);
-
if ((scalar $c->param('action') // '') eq 'inspect') {
my $url = Mojo::URL->new($api)->path('search');
diff --git a/lib/Korap/Tutorial.pm b/lib/Korap/Tutorial.pm
index e8726ee..f9d99da 100644
--- a/lib/Korap/Tutorial.pm
+++ b/lib/Korap/Tutorial.pm
@@ -4,11 +4,6 @@
sub page {
my $c = shift;
- # Redundant in Search
- $c->stash(test_port => (
- $c->req->url->to_abs->port == 6666 ||
- $c->app->mode =~ m/^development|test$/) ? 1 : 0);
-
if ($c->param('embedded')) {
$c->layout('snippet');
$c->stash(embedded => 1);