sqlite fts4 + sql.js によるブラウザでの全文検索 - end0tknr's kipple - web写経開発
前回の上記entryでは、sqliteのバイナリデータ(.db)を使用しましたが、 .dbでは容量が大きい為、更にzip化したものを扱うようにしてみました。
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body {font-family: "Helvetica Neue", Arial,"Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif; color: #555;} a:visited { color: #00f; } /* ヘッダ部の表示位置固定 */ th { position: sticky; top: 0; background-color:#b0e0e6; font-weight: normal;} table thead th:nth-child(1){width: 50px;} table thead th:nth-child(2){width: 65px;} table thead th:nth-child(3){width: 100px; } table thead th:nth-child(4){width: 520px;} table thead th:nth-child(5){width: 80px;} table tbody td:nth-child(1){text-align:center;} table tbody td:nth-child(2){text-align:center;} table tbody td:nth-child(3){text-align:center;} table tbody td:nth-child(5){text-align:center;} table { border-collapse: collapse; } th, td { border : 1px solid #888; padding: 1px; } #toast_msg{color: #ff4500; } #toast_msg.hide {display: none; } h1 {margin:5px 0; font-size:1.5em; font-family:"HG丸ゴシックM-PRO";} .note {font-size:small;} #find_str {width:500px;} #help_link_cotainer { color: #ff4500;} </style> </head> <body cz-shortcut-listen="true"> <div> 図面内に記載されている文字で図番を抽出 </div> <select id="zumen_type"> <option value="sekou_manual">施工マニュアル</option> </select> <input type="text" id="find_str" placeholder="検索キーワードを指定してください"> <button type="button" id="find_btn">検索</button> <div id="toast_msg" class="hide">検索結果がある状態でご利用ください</div> <table id="find_result"> <thead> <tr> <th></th> <th></th> <th>図番-付番</th> <th>図面名称</th> <th>作成日</th> </tr> </thead> <tbody> </tbody> </table> <template id="tbody_tr"> <tr> <td></td><td></td> <td></td><td></td><td></td> </tr> </template> <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.2/sql-wasm.js"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"> </script> <script src="./find_zumen2.js"></script> </body></html>
'use strict'; class FindHinban { constructor() {} download_zip = async(url)=>{ const response = await fetch(url); if (! response.ok ){ return undefined; } let jszip = new JSZip(); let res_array_buf = response.arrayBuffer(); let zip = await jszip.loadAsync( res_array_buf ); return zip; } init_window =async ()=> { const SQL = await initSqlJs({ locateFile:file=> `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.2/${file}` }); let zip = await this.download_zip("sekou_manual_full_texts.zip"); for (const filename in zip.files) { let zip_content = await zip.files[filename].async("uint8array"); this.db = new SQL.Database( zip_content ); break; } document.querySelector("#find_btn").addEventListener('click',()=>{ this.find_hinban(); }); document.querySelector("#find_str").addEventListener('keypress',(e)=>{ if (e.keyCode == 13) { // ENTER this.find_hinban(); } }); } find_hinban=()=>{ let find_str = document.querySelector("#find_str").value; find_str = this.validate_find_str(find_str); if(! find_str ){ this.toast_msg("検索キーワードを指定してください"); return; } let sql = "SELECT * FROM zumen_words WHERE words MATCH (?)"; let stmt = this.db.prepare(sql); stmt.bind([find_str]); console.log( stmt ); let tbody_elm = document.querySelector("#find_result tbody"); let tr_tmpl = document.querySelector("#tbody_tr"); tbody_elm.innerHTML = ""; let i = 0; while( stmt.step() ) { i++; let row = stmt.getAsObject(); var tr_elm = tr_tmpl.content.cloneNode(true); //console.log( i, row ); let td = tr_elm.querySelectorAll("td"); td[0].textContent = i++; td[2].textContent = row["file"]; td[3].textContent = row["name_kanji"]; td[4].textContent = row["create_day"]; tbody_elm.appendChild(tr_elm); } if(i==0){ this.toast_msg("見つかりませんでした"); return; } stmt.free(); } disp_find_result=(res_tsvs)=>{ let tbody_elm = document.querySelector("#find_result tbody"); var clone_tbody = tbody_elm.cloneNode(false); tbody_elm.parentNode.replaceChild(clone_tbody, tbody_elm); tbody_elm = document.querySelector("#find_result tbody"); let tsvs = res_tsvs.split("\n"); if ( res_tsvs.length == 0){ this.toast_msg("検索キーワードに一致するものは見つかりませんでした"); return; } let tr_tmpl = document.querySelector("#tbody_tr"); let i = 1; for(let tsv of tsvs){ let disp_cols = tsv.split("\t"); var tr_elm = tr_tmpl.content.cloneNode(true); var td = tr_elm.querySelectorAll("td"); td[0].textContent = i++; td[2].textContent = disp_cols[0]; td[3].textContent = disp_cols[1]; td[4].textContent = disp_cols[2]; td[5].textContent = disp_cols[4]; td[6].textContent = disp_cols[5].substring(2); td[7].textContent = disp_cols[6]; tbody_elm.appendChild(tr_elm); } let btn_elms = document.querySelectorAll("#find_result tbody button"); for(let btn_elm of btn_elms ){ btn_elm.addEventListener("click",(e)=>{ this.view_scan(e); }); } } view_scan=(e)=>{ let tr_elm = e.target.parentNode.parentNode; let td_elms = tr_elm.querySelectorAll("td"); let zuban = td_elms[2].textContent.trim(); let req_url = scan_base_url + zuban; location.href = req_url; } toast_msg=(msg)=>{ let msg_container = document.querySelector("#toast_msg"); msg_container.innerHTML = msg; msg_container.className = msg_container.className.replace("hide",""); setTimeout(()=>{ msg_container.className="hide";},4500); } validate_find_str=(org_find_str)=>{ org_find_str = org_find_str.trim(); if ( org_find_str.length == 0 ){ return undefined; } return org_find_str; } } let find_hinban = new FindHinban(); find_hinban.init_window();