api/v0: add a new model implementation
Use DBI exclusively to have a fine control of the produced queries. dbic requires a lot of symbols permutation to produce the desired ones.
This commit is contained in:
parent
b052526f4e
commit
0635e37e13
4 changed files with 318 additions and 0 deletions
84
lib/Pooru/API/V0/Model/Media.pm
Normal file
84
lib/Pooru/API/V0/Model/Media.pm
Normal file
|
@ -0,0 +1,84 @@
|
|||
package Pooru::API::V0::Model::Media;
|
||||
use v5.40;
|
||||
|
||||
use Pooru::API::V0::Model::PagedResults;
|
||||
|
||||
my $slice = {};
|
||||
|
||||
sub where_in ($k, @v) { "$k IN (" . join(",", split(//, "?" x @v)) . ")" }
|
||||
|
||||
|
||||
sub new ($class, $dbh) { bless {_dbh => $dbh}, $class }
|
||||
|
||||
sub count ($self)
|
||||
{
|
||||
return $self->{_dbh}->selectrow_arrayref(q{
|
||||
SELECT COUNT(*) FROM media
|
||||
})->[0];
|
||||
}
|
||||
|
||||
sub random_id ($self)
|
||||
{
|
||||
my $row = $self->{_dbh}->selectrow_arrayref(q{
|
||||
SELECT id FROM media ORDER BY random() LIMIT 1
|
||||
});
|
||||
|
||||
return defined($row) ? $row->[0] : undef;
|
||||
}
|
||||
|
||||
sub get ($self, @ids)
|
||||
{
|
||||
my $where_clause = "";
|
||||
$where_clause = "WHERE " . where_in("id", @ids) if @ids > 0;
|
||||
|
||||
my $sth = $self->{_dbh}->prepare(qq{
|
||||
SELECT * FROM media $where_clause ORDER BY id DESC
|
||||
});
|
||||
|
||||
return Pooru::API::V0::Model::PagedResults->new($sth, $self->ROWS,
|
||||
$slice, @ids);
|
||||
}
|
||||
|
||||
sub with_all_tags ($self, @tag_ids)
|
||||
{
|
||||
my $where_clause = where_in("tag_id", @tag_ids);
|
||||
|
||||
# "0 + ?" forces a numeric cast. Otherwise, it's interpreted as TEXT.
|
||||
my $stmt = qq{
|
||||
SELECT
|
||||
media_id
|
||||
FROM media_tag
|
||||
WHERE $where_clause
|
||||
GROUP BY media_id
|
||||
HAVING COUNT(media_id) = 0 + ?
|
||||
ORDER BY media_id DESC
|
||||
};
|
||||
my $sth = $self->{_dbh}->prepare($stmt);
|
||||
|
||||
return Pooru::API::V0::Model::PagedResults->new($sth, $self->ROWS,
|
||||
$slice, (@tag_ids, scalar @tag_ids));
|
||||
}
|
||||
|
||||
sub similar ($self, $id)
|
||||
{
|
||||
my $sth = $self->{_dbh}->prepare(qq{
|
||||
SELECT
|
||||
media_id,
|
||||
COUNT(tag_id) AS score
|
||||
FROM media_tag
|
||||
WHERE tag_id IN (
|
||||
SELECT
|
||||
tag_id
|
||||
FROM media_tag
|
||||
WHERE media_id = ?
|
||||
) AND media_id != ?
|
||||
GROUP BY media_id
|
||||
ORDER BY score DESC, media_id DESC
|
||||
LIMIT ?
|
||||
});
|
||||
|
||||
return Pooru::API::V0::Model::Results->new($sth, $slice,
|
||||
($id, $id, $self->SIMILAR_ROWS));
|
||||
}
|
||||
|
||||
1;
|
59
lib/Pooru/API/V0/Model/PagedResults.pm
Normal file
59
lib/Pooru/API/V0/Model/PagedResults.pm
Normal file
|
@ -0,0 +1,59 @@
|
|||
package Pooru::API::V0::Model::PagedResults;
|
||||
use v5.40;
|
||||
use parent "Pooru::API::V0::Model::Results";
|
||||
|
||||
|
||||
sub new ($class, $sth, $pgsz, $slice, @bind_values)
|
||||
{
|
||||
my $self = $class->SUPER::new($sth, $slice, (@bind_values, $pgsz, 0));
|
||||
|
||||
$self->{_current} = 1;
|
||||
$self->{_pgsz} = $pgsz;
|
||||
$self->{_offset} = @bind_values + 1;
|
||||
|
||||
my $dbh = $sth->{Database};
|
||||
$self->{_rows} = $dbh->selectrow_arrayref(qq{
|
||||
SELECT COUNT(*) FROM ($sth->{Statement})
|
||||
}, undef, @bind_values)->[0];
|
||||
$self->{_sth} = $dbh->prepare(qq{$sth->{Statement} LIMIT ? OFFSET ?});
|
||||
|
||||
{
|
||||
use integer;
|
||||
$self->{_npages} = $self->{_rows} / $self->{_pgsz} + 1;
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub page_size ($self) { $self->{_pgsz} }
|
||||
sub rows ($self) { $self->{_rows} }
|
||||
|
||||
sub page ($self, $n)
|
||||
{
|
||||
$n = $n < 1 ? 1 : $n > $self->{_npages} ? $self->{_npages} : $n;
|
||||
$self->{_current} = $n;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub pager ($self)
|
||||
{
|
||||
return {
|
||||
first_page => 1,
|
||||
previous_page => $self->{_current} > 1 ?
|
||||
$self->{_current} - 1 : undef,
|
||||
current_page => $self->{_current},
|
||||
next_page => $self->{_current} < $self->{_npages} ?
|
||||
$self->{_current} + 1 : undef,
|
||||
last_page => $self->{_npages},
|
||||
};
|
||||
}
|
||||
|
||||
sub fetchall ($self, $slice = $self->{_slice}) {
|
||||
$self->{_bind_values}->[$self->{_offset}] =
|
||||
$self->{_pgsz} * ($self->{_current} - 1);
|
||||
|
||||
return $self->SUPER::fetchall($slice);
|
||||
}
|
||||
|
||||
1;
|
30
lib/Pooru/API/V0/Model/Results.pm
Normal file
30
lib/Pooru/API/V0/Model/Results.pm
Normal file
|
@ -0,0 +1,30 @@
|
|||
package Pooru::API::V0::Model::Results;
|
||||
use v5.40;
|
||||
|
||||
|
||||
sub new ($class, $sth, $slice, @bind_values)
|
||||
{
|
||||
my $self = {
|
||||
_sth => $sth,
|
||||
_slice => $slice,
|
||||
_bind_values => [@bind_values],
|
||||
};
|
||||
|
||||
return bless $self, $class;
|
||||
}
|
||||
|
||||
sub fetchall ($self, $slice = $self->{_slice})
|
||||
{
|
||||
$self->{_sth}->execute($self->{_bind_values}->@*);
|
||||
|
||||
return $self->{_sth}->fetchall_arrayref($slice);
|
||||
}
|
||||
|
||||
sub single ($self, $slice = $self->{_slice})
|
||||
{
|
||||
my $rows = $self->fetchall($slice);
|
||||
|
||||
return $rows->@* == 1 ? $rows->[0] : undef;
|
||||
}
|
||||
|
||||
1;
|
145
lib/Pooru/API/V0/Model/Tags.pm
Normal file
145
lib/Pooru/API/V0/Model/Tags.pm
Normal file
|
@ -0,0 +1,145 @@
|
|||
package Pooru::API::V0::Model::Tags;
|
||||
use v5.40;
|
||||
|
||||
use Pooru::API::V0::Model::PagedResults;
|
||||
|
||||
my $slice = {};
|
||||
|
||||
sub where_in ($k, @v) { "$k IN (" . join(",", split(//, "?" x @v)) . ")" }
|
||||
|
||||
|
||||
sub new ($class, $dbh) { bless {_dbh => $dbh}, $class }
|
||||
|
||||
sub count ($self)
|
||||
{
|
||||
return $self->{_dbh}->selectrow_arrayref(q{
|
||||
SELECT COUNT(*) FROM tag
|
||||
})->[0];
|
||||
}
|
||||
|
||||
sub random_id ($self)
|
||||
{
|
||||
my $row = $self->{_dbh}->selectrow_arrayref(q{
|
||||
SELECT id FROM tag ORDER BY random() LIMIT 1
|
||||
});
|
||||
|
||||
return defined($row) ? $row->[0] : undef;
|
||||
}
|
||||
|
||||
sub get ($self, %search)
|
||||
{
|
||||
my $key = exists($search{id}) ?
|
||||
"id" : exists($search{display}) ?
|
||||
"display" : "name";
|
||||
my $where_clause = where_in($key, $search{$key}->@*);
|
||||
my @bind_values = $search{$key}->@*;
|
||||
|
||||
if (exists($search{kind_id})) {
|
||||
$where_clause .= " AND kind_id = ?";
|
||||
push(@bind_values, $search{kind_id});
|
||||
}
|
||||
|
||||
my $sth = $self->{_dbh}->prepare(qq{
|
||||
SELECT *
|
||||
FROM tag
|
||||
WHERE $where_clause
|
||||
LIMIT ?
|
||||
});
|
||||
|
||||
return Pooru::API::V0::Model::Results->new($sth, $slice,
|
||||
(@bind_values, $self->ROWS));
|
||||
}
|
||||
|
||||
sub _ranked ($self, $stmt, @bind_values)
|
||||
{
|
||||
my $sth = $self->{_dbh}->prepare($stmt);
|
||||
|
||||
return Pooru::API::V0::Model::PagedResults->new($sth, $self->ROWS,
|
||||
$slice, @bind_values);
|
||||
}
|
||||
|
||||
sub ranked ($self) {
|
||||
my $stmt = q{
|
||||
SELECT
|
||||
tag.id,
|
||||
tag.name,
|
||||
tag.kind_id,
|
||||
tag.display,
|
||||
COUNT(*) AS count
|
||||
FROM media_tag, tag
|
||||
WHERE media_tag.tag_id = tag.id
|
||||
GROUP BY tag.id
|
||||
ORDER BY count DESC, kind_id DESC, id ASC
|
||||
};
|
||||
|
||||
return $self->_ranked($stmt);
|
||||
}
|
||||
|
||||
sub ranked_for_tags ($self, @ids)
|
||||
{
|
||||
my $where_clause = where_in("tag.id", @ids);
|
||||
my $stmt = qq{
|
||||
SELECT
|
||||
tag.id,
|
||||
tag.name,
|
||||
tag.kind_id,
|
||||
tag.display,
|
||||
COUNT(*) AS count
|
||||
FROM media_tag, tag
|
||||
WHERE media_tag.tag_id = tag.id AND $where_clause
|
||||
GROUP BY tag.id
|
||||
ORDER BY count DESC, kind_id DESC, id ASC
|
||||
};
|
||||
|
||||
return $self->_ranked($stmt, @ids);
|
||||
}
|
||||
|
||||
sub ranked_for_media ($self, @ids)
|
||||
{
|
||||
my $where_clause = where_in("media_tag.media_id", @ids);
|
||||
my $stmt = qq{
|
||||
SELECT
|
||||
tag_count.id,
|
||||
tag_count.name,
|
||||
tag_count.kind_id,
|
||||
tag_count.display,
|
||||
tag_count.count
|
||||
FROM
|
||||
media_tag,
|
||||
(SELECT
|
||||
tag.id,
|
||||
tag.name,
|
||||
tag.kind_id,
|
||||
tag.display,
|
||||
COUNT(*) AS count
|
||||
FROM media_tag, tag
|
||||
WHERE media_tag.tag_id = tag.id
|
||||
GROUP BY tag.id) tag_count
|
||||
WHERE media_tag.tag_id = tag_count.id AND $where_clause
|
||||
ORDER BY count DESC, kind_id DESC, id ASC
|
||||
};
|
||||
|
||||
return $self->_ranked($stmt, @ids);
|
||||
}
|
||||
|
||||
sub search ($self, $q)
|
||||
{
|
||||
my $sth = $self->{_dbh}->prepare(qq{
|
||||
SELECT
|
||||
tag.id,
|
||||
tag.name,
|
||||
tag.kind_id,
|
||||
tag.display,
|
||||
COUNT(*) AS count
|
||||
FROM media_tag, tag
|
||||
WHERE media_tag.tag_id = tag.id AND tag.name LIKE ? ESCAPE '\\'
|
||||
GROUP BY tag.id
|
||||
ORDER BY count DESC, tag.kind_id DESC, tag.id ASC
|
||||
LIMIT ?
|
||||
});
|
||||
|
||||
return Pooru::API::V0::Model::Results->new($sth, $slice,
|
||||
($q, $self->SEARCH_ROWS));
|
||||
}
|
||||
|
||||
1;
|
Loading…
Add table
Add a link
Reference in a new issue