新しい本が出るということで、サイトの方も色々とテコ入れしているのですが、Jamstack構成のサイトで悩ましいことになるのが検索機能と関連記事の表示ではないでしょうか。
エビスコムのサイトでは、著者NOTEのページに検索機能を組み込んでいます。外部のサービスを利用しようかとも考えましたが、そのうち関連記事も組み込みたいということで、各記事の形態素解析を行い、キーワードの抽出を行う形で検索機能を組み込みました。
ところが、Gatsby v4でParallel Query Runningが搭載された結果、Gatsbyの処理の中にこれ以上重い処理を組み込むのはまずいという流れに。
そこで、形態素解析の一連の処理を、Gatsbyのライフサイクルから追い出してしまうとともに、関連記事の表示も組み込んでみました。
処理そのものは非常にシンプルです。
BM25Vectorizer
に渡してベクトル化similarity
(コサイン類似度)による比較で類似記事の抽出文章の評価に関してtf-idf
しか知らなかったもので、JavaScriptで使えるtf-idf
のライブラリーを探したものの、tf-idf
を計算してくれるだけでベクトル化などは自前で用意しなければならなかったもので苦労しました。
しかし、Okapi BM25
にたどりついて、そちらを探したところ、winkjsでBM25
によるベクトル化、コサイン類似度まで完結できるので、非常にシンプルな構成になりました(tokens().out()
な部分は、kuromojinからの出力に置き換えればなんとかなります)。
形態素解析: kuromojin
https://github.com/azu/kuromojin
文章の評価: BM25Vectorizer/similarity
https://winkjs.org/
記事の鮮度なども加味してバランスを取りつつ、こんな感じのデータが取得できています(想像していたものとは違うものの、なかなか納得できる結果でした)。
gatsby-v4-react-helmet [
{ slug: 'gatsby-v4-wordpress-trouble', similarity: 0.175817 },
{ slug: 'gatsbyjs-gutenberg-css', similarity: 0.097883 },
{ slug: 'microcms-imgix-gatsby', similarity: 0.089381 },
{ slug: 'site-and-gatsby', similarity: 0.085722384 },
{ slug: 'file-system-route-api', similarity: 0.08334787199999999 },
]
こうして出来上がったデータですが、Gatsbyで扱うことが目的です。JSONファイルとして出力して、gatsby-transformer-jsonで読み込むのがシンプルですが、JSONファイルの扱いでトラブルになりそうなので、今回はMongoDBにデータベースを用意することにしました。
Gatsbyからは、gatsby-source-mongodbで簡単に扱うことができます。
あとは、predev
やprebuild
でデータベースを更新して、ビルドの際には必要なデータをMongoDBから取得するだけです。
ひとまず、これで目的は達成です。しかし、WordPressとMongoDBからのデータをslug
をもとに探しながら処理しなければならないのは、ちょっと面倒です。
そこで、gatsby-node.js
に以下のように追加して、スキーマーをカスタマイズします。
exports.createSchemaCustomization = ({ actions, schema }) => {
const { createTypes } = actions
createTypes(`
type WpNote implements Node {
keyword: mongodbEbisucomNotekeyword @link(from: "slug" by: "slug")
}
`)
}
これで、WordPress側のWpNote
というノードにkeyword
というフィールドを追加し、そこにMongoDBから読み込んでいるノードを繋いでくれます。繋ぐ際には、両方のノードのslug
が一致しているものを繋ぐように指定しています(この場合、 by: "slug"
だけでも機能します)。
公式のCustomizing the GraphQL Schema: foreign-key-fieldsのあたりが参考になります。
フィールド単位で持ってくるのであれば、リゾルバーを利用するのが正解だと思いますが、今後の拡張も考えて、ざっくりと繋いでしまいます。
GraphiQLで確認してみると、こんな感じに繋がれているのが確認できます。
あとは、今までのクエリをちょっと書き換えれば、用意したデータも簡単に扱うことができます。
こんな感じでゆる~く何とかなるのも、Gatsbyの魅力だと思います。