IndexedDB で全文検索
そういう用途のものじゃないよ、というのは重々承知ですが……
全件で 8000 エントリくらいしかないので、このデモンストレーションだとスピード的なメリットは小さいかもね、、、
検索したいオブジェクトがこんなんだったとして
code:ts
const item = {
id: 123,
text: "こんにちはこんにちは。...",
};
単語ごとのインデックスを作っておく(当然versionchangeトランザクション中にやる必要がある)
code:ts
objectStore.createIndex('textWords', { multiEntry: true });
add する際に単語分割をかける。Chrome では Intl.Segmenter による分かち書きが行える(精度はかなりカスだが)ので、雑にこれを使う
真面目にやりたければ kuromoji みたいなやつを使うか、そもそもサーバサイドで高性能な辞書で分かち書きしたものを用意しておくのが良いだろう
Ngramでもいいかも?
code:ts
const segmenter = new Intl.Segmenter('ja', { granularity: 'word' });
objectStore.add({
...item,
});
ここまででデータ保存の仕組みはできた。つぎに、キーワードで検索する。まずキーワードも分かち書きする
code:ts
const query = 'りんご レシピ まずい おすすめ';
で、各単語について検索をかける。まだ全部知る必要はなく、主キーのみ得られればOK
code:ts
// 適切なトランザクションからobjectStoreが得られているとして:
const index = objectStore.index('textWords');
const result = Promise.all(queryWords.map(word => {
return new Promise(resolve => {
const req = index.getAllKeys(word);
req.addEventListener('success', () => {
resolve(req.result);
});
});
}));
各ワードごとに得られた結果(=そのワードを含むエントリのキーのリスト)の共通部分を求める。
キーのリストはキー順にソートされているので(これ本当?仕様よむか…→本当でした)、共通部分は高速に求まる。
あとは得られた共通部分(入力したキーワードに含まれる単語がすべて含まれるエントリのキー)をオブジェクトストアからもらってくればOK
完全一致にしたい、みたいな場合はさらにフィルタリング?
さらなる気づき:DLsite では既に IndexedDB による全文検索が実装されている雰囲気を感じる
https://gyazo.com/e2d4ff23944b848357d4ccae9940cc83
しかし、|で区切られた文字列を相手取ってどう全文検索をかけるんだ?
このsearch_fulltextはインデックスが作られているが、このインデックスは multiEntry: true ではなかった
以下、想像
おそらく、このsearch_fulltextに対してgetAllKeysしたのち JavaScript で文字列比較をして検索しているのでは
つまり、(ここで試したように)転置インデックスを使うのではなく、単純な文字列のストレージとして IndexedDB (のインデックス)が使われているのではないか