このブログ、sqlite と php で表示しているのですが、関連記事の作成がかなりページ表示時間を遅くしていました。具体的には以下のようにデータベースから読み込んだ全記事を記事同士総当たりでタグ比較していくという我ながら残念な実装でした。(アルゴリズムもタグが一つでも含まれれば関連記事としていて、あまりいいものではないですが、今回はスコープ外ということで)
この実装を改善して成果があったので記録しておきます。
private function createRelatedArticles()
{
$maxRelatedEntryCount = 5;
for ($i=$this->size-1; $i >= 0; --$i)
{
$entry0 = $this->data[$i];
if (count($entry0->tagList) == 0)
{
continue;
}
for ($j=$i - 1; $j >= 0; --$j)
{
$entry1 = $this->data[$j];
if (count($entry1->tagList) == 0)
{
continue;
}
if ($this->areRelatedEntry($entry0, $entry1))
{
if (count($entry0->relatedEntries) < $maxRelatedEntryCount)
{
array_push($entry0->relatedEntries, $entry1);
}
if (count($entry1->relatedEntries) < $maxRelatedEntryCount)
{
array_push($entry1->relatedEntries, $entry0);
}
}
}
}
}
private function areRelatedEntry($entry0, $entry1)
{
foreach ($entry0->tagList as $tag0)
{
if (empty ($tag0)||ctype_space ($tag0))
{
continue;
}
if ($this->hasTag($entry1, $tag0))
{
return true;
}
}
return false;
}
private function hasTag($targetEntry, $targetTag)
{
foreach ($targetEntry->tagList as $tag)
{
if ($tag == $targetTag)
{
return true;
}
}
return false;
}
これを以下のようにデータベースから最低限必要なものだけ引っ張ってくる実装に変えてみました。sqlite では配列型を扱えないので、タグは文字列でカンマ区切りで突っ込んでいますが、like '%hoge%' で文字列が含まれているかで判定するので代用しています。これだとひとつのタグ文字列を完全一致で検索できないので、格納するタグ文字列をカンマ区切りではなく、[hoge][fuga]みたいな感じの書式に変えないとダメそうです。それは今後の課題ということで。
public function printContentPageDirectlyFromDb($filepath, $id)
{
$db = sqlite_open($filepath, 0666, $sqlite_err);
if (!$db)
{
die('connecting DB failed: '.$sqlite_err);
}
$sql = "SELECT * FROM main_data WHERE id=".$id;
$result = sqlite_query($db, $sql, SQLITE_BOTH, $sqlite_err);
if (!$result)
{
die('query failed: '.$sqlite_err);
}
$dbDataSize = sqlite_num_rows($result);
if ($dbDataSize == 0)
{
return;
}
$tableData = sqlite_fetch_all($result, SQLITE_ASSOC);
$rowData = $tableData[0];
$content = new EntryData(
$rowData['id']
,$rowData['date']
,$this->categoryList->getCategoryByID($rowData['categoryid'])
,$rowData['title']
,$rowData['article']
,$rowData['tag']
);
$this->createRelatedArticlesDirectlyFromDb($db, $content);
print($content->getContentTag());
sqlite_close($db);
}
private function createRelatedArticlesDirectlyFromDb($db, $entry)
{
if (($entry->tagList == null) || (count($entry->tagList) == 0))
{
return;
}
$query = "SELECT * FROM main_data WHERE visibility=1 AND id != ".$entry->id." AND (";
$tagIndex = 0;
foreach ($entry->tagList as $tag)
{
if ($tagIndex > 0)
{
$query .= " OR ";
}
$query .= "tag like '%".$tag."%'";
++$tagIndex;
}
$query .= ") ORDER BY date DESC";
$result = sqlite_query($db, $query, SQLITE_BOTH, $sqlite_err);
if (!$result)
{
print('query('.$query.') failed: '.$sqlite_err);
return;
}
$dbDataSize = sqlite_num_rows($result);
$tableData = sqlite_fetch_all($result, SQLITE_ASSOC);
$maxRelatedEntryCount = 5;
if ($dbDataSize > $maxRelatedEntryCount)
{
$dbDataSize = $maxRelatedEntryCount;
}
for ($i=0; $i<$dbDataSize; ++$i)
{
$rowData = $tableData[$i];
$relatedEntry = new EntryData(
$rowData['id']
,$rowData['date']
,$this->categoryList->getCategoryByID($rowData['categoryid'])
,$rowData['title']
,$rowData['article']
,$rowData['tag']
);
array_push($entry->relatedEntries, $relatedEntry);
}
}
この変更により、処理時間が 0.44 秒から 0.014 秒になりました。元が遅すぎただけですが、結果的に 30 倍くらい速くなりました。php の処理時間のプロファイルとかやっていなかったのであまり問題意識なかったのですが、たまにはやらないとだめですね。