NextJSのサイトにサジェスト機能を実装中で個人開発のサイトなので自前でサジェスト機能を実装してみることにしました。
なお、この記事に掲載したコード自体はバニラJSです。
(本番用ではないしこっちの方が使い回しやすい。別のAPIを使うことになる可能性もあるので今はTSも不要かなと。)
ひらなが→漢字への変換スクリプト
Google CGI API for Japanese Inputという無料の変換APIを使いました。
module.exports = async(SENTENCE) => {
const axios = require(`axios`);
const BASE_URL = `http://www.google.com/transliterate`;
const options = {
params: {
langpair: 'ja-Hira|ja',
text: SENTENCE
},
method: "GET",
url: BASE_URL,
headers: {
'Content-Type': `application/json`,
}
};
try {
const res = await axios(options);
return res.data[0][1];
} catch (e) {
if (e.response) {
const {response: {data, status, headers}} = e;
console.log(data, status, headers);
} else if (e.request) {
console.log(e.request);
} else {
console.log('Error', e.message);
}
}
}
一例としてはえび🍤をテキストに設定すると、
params: {
langpair: 'ja-Hira|ja',
text: 'えび'
},
以下の多次元配列が返ってきます。
[ [ 'えび', [ 'エビ', '海老', 'えび', '蝦', '蛯' ] ] ]
[ 'エビ', '海老', 'えび', '蝦', '蛯' ] の中にGoogleのサジェスト結果が漢字つきで返ってきてるので漢字のキーワードだけ以下のように正規表現で抜き取って辞書用キーワードとして使用します。
const kanjiRegex = /([\u{3005}\u{3007}\u{303b}\u{3400}-\u{9FFF}\u{F900}-\u{FAFF}\u{20000}-\u{2FFFF}][\u{E0100}-\u{E01EF}\u{FE00}-\u{FE02}]?)/mu;
const kanjiArr = await HIRA_TO_KANJI_API(keyword); // [ 'エビ', '海老', 'えび', '蝦', '蛯' ]のように配列を返す
const kanjis = kanjiArr.filter(kanji => kanjiRegex.test(kanji)); // [ '海老', '蝦', '蛯' ] が返ってくる
不憫な点としてはこのAPI、配列データを渡してあげればまとめて変換してくれるということはなく一単語ずつAPIを呼ばないと行けません。
そのため時間がかかります。また、空のデータなどを渡したりするとエラーを吐くのでプリミティブな値(String型)で渡してあげましょう。
ちなみにmap関数で非同期処理を返してそれをPromise.allで並列的にresolveしようとしたら途中でエラーが返ってきたので直列の非同期処理に書き直しました。
漢字→ひらがなへの変換スクリプト
次いでひらがなへ変換するAPIにGooラボのひらがな化APIを使いました。これも無料です。
APIキーを取得する必要がありますがサイトへ行けばすぐに発行できました。
module.exports = async(SENTENCE) => {
require('dotenv').config({path: '.env.development.local'});
const axios = require(`axios`);
const APIKEY = process.env.GOO_API_KEY;
const BASE_URL = `https://labs.goo.ne.jp/api/hiragana`;
const OUTPU_TYPE = `hiragana`;
const options = {
method: 'post',
url: BASE_URL,
headers: {'Content-Type': `application/json`},
data: {
app_id: APIKEY,
sentence: SENTENCE,
output_type: OUTPU_TYPE
}
};
try {
const res = await axios(options);
return res.data;
} catch (e) {
if (e.response) {
const {response: {data, status, headers}} = e;
console.log(data, status, headers);
} else if (e.request) {
console.log(e.request);
} else {
console.log('Error', e.message);
}
}
}
このAPIの良いところは漢字にノイズが入っていても処理されます。
data: {
app_id: APIKEY,
sentence: 'あああ漢字あああ',
output_type: OUTPU_TYPE
}
と設定すると
'あああ かんじあああ'
が返ってきます。
そのため小手先のテクニックですがこれを利用して配列自体をJSON.stringifyで一旦文字化して翻訳後に配列に戻すというやり方でAPIを叩く回数を減らしました。
このAPIも配列を渡したりしてまとめて漢字→ひらがな変換はできなかったので。
どのような辞書を作っているのか
自分が上の2つのAPIでサジェスト機能の辞書データを作成しているのは単語しかないデータを扱っているからです。
不足している漢字やひらがな単語データを追加してあげることで単語のみ検索に対応させようとしています。
//あくまでソースデータのイメージ
[
{
Id: 01,
Name:'りんご 果汁 青森' // 単語を分解してキーワードとする。不足してる漢字やひらがなはAPIを叩いて追加する
},
{
Id: 02,
Name:'ミカン ジュース'
},
...
]
これが接続詞の入っているデータであれば、形態素解析(N-gramとやら)が必要そうだったのでこのやり方は通用しなかったかと思います。
とりあえず上のAPI2つを使ってキーワードの辞書データを作ったのでNextJSにこれから実装してきます。〜完〜