Support text information endpoint
Change-Id: I93499d56e235f99dd56189e0174b5c572ae36189
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index f5151b4..1fa6c52 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -210,9 +210,6 @@
# Base query route
$r->get('/')->to('search2#query')->name('index');
- # Corpus route
- $r->get('/corpus')->to('Search2#corpus_info')->name('corpus');
-
# Documentation routes
$r->get('/doc')->to('documentation#page', page => 'korap')->name('doc_start');
$r->get('/doc/:page')->to('documentation#page', scope => undef);
@@ -223,8 +220,9 @@
$r->get('/contact')->mail_to_chiffre('documentation#contact');
# Match route
- my $corpus = $r->route('/corpus/:corpus_id');
- my $doc = $corpus->get('/:doc_id');
+ # Corpus route
+ my $corpus = $r->get('/corpus')->to('Search2#corpus_info')->name('corpus');
+ my $doc = $r->route('/corpus/:corpus_id/:doc_id');
my $text = $doc->get('/:text_id')->to('search2#text_info')->name('text');
my $match = $doc->get('/:text_id/:match_id')->to('search2#match_info')->name('match');
@@ -234,9 +232,6 @@
$user->get('/logout')->to(action => 'logout')->name('logout');
# $r->any('/register')->to(action => 'register')->name('register');
# $r->any('/forgotten')->to(action => 'pwdforgotten')->name('pwdforgotten');
-
- # Default user is called 'korap'
- # $r->route('/user/:user/:collection')
};
diff --git a/lib/Kalamar/Controller/Search2.pm b/lib/Kalamar/Controller/Search2.pm
index 7922a2a..f071fbf 100644
--- a/lib/Kalamar/Controller/Search2.pm
+++ b/lib/Kalamar/Controller/Search2.pm
@@ -14,6 +14,13 @@
# TODO:
# Add match_info template for HTML
+#
+# TODO:
+# Support search in corpus and virtualcollection
+#
+# TODO:
+# set caches with timing like '120min'
+
# Query endpoint
@@ -71,26 +78,13 @@
$c->stash(items_per_page => $items_per_page);
+ # TODO:
+ # if ($v->param('action') eq 'inspect') use trace!
+
# Set offset
# From Mojolicious::Plugin::Search::Index
$query{offset} = $v->param('o') || ((($page // 1) - 1) * ($items_per_page || 1));
-
- # already set by stash - or use plugin param
- # else {
- # $items_per_page = $c->stash('search.count') // $plugin->items_per_page
- # };
-
- # Set start page based on param
- #if ($query{p}) {
- # $index->start_page(delete $param{start_page});
- #}
- ## already set by stash
- #elsif ($c->stash('search.start_page')) {
- # $index->start_page($c->stash('search.start_page'));
- #};
-
-
# Create remote request URL
my $url = Mojo::URL->new($c->korap->api);
$url->path('search');
@@ -202,16 +196,6 @@
# Set result values
$c->stash(items_per_page => $meta->{itemsPerPage});
- ## Bouncing query
- ## if ($json->{query}) {
- ## $index->query_jsonld($json->{query});
- ## };
-
- ## Legacy
- ## elsif ($json->{request}->{query}) {
- ## $index->query_jsonld($json->{request}->{query});
- ## };
-
# Bouncing collection query
if ($json->{corpus} || $json->{collection}) {
$c->stash(corpus_jsonld => ($json->{corpus} || $json->{collection}));
@@ -258,6 +242,115 @@
};
+# Corpus info endpoint
+# This replaces the collections endpoint
+sub corpus_info {
+ my $c = shift;
+
+ # Input validation
+ my $v = $c->validation;
+ $v->optional('cq');
+
+ my $url = Mojo::URL->new($c->korap->api);
+
+ # Use hash slice to create path
+ $url->path('statistics');
+
+ # Add query
+ $url->query(corpusQuery => $v->param('cq'));
+
+ $c->app->log->debug("Statistics info: $url");
+
+ # Async
+ $c->render_later;
+
+ # Request koral, maybe cached
+ $c->cached_koral_p('get', $url)
+
+ # Process response
+ ->then(
+ sub {
+ my $json = shift;
+ return $c->render(
+ json => $c->notifications(json => $json),
+ status => 200
+ );
+ }
+ )
+
+ # Deal with errors
+ ->catch(
+ sub {
+ return $c->render(
+ json => $c->notifications('json')
+ )
+ }
+ )
+
+ # Start IOLoop
+ ->wait;
+
+ return 1;
+};
+
+
+# Text info endpoint
+sub text_info {
+ my $c = shift;
+
+ # Input validation
+ my $v = $c->validation;
+ $v->optional('fields');
+
+ my %query = (fields => '@all');
+ $query{fields} = $v->param('fields') if $v->param('fields');
+
+ my $url = Mojo::URL->new($c->korap->api);
+
+ # Use hash slice to create path
+ $url->path(
+ join('/', (
+ 'corpus',
+ $c->stash('corpus_id'),
+ $c->stash('doc_id'),
+ $c->stash('text_id')
+ ))
+ );
+ $url->query(%query);
+
+ # Async
+ $c->render_later;
+
+ # Request koral, maybe cached
+ $c->cached_koral_p('get', $url)
+
+ # Process response
+ ->then(
+ sub {
+ my $json = shift;
+ return $c->render(
+ json => $c->notifications(json => $json),
+ status => 200
+ );
+ }
+ )
+
+ # Deal with errors
+ ->catch(
+ sub {
+ return $c->render(
+ json => $c->notifications('json')
+ )
+ }
+ )
+
+ # Start IOLoop
+ ->wait;
+
+ return 1;
+};
+
+
# Match info endpoint
sub match_info {
my $c = shift;
@@ -330,61 +423,6 @@
};
-# Get information about
-# This replaces the collections endpoint
-sub corpus_info {
- my $c = shift;
-
- # Input validation
- my $v = $c->validation;
- $v->optional('cq');
-
- my $url = Mojo::URL->new($c->korap->api);
-
- # Use hash slice to create path
- $url->path('statistics');
-
- # Add query
- $url->query(corpusQuery => $v->param('cq'));
-
- # Set stash
- $c->stash('search._resource_cache' => $url->to_string);
-
- $c->app->log->debug("Statistics info: $url");
-
- # Async
- $c->render_later;
-
- # Request koral, maybe cached
- $c->cached_koral_p('get', $url)
-
- # Process response
- ->then(
- sub {
- my $json = shift;
- return $c->render(
- json => $c->notifications(json => $json),
- status => 200
- );
- }
- )
-
- # Deal with errors
- ->catch(
- sub {
- return $c->render(
- json => $c->notifications('json')
- )
- }
- )
-
- # Start IOLoop
- ->wait;
-
- return 1;
-};
-
-
# Cleanup array of matches
sub _map_matches {
return c() unless $_[0];
@@ -419,3 +457,178 @@
__END__
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+Kalamar::Controller::Search
+
+
+=head1 DESCRIPTION
+
+L<Kalamar::Controller::Search> is the controller class for
+search related endpoints in Kalamar. Actions are released when routes
+match.
+
+
+=head1 METHODS
+
+L<Kalamar::Controller::Search> inherits all methods from
+L<Mojolicious::Controller> and implements the following new ones.
+
+=head2 query
+
+ GET /?q=Baum&ql=poliqarp
+
+Action for all queries to the system. Returns C<HTML> only for the moment.
+
+The following parameters are supported.
+
+
+=over 2
+
+=item B<q>
+
+The query string. This may any query written in a supported query language.
+
+
+=item B<ql>
+
+The query language. This may be any query language supported by the system,
+written as the API expects the string.
+
+
+=item B<action>
+
+May be C<inspect>. In that case, the serialized request is mirrored instead of
+processed.
+
+B<This switch is experimental and may change without warnings!>
+
+
+=item B<snippet>
+
+If set, the query is returned in the snippet view template.
+
+B<This parameter is experimental and may change without warnings!>
+
+
+=item B<cutoff>
+
+If set, the query will be cut off after the matches.
+
+B<This parameter is directly forwarded to the API and may not be supported in the future.>
+
+
+=item B<count>
+
+If set, the query will be only return the given number of matches,
+in case the API supports it. Will fallback to the default number of matches defined
+by the API or the backend.
+
+B<This parameter is directly forwarded to the API and may not be supported in the future.>
+
+
+=item B<p>
+
+If set, the query will page to the given number of pages in the result set.
+Will default to 1.
+
+B<This parameter is directly forwarded to the API and may not be supported in the future.>
+
+=item B<o>
+
+If set, the matches will offset to the given match in the result set.
+Will default to 0.
+
+B<This parameter is directly forwarded to the API and may not be supported in the future.>
+
+=item B<context>
+
+The context of the snippets to retrieve. Defaults to C<40-t,40-t>.
+
+B<This parameter is directly forwarded to the API and may not be supported in the future.>
+
+=item B<cq>
+
+The corpus query to limit the search to.
+
+=back
+
+
+=head2 corpus
+
+ /corpus?cq=corpusSigle+%3D+%22GOE%22
+
+Returns statistics information for a virtual corpus.
+
+=head2 text
+
+ /corpus/:corpus_id/:doc_id/:text_id
+
+Returns meta data information for a specific text.
+
+
+=head2 match
+
+ /corpus/:corpus_id/:doc_id/:text_id/:match_id?foundry=*
+
+Returns information to a match either as a C<JSON> or an C<HTML> document.
+The path defines the concrete match, by corpus identifier, document identifier,
+text identifier (all information as given by DeReKo), and match identifier
+(essentially the position of the match in the document, including highlight information).
+
+The following parameters are supported.
+
+
+=over 2
+
+=item B<foundry>
+
+Expects a foundry definition for retrieved information.
+If not given, returns all annotations for the match.
+If given, returns only given layer information for the defined foundry.
+
+B<This parameter is experimental and may change without warnings!>
+
+
+=item B<layer>
+
+Expects a layer definition for retrieved information.
+If not given, returns all annotations for the foundry.
+If given, returns only given layer information for the defined foundry.
+
+B<This parameter is experimental and may change without warnings!>
+
+
+=item B<spans>
+
+Boolean value - either C<true> or C<false> - indicating, whether span information
+(i.e. for tree structures) should be retrieved.
+
+=back
+
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2015-2018, L<IDS Mannheim|http://www.ids-mannheim.de/>
+Author: L<Nils Diewald|http://nils-diewald.de/>
+
+Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
+Corpus Analysis Platform at the
+L<Institute for the German Language (IDS)|http://ids-mannheim.de/>,
+member of the
+L<Leibniz-Gemeinschaft|http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/>
+and supported by the L<KobRA|http://www.kobra.tu-dortmund.de> project,
+funded by the
+L<Federal Ministry of Education and Research (BMBF)|http://www.bmbf.de/en/>.
+
+Kalamar is free software published under the
+L<BSD-2 License|https://raw.githubusercontent.com/KorAP/Kalamar/master/LICENSE>.
+
+=cut
diff --git a/t/fixtures/fake_backend.pl b/t/fixtures/fake_backend.pl
index 9f06697..7506d84 100644
--- a/t/fixtures/fake_backend.pl
+++ b/t/fixtures/fake_backend.pl
@@ -114,6 +114,24 @@
return 1;
};
+# Textinfo fixtures
+get '/corpus/:corpusId/:docId/:textId' => sub {
+ my $c = shift;
+
+ my $file = join('_', (
+ 'textinfo',
+ $c->stash('corpusId'),
+ $c->stash('docId'),
+ $c->stash('textId')
+ ));
+
+ my $slug = slugify($file);
+
+ # Get response based on query parameter
+ my $response = $c->load_response($slug);
+ return $c->render(%$response);
+};
+
# Matchinfo fixtures
get '/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
diff --git a/t/fixtures/response_textinfo_goe_agi_00000.json b/t/fixtures/response_textinfo_goe_agi_00000.json
new file mode 100644
index 0000000..384df8d
--- /dev/null
+++ b/t/fixtures/response_textinfo_goe_agi_00000.json
@@ -0,0 +1,4 @@
+{
+ "status" : 200,
+ "json" : {"messages":[["Response format is temporary"]],"@context":"http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld","meta":{},"document":{"@type":"koral:document","fields":[{"@type":"koral:field","type":"type:string","key":"textSigle","value":"GOE/AGI/00000"},{"@type":"koral:field","type":"type:text","key":"author","value":"Goethe, Johann Wolfgang von"},{"@type":"koral:field","type":"type:string","key":"docSigle","value":"GOE/AGI"},{"@type":"koral:field","type":"type:text","key":"docTitle","value":"Goethe: Autobiographische Schriften III, (1813-1816, 1819-1829)"},{"@type":"koral:field","type":"type:string","key":"textType","value":"Autobiographie"},{"@type":"koral:field","type":"type:string","key":"language","value":"de"},{"@type":"koral:field","type":"type:string","key":"availability","value":"ACA-NC"},{"@type":"koral:field","type":"type:text","key":"title","value":"Italienische Reise"},{"@type":"koral:field","type":"type:date","key":"creationDate","value":"1813"},{"@type":"koral:field","type":"type:string","key":"foundries","value":"corenlp corenlp/constituency corenlp/morpho corenlp/sentences dereko dereko/structure dereko/structure/base-sentences-paragraphs-pagebreaks malt malt/dependency marmot marmot/morpho opennlp opennlp/morpho opennlp/sentences treetagger treetagger/morpho"},{"@type":"koral:field","type":"type:date","key":"pubDate","value":"1982"},{"@type":"koral:field","type":"type:store","key":"reference","value":"Goethe, Johann Wolfgang von: Italienische Reise. Auch ich in Arkadien!, (Geschrieben: 1813-1816), In: Goethe, Johann Wolfgang von: Goethes Werke, Bd. 11, Autobiographische Schriften III, Hrsg.: Trunz, Erich. München: Verlag C. H. Beck, 1982, S. 9-349"},{"@type":"koral:field","type":"type:text","key":"subTitle","value":"Auch ich in Arkadien!"},{"@type":"koral:field","type":"type:store","key":"tokenSource","value":"base#tokens"},{"@type":"koral:field","type":"type:store","key":"publisher","value":"Verlag C. H. Beck"},{"@type":"koral:field","type":"type:text","key":"corpusAuthor","value":"Goethe, Johann Wolfgang von"},{"@type":"koral:field","type":"type:store","key":"layerInfos","value":"corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens"},{"@type":"koral:field","type":"type:string","key":"pubPlace","value":"München"},{"@type":"koral:field","type":"type:text","key":"corpusTitle","value":"Goethes Werke"},{"@type":"koral:field","type":"type:string","key":"corpusSigle","value":"GOE"},{"@type":"koral:field","type":"type:store","key":"corpusEditor","value":"Trunz, Erich"}]}}
+}
diff --git a/t/fixtures/response_textinfo_goe_agy_00000.json b/t/fixtures/response_textinfo_goe_agy_00000.json
new file mode 100644
index 0000000..9182e24
--- /dev/null
+++ b/t/fixtures/response_textinfo_goe_agy_00000.json
@@ -0,0 +1,4 @@
+{
+ "status" : 200,
+ "json" : {"errors":[[630,"Document not found"]],"messages":[["Response format is temporary"]],"@context":"http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld","meta":{},"document":{"@type":"koral:document","fields":[]}}
+}
diff --git a/t/text_info.t b/t/text_info.t
new file mode 100644
index 0000000..7c06bf6
--- /dev/null
+++ b/t/text_info.t
@@ -0,0 +1,42 @@
+use Mojo::Base -strict;
+use Test::Mojo;
+use Test::More;
+use Mojo::File qw/path/;
+
+
+#####################
+# Start Fake server #
+#####################
+my $mount_point = '/api/';
+$ENV{KALAMAR_API} = $mount_point;
+
+my $t = Test::Mojo->new('Kalamar');
+
+# Mount fake backend
+# Get the fixture path
+my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, 'fixtures');
+my $fake_backend = $t->app->plugin(
+ Mount => {
+ $mount_point =>
+ $fixtures_path->child('fake_backend.pl')
+ }
+);
+# Configure fake backend
+$fake_backend->pattern->defaults->{app}->log($t->app->log);
+
+# Query passed
+$t->get_ok('/corpus/GOE/AGI/00000')
+ ->status_is(200)
+ ->json_is('/document/fields/0/key', 'textSigle')
+ ->json_is('/document/fields/0/value', 'GOE/AGI/00000')
+ ;
+
+# Not found - should probably be 404
+$t->get_ok('/corpus/GOE/AGY/00000')
+ ->status_is(200)
+ ->json_is('/notifications/0/1', '630: Document not found')
+ ;
+
+
+done_testing;
+__END__