wordnet日本語版(sqlite)を使って類語Web APIを作った
はじめに
何を作ったのか?
最近、文章を単語に分割して、単語ごとに類語を表示するWebアプリを作ったのですが、文章を単語に分割する時にwordnet日本語版を使って関連語を返すWeb APIを作りました。
作ったWebアプリはこれです。
この記事を書いた人
@takasqr アプリケーション開発が大好きなエンジニア。Vue、Swift、Electrom などでアプリを作って公開している。AWS や Firebase などのクラウドサービスも好き。
wordnetってなに?
WordNet(ワードネット)は英語の概念辞書(意味辞書)である。WordNetでは英単語がsynsetと呼ばれる同義語のグループに分類され、簡単な定義や、他の同義語のグループとの関係が記述されている。 WordNetの目的は直感的に使うことのできる辞書とシソーラスが組み合わされた成果物を作ること、および自動的文書解析や人工知能のアプリケーションの実現を支援することにある。WordNetのデータベースやソフトウェアはBSDライセンスによって公開され、自由にダウンロードして用いることができる。データベースはオンラインで参照することもできる。 WordNet | Wikipedia
今回はWordNet日本語版を使って、Web APIを作りました。
デプロイ先はFirebase Functionsです。
sqliteファイルをWeb API化する
WordNetの日本語版のSQLiteのファイルは200Mあります。 このファイルをFirebase functionsにデプロイしてfuncitons上でsqliteファイルにアクセスしてjsonとして返してます。
- そもそも200Mのファイルをfirebase functionsにデプロイできるのか?
- 処理速度(パフォーマンス)は問題ないか?
等々の懸念はありましたが、やってみると案外問題なかったです。
コード
Web API(Firebase Functions)
Firebase Functionsのコード。Web APIの部分。
const functions = require('firebase-functions');
const sqlite3 = require("sqlite3");
const db = new sqlite3.Database("./data/wnjpn.db");
exports.wnpa = functions.region('asia-northeast1').runWith({ timeoutSeconds: 540, memory: "1GB" }).https.onRequest(async (req, res) => {
let returnValue = await relatedWord(req.query.lemma, req.query.lang).then(async (data) => {
return data
})
res.status(200).header('Access-Control-Allow-Origin', '*').send(returnValue);
});
function relatedWord(lemma, lang) {
return new Promise(resolve => {
let data = []
let sql = ''
sql = sql + ' select'
sql = sql + ' t002.wordid as wordid_original,'
sql = sql + ' t001.lang,'
sql = sql + ' t001.lemma as lemma_original,'
sql = sql + ' t002.synset,'
sql = sql + ' t003.wordid as wordid_network,'
sql = sql + ' t004.lemma as lemma_network'
sql = sql + ' from'
sql = sql + ' word t001'
sql = sql + ' inner join sense t002'
sql = sql + ' on'
sql = sql + ' t001.wordid = t002.wordid'
sql = sql + ' inner join sense t003'
sql = sql + ' on'
sql = sql + ' t002.synset = t003.synset'
sql = sql + ' inner join word t004'
sql = sql + ' on'
sql = sql + ' t003.wordid = t004.wordid'
sql = sql + ' where'
sql = sql + ' (t001.lemma = $lemma)'
sql = sql + ' and'
sql = sql + ' (t001.lang = $lang)'
db.each(sql, { $lemma: lemma, $lang: lang }, (err, row) => {
if (err) {
functions.logger.error("error:", err);
}
console.log(row)
data.push({
wordid_original: row.wordid_original,
lang: row.lang,
lemma_original: row.lemma_original,
synset: row.synset,
wordid_network: row.wordid_network,
lemma_network: row.lemma_network
})
}, (err, count) => {
console.log(err)
if (err) {
functions.logger.error("error:", err);
}
resolve(data)
});
})
}
async/await
を使うためにrelatedWord
と言う関数に切り出しています。
呼び出し側(React)
react側のコードです。 axiosを使って呼び出します。
const input = '博物館'
Axios
.get('https://asia-northeast1-xxxx-xxxxx.cloudfunctions.net/wnpa?lemma=' + input + '&lang=jpn')
.then((res) => {
console.log(res.data)
})
.catch(error => console.log(error))
vueでもaxiosを使えるので同じように書けるはずです。
戻り値
Firebase functionsから値が返ってくるので、取り出します。
react側の、
console.log(res.data)
の部分です。
[
{
"wordid_original": 211236,
"lang": "jpn",
"lemma_original": "博物館",
"synset": "03800563-n",
"wordid_network": 46258,
"lemma_network": "museum"
},
{
"wordid_original": 211236,
"lang": "jpn",
"lemma_original": "博物館",
"synset": "03800563-n",
"wordid_network": 166702,
"lemma_network": "ミュージアム"
},
{
"wordid_original": 211236,
"lang": "jpn",
"lemma_original": "博物館",
"synset": "03800563-n",
"wordid_network": 211236,
"lemma_network": "博物館"
},
{
"wordid_original": 211236,
"lang": "jpn",
"lemma_original": "博物館",
"synset": "03800563-n",
"wordid_network": 214478,
"lemma_network": "美術館"
},
{
"wordid_original": 211236,
"lang": "jpn",
"lemma_original": "博物館",
"synset": "03800563-n",
"wordid_network": 244859,
"lemma_network": "総合博物館"
}
]