質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

861閲覧

zipファイルをJavaScriptで読む際の効率的な方法(1回目にローカルに保存し2回目以降はそれを読む方法、またはそんなことをせずとも勝手にキャッシュされ効率的になっているのか知りたい)

munekun

総合スコア108

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

2クリップ

投稿2024/12/17 17:01

編集2024/12/17 17:35

前提

形態素解析を実装したいので、Kuromoji というライブラリを導入しました。

Kuromojiでかんたん形態素解析 という記事のおかげもあり、基本的な動作は確認済みです。

目的

目的はこの辞書データの効率的な参照です。

上記リンクに ディクショナリーフォルダ1式(辞書データです) とあるように、Kuromoji は zip ファイルを辞書として参照するのですが、これがなかなか大きなデータでして、効率的に(サーバー負荷を少なく)扱う方法はないかと考えている次第です。

考えていること

考えていること1

1回読んだ zip ファイルを何度も読むことは避けたいと思い、IndexedDB への保存を試みました。
しかし、Kuromoji は kuromoji.builder() なる関数によって辞書を参照するようで、その引数に指定できる値が辞書へのURLだけのようでした。

つまり、IndexedDB へ保存しても、それを kuromoji.builder() のために参照する方法がなさそう、ということです。

もしこの方法をご存じの方がいらっしゃったらご教示いただけますと幸いです。

考えていること2

上述したように IndexedDB への保存という案では実装できなそうだ、と悩んでいたのですが、そもそももしかして、zip ファイルを読んだ際にもキャッシュって効くのでしょうか?だとしたら IndexedDB への保存など不要?

実際に以下のような計測コードを試したところ、1回目と2回目で半分くらいの時間になったので、どうやら zip ファイルにもキャッシュが効くのか?と思えたのですが、その確証が得れません。

JavaScript

1(() => { 2 const DICT_PATH = "./dict"; 3 4 // 長文として https://www.aozora.gr.jp/cards/000148/files/789_14547.html から『吾輩は猫である』など 5 const story = `長文`; 6 7 const ids = []; 8 const names = []; 9 10 // 処理開始の時間を記録 11 console.time("Kuromoji Processing Time"); 12 13 // Kuromoji 14 kuromoji.builder({ dicPath: DICT_PATH }).build((err, tokenizer) => { 15 if (err) { 16 console.error("Tokenizer error:", err); 17 return; 18 } 19 20 const tokens = tokenizer.tokenize(story); // 解析データの取得 21 tokens.forEach((token) => { // 解析結果を順番に取得する 22 console.log(token); 23 }); 24 25 // 処理終了の時間を記録 26 console.timeEnd("Kuromoji Processing Time"); 27 }); 28 29})();

どうなのでしょうか?もしキャッシュが効くのなら、特に何もしなくてもできる限り効率的な実装がなされていると考えていいのでしょうか?

確認したこと

Kuromoji は CDN で下記リンクから読んでおりまして、
https://cdn.jsdelivr.net/npm/kuromoji@0.1.2/build/kuromoji.js

これを確認すると、辞書データの扱いに際しては zlib.js というライブラリ を使っているようです。

ただこのライブラリが Kuromoji の辞書データである zip ファイルを解凍し、キャッシュし、よしなにしてくれるのか?までは調査、読解するスキルがありませんでした。

以上です。なんだかいまいちまとまっていない文章になってしまいましたが、質問としては Kuromoji の辞書データを効率的に(サーバー負荷を少なく)扱う方法か、またはそもそもそんな方法を模索しなくてもキャッシュされているから何もしなくてもいいのか、という2点になります。

よろしくお願い致します。

補足

下記 JavaScript のように
・初回訪問した際は initializeTokenizer() を実行して IndexedDB に保存
・形態素解析の際は analyzeText() で解析

ということが出来るかも?と思ったのですが、初回訪問の initializeTokenizer() の時点で blob:https:/example.com/[UUID]/base.dat.gz など各種辞書データの GET ができないとコンソールにエラーが出まくってしまい、断念しました。(この方針も、そもそもブラウザが1回読んだ zip ファイルを自動でキャッシュしてくれるのであれば不要ですが。)

JavaScript

1/*---------------------------------------------------------------------------------------------------- 2 3 File: kuromoji-manager.js 4 Path: /assets/js/module/lib/kuromoji-manager.js 5 Description: Kurromoji の管理モジュール 6 7----------------------------------------------------------------------------------------------------*/ 8 9let tokenizer; 10const DB_NAME = "kuromojiCacheDB"; 11const STORE_NAME = "dictionaryStore"; 12const DICT_PATH = "./dict"; 13 14/*-------------------------------------------------- 15 解析 16--------------------------------------------------*/ 17 18// 形態素解析を実行 19function analyzeText(string) { 20 21 if (!tokenizer) { 22 const contents = "Tokenizer is not ready yet. Please wait..."; 23 console.log('analyzeText() contents', contents); 24 return; 25 } 26 27 const tokens = tokenizer.tokenize(string); 28 const contents = tokens.map(token => 29 `表層形: ${token.surface_form}, 品詞: ${token.pos}, 基本形: ${token.base_form}` 30 ).join("\n"); 31 32 console.log('analyzeText() contents', contents); 33} 34 35/*-------------------------------------------------- 36 初期化 37--------------------------------------------------*/ 38 39// IndexedDBに辞書データを保存 40async function saveDictionaryToDB(data) { 41 const db = await openIndexedDB(); 42 const transaction = db.transaction(STORE_NAME, "readwrite"); 43 const store = transaction.objectStore(STORE_NAME); 44 store.put(data, "kuromoji_dict"); 45 console.log("辞書データをIndexedDBに保存しました。"); 46} 47 48// IndexedDBから辞書データを取得 49async function getDictionaryFromDB() { 50 const db = await openIndexedDB(); 51 const transaction = db.transaction(STORE_NAME, "readonly"); 52 const store = transaction.objectStore(STORE_NAME); 53 return store.get("kuromoji_dict"); 54} 55 56// IndexedDBを開く(または作成) 57function openIndexedDB() { 58 return new Promise((resolve, reject) => { 59 const request = indexedDB.open(DB_NAME, 1); 60 request.onupgradeneeded = event => { 61 const db = event.target.result; 62 if (!db.objectStoreNames.contains(STORE_NAME)) { 63 db.createObjectStore(STORE_NAME); 64 } 65 }; 66 request.onsuccess = () => resolve(request.result); 67 request.onerror = () => reject(request.error); 68 }); 69} 70 71// トークナイザーを初期化 72async function initializeTokenizer() { 73 console.log("辞書データをロード中..."); 74 75 const cachedData = await getDictionaryFromDB(); 76 if (cachedData) { 77 console.log("IndexedDBから辞書データを読み込み中..."); 78 // 辞書データをBlob URLに変換 79 const dicBlobURL = URL.createObjectURL(new Blob([cachedData])); 80 81 console.log('initializeTokenizer() dicBlobURL: ', dicBlobURL); 82 83 tokenizer = await buildTokenizer(dicBlobURL); // build関数をPromise化して利用 84 } else { 85 console.log("CDNから辞書データを取得中..."); 86 tokenizer = await buildTokenizer(DICT_PATH); 87 // 辞書データをIndexedDBに保存する処理(省略) 88 } 89} 90 91// Kuromojiのビルダー関数をPromiseでラップする 92function buildTokenizer(dicPath) { 93 return new Promise((resolve, reject) => { 94 kuromoji.builder({ dicPath: dicPath }).build((err, tokenizer) => { 95 if (err) { 96 reject(err); 97 } else { 98 resolve(tokenizer); 99 } 100 }); 101 }); 102} 103 104/*-------------------------------------------------- 105 export 106--------------------------------------------------*/ 107 108export default { 109 analyzeText, 110 initializeTokenizer, 111}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lhankor_Mhy

2024/12/18 01:50

全く試してみていないのですが、ソースを読むと、kuromoji.builder() は TokenizerBuilder クラスで、TokenizerBuilder は受け取ったパスを DictionaryLoader クラスに渡しているだけで、DictionaryLoader が実際の辞書ファイルの読み取りをして Tokenizer クラスに辞書データを渡しているようです。 ですので、辞書データが再現できているのであれば、Tokenizer クラスに直接渡してごにょごにょできるのでは、と感じました。全然試していないのですが。
munekun

2024/12/18 05:42 編集

wyq2020さん、ありがとうございます。参考にさせて頂きます。
munekun

2024/12/18 04:48

Lhankor_Mhyさん、ありがとうございます。いつもご回答参考にさせて頂いております。 さっそく仰る方針で進めてみたいと思います。
juner

2024/12/18 06:40 編集

ブラウザの ファイルシステムである Origin Private File System に保存してもよさそうですね。 ……と思ったが、この dict はファイルパスとしてアクセスできないとダメなので 普通に サービスワーカーでごにょごにょしないとだめなやつですね。(保存先は opfs でもいいけど普通に サービスワーカーで cache api したほうがよさそう https://developer.mozilla.org/ja/docs/Web/API/File_System_API/Origin_private_file_system https://developer.mozilla.org/ja/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%A0%BC%E7%B4%8D%E3%81%99%E3%82%8B%E6%8A%80%E8%A1%93%E3%81%AF%E4%BD%95%E3%81%8B
munekun

2024/12/20 06:45

junerさん、ありがとうございます。せっかくコメントくださったのにすみません。キャッシュやブラウザの知識が大きく欠けており、実はみなさんの仰ることがほぼ分かっていない状況です。とりあえずなんかいろいろ種類がある感じですね!もう少し勉強しておきます。
guest

回答1

0

ベストアンサー

// テストサイト:https://test-8hb.pages.dev/ const dicPath = "https://cdn.jsdelivr.net/npm/kuromoji@0.1.2/dict/"; // ----(無理やりURLを使う) // https://github.com/takuyaa/kuromoji.js/blob/master/src/loader/BrowserDictionaryLoader.js // https://github.com/takuyaa/kuromoji.js/blob/master/src/loader/DictionaryLoader.js let _open = window.XMLHttpRequest.prototype.open; window.XMLHttpRequest.prototype.open = function() { // console.log(arguments); if (arguments && arguments[1]) { arguments[1] = arguments[1].replace("https:/cdn.jsdelivr.net", "https://cdn.jsdelivr.net") } return _open.apply(this, arguments); }; // ---- kuromoji.builder({ dicPath }).build(function (error, tokenizer) { let path = tokenizer.tokenize("吾輩は猫である。名前はまだ無い。"); document.body.innerHTML = JSON.stringify(path) });

イメージ説明
参考資料
ブラウザキャッシュの仕組み
キャッシュの概要
Cache-Control

投稿2024/12/18 12:13

wyq2020

総合スコア67

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

munekun

2024/12/20 06:41

ご回答ありがとうございます。dicPath のような各種ファイルのディレクトリへのリクエストであっても、各種ファイルがそれぞれブラウザキャッシュに保存されているよ、ということでしょうか・・? つまり「1回目にローカルに保存し2回目以降はそれを読む方法、またはそんなことをせずとも勝手にキャッシュされ効率的になっているのか知りたい」という質問に対しては「勝手にキャッシュされ効率的になっている」であり、だから特に何もする必要がない。ということでしょうか? それともご提示いただいたコードに、キャッシュに必要な部分とか、ポイントなどはございますか? 恐れ入りますが、もう少しご説明がほしいです・・
wyq2020

2024/12/22 01:20

不明な点や間違ったことがありましたら、遠慮なく言ってください。 質問に対する最もシンプルな解決方法は、HTTPヘッダーの`Cache-Control`を利用することだと思います。同じHTTPリクエストを一定時間内に送信すると、ブラウザはサーバーにリクエストを送る代わりに、キャッシュされたレスポンスを返します。
wyq2020

2024/12/22 01:54 編集

`kuromoji/src/loader/DictionaryLoader.js`のソースファイルによると、内部では`require("path")`を使用して`dicPath`とファイル名を結合しています。またライブラリがクラスをエクスポート(export)していないため、提案した方法の実装が難しくなることがあります。
munekun

2024/12/25 15:26

ありがとうございます。このあたりの知識が乏しく具体的に分からない点を質問できるレベルでさえありませんでした。でもいくつか仰る内容を調べていくうちにぼんやり見えてきました。ひとまずご提示いただいた方針で実装を進めてみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問