Skip to content

Commit

Permalink
listTagnamesByPrefix, the ajax tagname autocompleter, now uses the
Browse files Browse the repository at this point in the history
tagname_cache table to find suggestions.  This should be an order
of magnitude faster on average and probably several orders faster
for the worst-case.
  • Loading branch information
jamiemccarthy committed Mar 19, 2008
1 parent 02f77d2 commit b7c9ec1
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 13 deletions.
1 change: 1 addition & 0 deletions plugins/Tags/PLUGIN
Expand Up @@ -6,6 +6,7 @@ mysql_dump=mysql_dump.sql
mysql_schema=mysql_schema.sql
requiresplugin=Ajax
task=tagbox.pl
task=tags_tagnamecache.pl
task=tags_udc.pl
task=tags_updateclouts.pl
template=templates/data;tags;default
Expand Down
79 changes: 67 additions & 12 deletions plugins/Tags/Tags.pm
Expand Up @@ -603,6 +603,9 @@ sub getTagnameidClid {
}

# Is it descriptive?
# XXX this should be optimized by retrieving the list of _all_
# descriptive tagnames in memcached or the local closure and
# doing a lookup on that.
if ($types->{describe} && !$clid) {
$tn_data = $self->getTagnameDataFromId($tagnameid);
$clid = $types->{describe} if $tn_data->{descriptive};
Expand Down Expand Up @@ -1854,25 +1857,65 @@ sub tagnameorder {
$a2 cmp $b2 || $a1 cmp $b1;
}

{ # closure
my $tagname_cache_lastcheck = 1;
sub listTagnamesByPrefix {
my($self, $prefix_str, $options) = @_;
my $constants = getCurrentStatic();
my $reader = getObject('Slash::DB', { db_type => 'reader' });
my $like_str = $self->sqlQuote("$prefix_str%");
my $minc = $self->sqlQuote($options->{minc} || $constants->{tags_prefixlist_minc} || 4);
my $mins = $self->sqlQuote($options->{mins} || $constants->{tags_prefixlist_mins} || 3);
my $num = $options->{num} || $constants->{tags_prefixlist_num};
$num = 10 if !$num || $num !~ /^(\d+)$/ || $num < 1;
my $ret_hr;

my $mcd = undef;
$mcd = $self->getMCD() unless $options;
my $mcdkey = "$self->{_mcd_keyprefix}:tag_prefx:";
if ($mcd) {
my $ret_str = $mcd->get("$mcdkey$prefix_str");
return $ret_str if $ret_str;
$ret_hr = $mcd->get("$mcdkey$prefix_str");
return $ret_hr if $ret_hr;
}

# If the tagname_cache table has been filled, use it.
# Otherwise, perform an expensive query directly.
# The logic is that $tagname_cache_lastcheck stays a
# large positive number (a timestamp) until we determine
# that the table _does_ have rows, at which point that
# number drops to 0. Once its value hits 0, it is never
# checked again.
if ($tagname_cache_lastcheck > 0 && $tagname_cache_lastcheck < time()-3600) {
my $rows = $reader->sqlCount('tagname_cache');
$tagname_cache_lastcheck = $rows ? 0 : time;
}
my $use_cache_table = $tagname_cache_lastcheck ? 0 : 1;
if ($use_cache_table) {
$ret_hr = $self->listTagnamesByPrefix_cache($prefix_str, $options);
} else {
$ret_hr = $self->listTagnamesByPrefix_direct($prefix_str, $options);
}

if ($mcd) {
# The expiration we use is much longer than the tags_cache_expire
# var since the cache data changes only once a day.
$mcd->set("$mcdkey$prefix_str", $ret_hr, 3600);
}

# XXX boost SUM if tagname is descriptive
return $ret_hr;
}
}

# This is a quick-and-dirty (and not very accurate) estimate which
# is only performed for a site which has not built its tagname_cache
# table yet. Hopefully most sites will use this the first day the
# Tags plugin is installed and then never again.

sub listTagnamesByPrefix_direct {
my($self, $prefix_str, $options) = @_;
my $constants = getCurrentStatic();
my $like_str = $self->sqlQuote("$prefix_str%");
my $minc = $self->sqlQuote($options->{minc} || $constants->{tags_prefixlist_minc} || 4);
my $mins = $self->sqlQuote($options->{mins} || $constants->{tags_prefixlist_mins} || 3);
my $num = $options->{num} || $constants->{tags_prefixlist_num};
$num = 10 if !$num || $num !~ /^(\d+)$/ || $num < 1;

my $reader = getObject('Slash::DB', { db_type => 'reader' });
my $ar = $reader->sqlSelectAllHashrefArray(
'tagname,
COUNT(DISTINCT tags.uid) AS c,
Expand All @@ -1891,10 +1934,22 @@ sub listTagnamesByPrefix {
for my $hr (@$ar) {
$ret_hr->{ $hr->{tagname} } = $hr->{sc};
}
if ($mcd) {
my $mcdexp = $constants->{tags_cache_expire} || 180;
$mcd->set("$mcdkey$prefix_str", $ret_hr, $mcdexp)
}
return $ret_hr;
}

sub listTagnamesByPrefix_cache {
my($self, $prefix_str, $options) = @_;
my $constants = getCurrentStatic();
my $like_str = $self->sqlQuote("$prefix_str%");
my $num = $options->{num} || $constants->{tags_prefixlist_num};
$num = 10 if !$num || $num !~ /^(\d+)$/ || $num < 1;

my $reader = getObject('Slash::DB', { db_type => 'reader' });
my $ret_hr = $reader->sqlSelectAllKeyValue(
'tagname, weight',
'tagname_cache',
"tagname LIKE $like_str",
"ORDER BY weight DESC LIMIT $num");
return $ret_hr;
}

Expand Down
14 changes: 13 additions & 1 deletion plugins/Tags/mysql_schema.sql
Expand Up @@ -33,7 +33,19 @@ CREATE TABLE tagnames (
PRIMARY KEY tagnameid (tagnameid),
UNIQUE tagname (tagname)
) TYPE=InnoDB;


# tagname_cache is not normalized because it's intended to be used
# for quick lookups.

DROP TABLE IF EXISTS tagname_cache;
CREATE TABLE tagname_cache (
tagnameid int UNSIGNED NOT NULL,
tagname VARCHAR(64) NOT NULL,
weight FLOAT UNSIGNED DEFAULT 0.0 NOT NULL,
PRIMARY KEY tagnameid (tagnameid),
UNIQUE tagname (tagname),
) TYPE=InnoDB;

DROP TABLE IF EXISTS tagname_params;
CREATE TABLE tagname_params (
tagnameid int UNSIGNED NOT NULL,
Expand Down
10 changes: 10 additions & 0 deletions sql/mysql/upgrades
Expand Up @@ -5165,3 +5165,13 @@ UPDATE vars SET value = 'T_2_5_0_197' WHERE name = 'cvs_tag_currentcode';

# for plugins/Ajax
UPDATE ajax_ops set reskey_name = 'ajax_user_static', reskey_type='createuse' WHERE op='admin_signoff';

# for plugins/Tags
CREATE TABLE tagname_cache (
tagnameid int UNSIGNED NOT NULL,
tagname VARCHAR(64) NOT NULL,
weight FLOAT UNSIGNED DEFAULT 0.0 NOT NULL,
PRIMARY KEY tagnameid (tagnameid),
UNIQUE tagname (tagname),
) TYPE=InnoDB;

0 comments on commit b7c9ec1

Please sign in to comment.