Use a very naive approach: define similarity as the number of shared tags between two media pieces. It can be implemented in SQL and produces decent results.
172 lines
4.3 KiB
Perl
172 lines
4.3 KiB
Perl
package Pooru::API::V0::Controller::Media;
|
|
use Mojo::Base "Mojolicious::Controller", -signatures;
|
|
|
|
use List::Util qw(uniq);
|
|
|
|
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;
|
|
|
|
return $self->render(json => {
|
|
media => [@media],
|
|
pager => {$self->pager($paged_media)},
|
|
});
|
|
}
|
|
|
|
# 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;
|
|
|
|
return $self->render(json => {
|
|
media => [@media],
|
|
pager => {$self->pager($paged_media)},
|
|
});
|
|
}
|
|
|
|
sub list ($self)
|
|
{
|
|
my $v = $self->validation;
|
|
|
|
my $page = $v->optional("page")->num(1, undef)->param // 1;
|
|
return $self->render(
|
|
json => {error => "Invalid page number."},
|
|
status => 400,
|
|
) if $v->has_error;
|
|
|
|
my @tags = uniq split(" ", $v->optional("tags")->param // "");
|
|
return $self->render(
|
|
json => {error => "Invalid tags."},
|
|
status => 400,
|
|
) if $v->has_error;
|
|
|
|
return @tags == 0 ?
|
|
$self->_list_no_tags($page) :
|
|
$self->_list_with_tags($page, @tags);
|
|
|
|
}
|
|
|
|
sub show ($self)
|
|
{
|
|
my $v = $self->validation;
|
|
|
|
my $page = $v->optional("page")->num(1, undef)->param // 1;
|
|
return $self->render(
|
|
json => {error => "Invalid page number."},
|
|
status => 400,
|
|
) if $v->has_error;
|
|
|
|
my $media_id = $self->stash("media_id");
|
|
my $media = $self->schema->resultset("Media")
|
|
->single({id => $media_id});
|
|
|
|
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;
|
|
|
|
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)},
|
|
});
|
|
}
|
|
|
|
# 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});
|
|
|
|
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 %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;
|
|
|
|
return $self->render(json => {
|
|
media => [@media],
|
|
});
|
|
}
|
|
|
|
1;
|