これまでページの下の方にLinkWithin を使って関連ページを表示していましたが、 表示がイマイチなのと、外部のスクリプトを呼ぶ負担も気になるので 自作板にしました。
JekyllでのRelated Posts
Jekyll自体にRelated Postsを作る機能は入っていて _config.ymlに
lsi: true
と書くかjekyll --lsi ...
とオプションで渡す事で
site.related_posts
という値が使える様になります。
ただし、これは至るところで言われてますがものすごい遅いです。
17記事で6秒位、26記事で1分位。n^2
な感じで増えていくと思うので
400記事とかになったら全然終わりません。
そこでgsl(GNU Specific Library)を入れる事で 中のアルゴリズムをより賢いものを使って改善出来るとのこと 1。
MacだとHomebrewで入れられますが、普通にbrew install gsl
で入れらるのは
現在version 1.6で、このRelated postsとは互換性が無く、
1.4以前のものでないと行けません。
1.4の物がversions: https://github.com/Homebrew/homebrew-versions
に入ってるので
$ # brew rm gsl # if you have installed normal gsl
$ brew install homebrew/versions/gsl114
こんな感じで導入。 Rubyで使うため
Gemfileに
gem 'gsl'
と加えてbundle install
でインストール。
この時点でJekyllの中身がgslを使うようになってくれます。
これをやると26記事位なら一瞬で終わりました。 450記事程で3分位。まあ許せる範囲。
ちなみにこの作業は各ページをレンダリングする直前で行われます。
一番最初のページをレンダリングする時に、最初に関連付けの計算を全部行うのでそこで時間がかかります。
その後はページをレンダリングするごとにsite.related_posts
に関連ページを詰める作業が行われます(こちらは一瞬)。
この関連ページを作るのに使われてるLSI(A Latent Semantic Indexer)と言うツールは jekyll/classifier-reborn の中に入ってるものみたいですが、 Jekyllではページの内容全体で関連性を探ってる様です。
それが逆に良くないのか、ちょっと見てみたところ余り関係がないものもちらほら入ってたりしました。
改良版プラグイン
上に書いたようにやたら遅いので早くからなんとかしようとしてる人も多くて
jekyll speedup
とかで検索するとこの関連付けに関するものが結構出てきたりします。
(そしてそんなの使ってないから別にどうでも良い、と思ってしまう。。。)
gslで速くなるとは言え、それでも3分はちょっと、という人も多いかもしれません。
後、上にも書いたように、時間がかかる割にはそれ程制度は良くありません。 (多分、アルゴリズムが悪いのではなくJekyllの中で渡しているページの中身の範囲が悪いのかと。)
ということで出てきたプラグインがこれ。
このプラグインは上のRelated Postの機能を上書きします。
ただし、lsi
をtrue
とかにしなくても常に走るようになります。(止め方が良くわからない。。。)
こちらは各ポストのTagを見て、タグが重複した頻度によって関連性の順位を付けます。
あるポストに対して、そのポストが持ってるタグと同じタグを持ってるポストを集めてきて いくつ同じタグを持ってるかで順位を決めます。
この際、多くのポストが持ってるタグの重みは軽くして、余り他のポストが持ってないタグが被ったら 重みを増して関連性を付けます。
単純な作業ですがこれだと自分でタグを意識して付ければ 意識的に関連ポストが操作できます。
ただし、上のプラグインは現在のJekyllでは動きません。
なので今使うとしたらこちらのフォーク版:
取り敢えず今も動きます。
これを入れて実際にやってみると1秒もかからずに終わりました、素晴らしい。
関連性も自分で付けたタグが同じものから選ばれてくるので ちょっと偏る感じもありますがまあ関連があるものだけがきちんと選ばれています。
もうちょっと自分なりに改良する
ですが、ちょっと色々と変更したい点もあるので 上のフォーク版を更にフォークして改良しました。
まず、プラグイン自体を各ページのレンダリングのフックみたいなものではなく、
Generatorとして登録して、各ページにpage.related_posts
として
値を詰めることにしました。
一番の理由はこの方法だと_config.ymlの設定次第で無効にして走らせない様にすることが出来るからです。 (というか上のプラグインの無効化の方法が結局分かりませんでした。。。まあ最悪ファイル消せばいいんですけど。)
後はsource/_includes/post/related_posts.htmlとか source/_includes/custom/asides/related_posts.htmlとか を用意してるのでそれらを適当に記事下やサイドバーに入れてあげて、 リポジトリにある_config.ymlのように
# Related post
related_posts: 20
related_posts_show: 5
related_posts_text: "You might also like..."
# jekyll-var
jekyll_var:
include:
- related_posts_show
とでも設定して上げればOK。 JavaScriptに設定を渡したい場合は jekyll-var-to-js を使います。
設定としてはrelated_posts
が各ページ与える関連ページの数ですが、
これに加えてrelated_posts_show
という値を入れておくと、
上の設定なら関連性の強い上位20記事を用意しておいて、そのうち5記事を毎回ランダムに表示、みたいな事をしてくれます
2。
サイドバーのランダムリストのアップデート でやったことと同じ様な事をしています。
Octopressのgenerateを劇的にスピードアップする
を使うと、サイドバーに書くページごとの物が使えないので
この場合は
source/_includes/post/related_posts.htmlを_layouts/post.htmlの
中の</article>
の下辺りに
{% unless site.related_posts == false %}
{% include post/related_posts.html %}
{% endunless %}
と入れておけば記事下に関連リストが載せられます。
ちなみに1記事しかない場合はタイトルも表示しない様になってます。 (のでgenerate_only みたいな事をすると表示されないので注意してください。)
まとめ
これとランダムリストが微妙に被るのでどっちかだけでいいような気もしてきたな、とか。
-
jekyll-var
でrelated_posts_show
を渡さない場合はデフォルトで5。ただしランダムにするためにはrelated_posts_show: 5
の設定は必要。(この場合は5でなくても0以外を設定してればOK。) ↩