api/v0: switch the API to the new model
This commit is contained in:
parent
0635e37e13
commit
230c595e80
5 changed files with 99 additions and 137 deletions
|
@ -5,6 +5,18 @@ use DBD::SQLite::Constants ":dbd_sqlite_string_mode";
|
|||
use DBIx::Migration;
|
||||
use Pooru::Schema;
|
||||
|
||||
use Pooru::API::V0::Model::Media;
|
||||
use Pooru::API::V0::Model::Tags;
|
||||
|
||||
use DBI;
|
||||
|
||||
use constant {
|
||||
"Pooru::API::V0::Model::Media::ROWS" => 12,
|
||||
"Pooru::API::V0::Model::Media::SIMILAR_ROWS" => 6,
|
||||
"Pooru::API::V0::Model::Tags::ROWS" => 100,
|
||||
"Pooru::API::V0::Model::Tags::SEARCH_ROWS" => 15,
|
||||
};
|
||||
|
||||
sub _search_opts ()
|
||||
{
|
||||
return {
|
||||
|
@ -63,6 +75,32 @@ sub startup ($self)
|
|||
dir => $self->app->home->child("migrations")->to_string,
|
||||
})->migrate;
|
||||
|
||||
$self->helper(dbh => sub {
|
||||
state $dbh = DBI->connect(
|
||||
$config->{dsn},
|
||||
$config->{db_username},
|
||||
$config->{db_password},
|
||||
{
|
||||
PrintError => 0,
|
||||
RaiseError => 1,
|
||||
AutoCommit => 1,
|
||||
($config->{dbi_connect_args} // {})->%*,
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
$self->dbh->{FetchHashKeyName} = "NAME_lc";
|
||||
if ($self->dbh->{Driver}->{Name} eq "SQLite") {
|
||||
$self->dbh->do("PRAGMA foreign_keys = ON");
|
||||
}
|
||||
|
||||
$self->helper(tags_model => sub {
|
||||
state $model = Pooru::API::V0::Model::Tags->new($self->dbh)
|
||||
});
|
||||
$self->helper(media_model => sub {
|
||||
state $model = Pooru::API::V0::Model::Media->new($self->dbh)
|
||||
});
|
||||
|
||||
my $r = $self->routes;
|
||||
|
||||
$r->get("/meta")->to("meta#index")->name("meta");
|
||||
|
|
|
@ -7,52 +7,26 @@ my %search_opts = Pooru::API::V0::_search_opts->%*;
|
|||
|
||||
sub _list_no_tags ($self, $page)
|
||||
{
|
||||
my $paged_media = $self->schema->resultset("Media")
|
||||
->search(undef, $search_opts{Media})
|
||||
->page($page);
|
||||
my @media = map +{
|
||||
id => $_->id,
|
||||
storage_id => $_->storage_id,
|
||||
filename => $_->filename,
|
||||
content_type => $_->content_type,
|
||||
upload_datetime => $_->upload_datetime,
|
||||
}, $paged_media->all;
|
||||
my $paged_media = $self->media_model->get->page($page);
|
||||
|
||||
return $self->render(json => {
|
||||
media => [@media],
|
||||
pager => {$self->pager($paged_media)},
|
||||
media => $paged_media->fetchall,
|
||||
pager => $paged_media->pager,
|
||||
});
|
||||
}
|
||||
|
||||
# Executes
|
||||
#
|
||||
# SELECT * FROM tagged_media_view
|
||||
# WHERE (tag_display = t_1 OR ... OR tag_display = t_N)
|
||||
# GROUP BY media_id HAVING COUNT(media_id) = N
|
||||
sub _list_with_tags ($self, $page, @tags)
|
||||
{
|
||||
my $attrs = {
|
||||
$search_opts{TaggedMediaView_media}->%*,
|
||||
|
||||
# Use "0 + ?" as otherwise a bare "?" interprets the value as
|
||||
# TEXT, breaking the functionality.
|
||||
having => \["COUNT(media_id) = 0 + ?", scalar @tags],
|
||||
group_by => "media_id",
|
||||
};
|
||||
|
||||
my $paged_media = $self->schema->resultset("TaggedMediaView")
|
||||
->search({tag_display => [@tags]}, $attrs)->page($page);
|
||||
my @media = map +{
|
||||
id => $_->media_id,
|
||||
storage_id => $_->media_storage_id,
|
||||
filename => $_->media_filename,
|
||||
content_type => $_->media_content_type,
|
||||
upload_datetime => $_->media_upload_datetime,
|
||||
}, $paged_media->all;
|
||||
my $paged_media_ids = $self->media_model->with_all_tags(@tags)
|
||||
->page($page);
|
||||
my @media_ids = map {$_->{media_id}} $paged_media_ids->fetchall->@*;
|
||||
|
||||
return $self->render(json => {
|
||||
media => [@media],
|
||||
pager => {$self->pager($paged_media)},
|
||||
media => $self->media_model->get(@media_ids)->page($page)
|
||||
->fetchall,
|
||||
# Use the original pager, as the one for the media is already
|
||||
# capped at the size of a single page.
|
||||
pager => $paged_media_ids->pager,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -72,10 +46,18 @@ sub list ($self)
|
|||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Too many tags."},
|
||||
status => 400,
|
||||
) if @tags > Pooru::API::V0::Model::Tags::ROWS;
|
||||
|
||||
my @tag_ids;
|
||||
@tag_ids = map {$_->{id}} $self->tags_model
|
||||
->get(display => [@tags])->fetchall->@* if @tags > 0;
|
||||
|
||||
return @tags == 0 ?
|
||||
$self->_list_no_tags($page) :
|
||||
$self->_list_with_tags($page, @tags);
|
||||
|
||||
$self->_list_with_tags($page, @tag_ids);
|
||||
}
|
||||
|
||||
sub show ($self)
|
||||
|
@ -88,85 +70,45 @@ sub show ($self)
|
|||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
my $media_id = $self->stash("media_id");
|
||||
my $media = $self->schema->resultset("Media")
|
||||
->single({id => $media_id});
|
||||
|
||||
my $media = $self->media_model->get($self->stash("media_id"))->single;
|
||||
return $self->render(
|
||||
json => {error => "Media not found"},
|
||||
status => 404,
|
||||
) if !defined($media);
|
||||
|
||||
my $paged_tags = $self->schema->resultset("TaggedMediaView")
|
||||
->search({media_id => $media_id},
|
||||
$search_opts{TaggedMediaView_tags})->page($page);
|
||||
my @tags = map +{
|
||||
id => $_->tag_id,
|
||||
name => $_->tag_name,
|
||||
kind_id => $_->tag_kind_id,
|
||||
display => $_->tag_display,
|
||||
count => $_->tag_count,
|
||||
}, $paged_tags->all;
|
||||
my $paged_tags = $self->tags_model
|
||||
->ranked_for_media($media->{id})->page($page);
|
||||
|
||||
return $self->render(json => {
|
||||
id => $media->id,
|
||||
storage_id => $media->storage_id,
|
||||
filename => $media->filename,
|
||||
content_type => $media->content_type,
|
||||
upload_datetime => $media->upload_datetime,
|
||||
tags => [@tags],
|
||||
pager => {$self->pager($paged_tags)},
|
||||
$media->%*,
|
||||
tags => $paged_tags->fetchall,
|
||||
pager => $paged_tags->pager,
|
||||
});
|
||||
}
|
||||
|
||||
# Executes
|
||||
# SELECT *, COUNT(tag_id) AS similarity_score FROM tagged_media_view
|
||||
# WHERE tag_id IN (
|
||||
# SELECT tag_id FROM tagged_media_view WHERE media_id = m
|
||||
# ) AND media_id != m
|
||||
# GROUP BY media_id ORDER BY similarity_score
|
||||
sub similar ($self)
|
||||
{
|
||||
my $media_id = $self->stash("media_id");
|
||||
my $media = $self->schema->resultset("Media")
|
||||
->single({id => $media_id});
|
||||
|
||||
my $media = $self->media_model->get($self->stash("media_id"))->single;
|
||||
return $self->render(
|
||||
json => {error => "Media not found"},
|
||||
status => 404,
|
||||
) if !defined($media);
|
||||
|
||||
my %attrs = (
|
||||
select => ["tag_id"],
|
||||
rows => 100,
|
||||
);
|
||||
my $tags = $self->schema->resultset("MediaTag")
|
||||
->search({media_id => $media_id}, \%attrs)->as_query;
|
||||
my @similar_media = $self->media_model
|
||||
->similar($media->{id})->fetchall->@*;
|
||||
my %media = map +(
|
||||
$_->{id} => $_
|
||||
), $self->media_model
|
||||
->get(map {$_->{media_id}} @similar_media)->fetchall->@*;
|
||||
|
||||
my %search = (
|
||||
media_id => {"!=", $media_id},
|
||||
tag_id => {"-in", $tags},
|
||||
);
|
||||
%attrs = (
|
||||
"+select" => [{count => "tag_id", -as => "similarity_score"}],
|
||||
"+as" => ["similarity_score"],
|
||||
group_by => "media_id",
|
||||
order_by => {-desc => "similarity_score"},
|
||||
rows => 6,
|
||||
);
|
||||
my @media = map +{
|
||||
id => $_->media_id,
|
||||
storage_id => $_->media_storage_id,
|
||||
filename => $_->media_filename,
|
||||
content_type => $_->media_content_type,
|
||||
upload_datetime => $_->media_upload_datetime,
|
||||
similarity_score => $_->get_column("similarity_score"),
|
||||
}, $self->schema->resultset("TaggedMediaView")
|
||||
->search(\%search, \%attrs)->all;
|
||||
foreach my $sm (@similar_media) {
|
||||
$sm = {
|
||||
score => $sm->{score},
|
||||
$media{$sm->{media_id}}->%*,
|
||||
}
|
||||
}
|
||||
|
||||
return $self->render(json => {
|
||||
media => [@media],
|
||||
});
|
||||
return $self->render(json => {media => [@similar_media]});
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -3,27 +3,27 @@ use Mojo::Base "Mojolicious::Controller", -signatures;
|
|||
|
||||
sub media ($self)
|
||||
{
|
||||
my $media = $self->schema->resultset("Media")->random->single;
|
||||
my $media_id = $self->media_model->random_id;
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Media not found."},
|
||||
status => 404,
|
||||
) if !defined($media);
|
||||
) if !defined($media_id);
|
||||
|
||||
return $self->redirect_to("show_media", media_id => $media->id);
|
||||
return $self->redirect_to("show_media", media_id => $media_id);
|
||||
}
|
||||
|
||||
sub tag ($self)
|
||||
{
|
||||
my $tag = $self->schema->resultset("Tag")->random->single;
|
||||
my $tag_id = $self->tags_model->random_id;
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Tag not found."},
|
||||
status => 404,
|
||||
) if !defined($tag);
|
||||
) if !defined($tag_id);
|
||||
|
||||
return $self->redirect_to(
|
||||
$self->url_for("show_tag")->query(id => $tag->id));
|
||||
$self->url_for("show_tag")->query(id => $tag_id));
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -13,19 +13,11 @@ sub list ($self)
|
|||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
my $paged_tags = $self->schema->resultset("TagCountView")
|
||||
->search(undef, $search_opts{TagCountView})->page($page);
|
||||
my @tags = map +{
|
||||
id => $_->id,
|
||||
name => $_->name,
|
||||
kind_id => $_->kind_id,
|
||||
display => $_->display,
|
||||
count => $_->count,
|
||||
}, $paged_tags->all;
|
||||
my $paged_tags = $self->tags_model->ranked->page($page);
|
||||
|
||||
return $self->render(json => {
|
||||
tags => [@tags],
|
||||
pager => {$self->pager($paged_tags)},
|
||||
tags => $paged_tags->fetchall,
|
||||
pager => $paged_tags->pager,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -41,7 +33,7 @@ sub show ($self)
|
|||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
my %search = ($v->topic => $tag_id_or_name);
|
||||
my %search = ($v->topic => [$tag_id_or_name]);
|
||||
|
||||
my $kind_id = $v->optional("kind_id")->param;
|
||||
return $self->render(
|
||||
|
@ -50,13 +42,9 @@ sub show ($self)
|
|||
) if $v->has_error;
|
||||
$search{kind_id} = $kind_id if defined($kind_id);
|
||||
|
||||
my @tags = map +{
|
||||
id => $_->id,
|
||||
name => $_->name,
|
||||
kind_id => $_->kind_id,
|
||||
display => $_->display,
|
||||
count => $_->count,
|
||||
}, $self->schema->resultset("TagCountView")->search(\%search)->all;
|
||||
my @tag_ids = map {$_->{id}} $self->tags_model
|
||||
->get(%search)->fetchall->@*;
|
||||
my @tags = $self->tags_model->ranked_for_tags(@tag_ids)->fetchall->@*;
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Tag not found."},
|
||||
|
@ -83,19 +71,7 @@ sub search ($self)
|
|||
|
||||
$q =~ s/[%\\_]/\\$&/g;
|
||||
$q .= '%';
|
||||
my %attrs = (
|
||||
$search_opts{TagCountView}->%*,
|
||||
rows => 10,
|
||||
);
|
||||
|
||||
my @tags = map +{
|
||||
id => $_->id,
|
||||
name => $_->name,
|
||||
kind_id => $_->kind_id,
|
||||
display => $_->display,
|
||||
count => $_->count,
|
||||
}, $self->schema->resultset("TagCountView")
|
||||
->search({name => \["LIKE ? ESCAPE '\\'", $q]}, \%attrs);
|
||||
my @tags = $self->tags_model->search($q)->fetchall->@*;
|
||||
|
||||
return $self->render(json => {tags => [@tags]});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
use DBD::SQLite::Constants ":dbd_sqlite_string_mode";
|
||||
|
||||
{
|
||||
dsn => "dbi:SQLite:db/pooru.db",
|
||||
dbi_connect_args => {
|
||||
# Highly recommended to keep this.
|
||||
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT,
|
||||
},
|
||||
|
||||
secrets => [
|
||||
# Generate with "openssl rand -hex 32".
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue