summaryrefslogtreecommitdiff
path: root/lib/Multi/API.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Multi/API.pm')
-rw-r--r--lib/Multi/API.pm95
1 files changed, 72 insertions, 23 deletions
diff --git a/lib/Multi/API.pm b/lib/Multi/API.pm
index 4c160f70..f360d231 100644
--- a/lib/Multi/API.pm
+++ b/lib/Multi/API.pm
@@ -48,6 +48,7 @@ sub spawn {
logfile => "$VNDB::M{log_dir}/api.log",
conn_per_ip => 5,
sess_per_user => 3,
+ results => 10,
tcp_keepalive => [ 120, 60, 3 ], # time, intvl, probes
throttle_cmd => [ 6, 100 ], # interval between each command, allowed burst
throttle_sql => [ 60, 1 ], # sql time multiplier, allowed burst (in sql time)
@@ -177,6 +178,22 @@ sub filtertosql {
}
+# generates the LIMIT/OFFSET/ORDER BY part of the queries
+sub sqllast { # $get, default sort field, hashref with sort fields and SQL variant
+ my($get, $def, $sort) = @_;
+
+ my $o = $get->{opt}{reverse} ? 'DESC' : 'ASC';
+ $get->{opt}{sort} = $def if !defined $get->{opt}{sort};
+ my $s = $sort->{$get->{opt}{sort}};
+ return cerr $get->{c}, badarg => 'Invalid sort field', field => 'sort' if !$s;
+ my $q = 'ORDER BY '.sprintf($s, $o);
+
+ my $res = $poe_kernel->get_active_session()->get_heap()->{results};
+ $q .= sprintf ' LIMIT %d OFFSET %d', $res+1, $res*($get->{opt}{page}-1);
+ return $q;
+}
+
+
## POE handlers
sub _start {
@@ -311,10 +328,28 @@ sub client_input {
$c->{throttle}[0] += $_[HEAP]{throttle_cmd}[0];
# handle get command
+ if($cmd eq 'get') {
+ my $opt = $arg->[3];
+ return cerr $c, badarg => 'Invalid argument for the "page" option', field => 'page'
+ if defined($opt->{page}) && (ref($opt->{page}) || $opt->{page} !~ /^\d+$/ || $opt->{page} < 1);
+ return cerr $c, badarg => '"reverse" option must be boolean', field => 'reverse'
+ if defined($opt->{reverse}) && !JSON::XS::is_bool($opt->{reverse});
+ return cerr $c, badarg => '"sort" option must be a string', field => 'sort'
+ if defined($opt->{sort}) && ref($opt->{sort});
+ $opt->{page} = $opt->{page}||1;
+ $opt->{reverse} = defined($opt->{reverse}) && $opt->{reverse};
+ my %obj = (
+ c => $c,
+ info => $arg->[1],
+ filters => $arg->[2],
+ opt => $opt,
+ );
+ return cerr $c, 'gettype', "Unknown get type: '$arg->[0]'" if $arg->[0] !~ /^(?:vn|release)$/;
+ return $_[KERNEL]->yield("get_$arg->[0]", \%obj);
+ }
+
+ # unknown command
return cerr $c, 'parse', "Unkown command '$cmd'" if $cmd ne 'get';
- my $type = shift @$arg;
- return cerr $c, 'gettype', "Unknown get type: '$type'" if $type !~ /^(?:vn|release)$/;
- $_[KERNEL]->yield("get_$type", $c, @$arg);
}
@@ -365,30 +400,32 @@ sub login_res { # num, res, [ c, arg ]
sub get_results {
- my $get = $_[ARG0]; # hashref, must contain: type, c, queries, time, list, info, filters,
+ my $get = $_[ARG0]; # hashref, must contain: type, c, queries, time, list, info, filters, more, opt
# update sql throttle
$get->{c}{throttle}[1] += $get->{time}*$_[HEAP]{throttle_sql}[0];
# send and log
my $num = @{$get->{list}};
- $get->{c}{wheel}->put([ results => { num => $num, items => $get->{list} }]);
- $_[KERNEL]->yield(log => $get->{c}, "T:%4.0fms Q:%d R:%02d get %s %s %s",
- $get->{time}*1000, $get->{queries}, $num, $get->{type}, join(',', @{$get->{info}}), encode_filters $get->{filters});
+ $get->{c}{wheel}->put([ results => { num => $num, more => $get->{more} ? TRUE : FALSE, items => $get->{list} }]);
+ $_[KERNEL]->yield(log => $get->{c}, "T:%4.0fms Q:%d R:%02d get %s %s %s {%s %s, page %d}",
+ $get->{time}*1000, $get->{queries}, $num, $get->{type}, join(',', @{$get->{info}}), encode_filters($get->{filters}),
+ $get->{opt}{sort}, $get->{opt}{reverse}?'desc':'asc', $get->{opt}{page});
}
sub get_vn {
- my($c, $info, $filters) = @_[ARG0..$#_];
+ my $get = $_[ARG0];
- return cerr $c, getinfo => "Unkown info flag '$_'", flag => $_ for (grep !/^(basic|details|anime|relations)$/, @$info);
+ return cerr $get->{c}, getinfo => "Unkown info flag '$_'", flag => $_
+ for (grep !/^(basic|details|anime|relations)$/, @{$get->{info}});
my $select = 'v.id, v.latest';
- $select .= ', vr.title, vr.original, v.c_released, v.c_languages, v.c_platforms' if grep /basic/, @$info;
- $select .= ', vr.alias AS aliases, vr.length, vr.desc AS description, vr.l_wp, vr.l_encubed, vr.l_renai' if grep /details/, @$info;
+ $select .= ', vr.title, vr.original, v.c_released, v.c_languages, v.c_platforms' if grep /basic/, @{$get->{info}};
+ $select .= ', vr.alias AS aliases, vr.length, vr.desc AS description, vr.l_wp, vr.l_encubed, vr.l_renai' if grep /details/, @{$get->{info}};
my @placeholders;
- my $where = encode_filters $filters, \&filtertosql, $c, \@placeholders, [
+ my $where = encode_filters $get->{filters}, \&filtertosql, $get->{c}, \@placeholders, [
[ 'id',
[ 'int' => 'v.id :op: :value:', {qw|= = != <> > > < < <= <= >= >=|} ],
[ inta => 'v.id :op:(:value:)', {'=' => 'IN', '!= ' => 'NOT IN'}, join => ',' ],
@@ -419,11 +456,16 @@ sub get_vn {
))', {'~', 1}, process => \'like' ],
],
];
- return if !$where;
+ my $last = sqllast $get, 'id', {
+ id => 'v.id %s',
+ title => 'vr.title %s',
+ released => 'v.c_released %s',
+ };
+ return if !$last || !$where;
$_[KERNEL]->post(pg => query =>
- qq|SELECT $select FROM vn v JOIN vn_rev vr ON v.latest = vr.id WHERE NOT v.hidden AND $where LIMIT 10|,
- \@placeholders, 'get_vn_res', { c => $c, info => $info, filters => $filters });
+ qq|SELECT $select FROM vn v JOIN vn_rev vr ON v.latest = vr.id WHERE NOT v.hidden AND $where $last|,
+ \@placeholders, 'get_vn_res', $get);
}
@@ -455,6 +497,7 @@ sub get_vn_res {
};
}
}
+ $get->{more} = pop(@$res)&&1 if @$res > $_[HEAP]{results};
$get->{list} = $res;
}
@@ -506,16 +549,16 @@ sub get_vn_res {
sub get_release {
- my($c, $info, $filters) = @_[ARG0..$#_];
+ my $get = $_[ARG0];
- return cerr $c, getinfo => "Unkown info flag '$_'", flag => $_ for (grep !/^(basic|details|vn|producers)$/, @$info);
+ return cerr $get->{c}, getinfo => "Unkown info flag '$_'", flag => $_ for (grep !/^(basic|details|vn|producers)$/, @{$get->{info}});
my $select = 'r.id, r.latest';
- $select .= ', rr.title, rr.original, rr.released, rr.type, rr.patch, rr.freeware, rr.doujin' if grep /basic/, @$info;
- $select .= ', rr.website, rr.notes, rr.minage, rr.gtin, rr.catalog' if grep /details/, @$info;
+ $select .= ', rr.title, rr.original, rr.released, rr.type, rr.patch, rr.freeware, rr.doujin' if grep /basic/, @{$get->{info}};
+ $select .= ', rr.website, rr.notes, rr.minage, rr.gtin, rr.catalog' if grep /details/, @{$get->{info}};
my @placeholders;
- my $where = encode_filters $filters, \&filtertosql, $c, \@placeholders, [
+ my $where = encode_filters $get->{filters}, \&filtertosql, $get->{c}, \@placeholders, [
[ 'id',
[ 'int' => 'r.id :op: :value:', {qw|= = != <> > > >= >= < < <= <=|} ],
[ inta => 'r.id :op:(:value:)', {'=' => 'IN', '!=' => 'NOT IN'}, join => ',' ],
@@ -548,11 +591,16 @@ sub get_release {
[ stra => 'rr.id :op:(SELECT rl.rid FROM releases_lang rl WHERE rl.lang IN(:value:))', {'=' => 'IN', '!=' => 'NOT IN'}, join => ',', process => \'lang' ],
],
];
- return if !$where;
+ my $last = sqllast $get, 'id', {
+ id => 'r.id %s',
+ title => 'rr.title %s',
+ released => 'rr.released %s',
+ };
+ return if !$where || !$last;
$_[KERNEL]->post(pg => query =>
- qq|SELECT $select FROM releases r JOIN releases_rev rr ON rr.id = r.latest WHERE $where AND NOT hidden LIMIT 10|,
- \@placeholders, 'get_release_res', { c => $c, info => $info, filters => $filters });
+ qq|SELECT $select FROM releases r JOIN releases_rev rr ON rr.id = r.latest WHERE $where AND NOT hidden $last|,
+ \@placeholders, 'get_release_res', $get);
}
@@ -581,6 +629,7 @@ sub get_release_res {
$_->{catalog} ||= undef;
}
}
+ $get->{more} = pop(@$res)&&1 if @$res > $_[HEAP]{results};
$get->{list} = $res;
}
elsif($get->{type} eq 'languages') {