過去ログ検索システム不具合

ドラクエ8をのんびりプレイしながら、ミディリンの更新も行っている毎日ですが
先日雑談掲示板の過去ログを1600件ほど、検索システムに掲載しました。

雑談掲示板の過去ログを検索システムに反映させるには
生成された過去ログのHTMLファイル(このHTMLは1つのHTMLに複数の記事が有)を
書き込み1件ずつを1つのHTMLに保存しなおし
namazuが入っているサーバーにFTPでファイルをあげれば、検索結果に反映されるというものです。

例えば、
kakiko300.htmという過去ログがあり、この過去ログに5000~5020の書込みがある場合
kakiko300.htm→5000.htm~5020.htm
というように
記事に分割するという処理をしています。

以前、この作業はテキストエディタと置換ツールなどを駆使して行っていました。
10個くらいの手順を踏んで作成するので、作業が複雑で手順書を作って行っていましたが
これが面倒くさい!

時間も無駄だし、手作業だと間違えることもあるので
先日Javaで下記のプログラムを作りました。

書いたプログラム↓


package log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

import comm.Fileuse;

public class log_create_mainbbs {

 public static void main(String[] args) {
  
  String inputPath = "D:\\dummy\\midilin\\log\\input\\";//取得元
  String outputPath = "D:\\dummy\\midilin\\log\\output\\";//書き出し先
  
  //ファイル一覧取得
  Fileuse fu = new Fileuse();
  ArrayList fileichiran = fu.getFileIchiran(inputPath);

  fileCreate(inputPath,outputPath,fileichiran,"0","SJIS");
  
 }
 
 
 public static void fileCreate(String inputPath,String outputPath,ArrayList
fileichiran,String outputname ,String moji_code){
  int i=0;
  int debug_count = 0;
  String debugstr = "";
  try{
   
   for(i=0;i<fileichiran.size();i++){

    //ファイル解析
    File file = new File(inputPath + "\\" +fileichiran.get(i).toString());
    FileInputStream fs = new FileInputStream(file);
    InputStreamReader ir = new InputStreamReader(fs,moji_code);
    BufferedReader br = new BufferedReader(ir);
    StringBuffer buffer = new StringBuffer();
    char[] readBuffer = new char[8192];
    int length = br.read(readBuffer, 0, 8192);
   
    while(length>=0){
     buffer.append(readBuffer,0,length);
     length = br.read(readBuffer, 0, 8192);
    }
    String out = buffer.toString();
   
    String search_start_text = "<center>[";
    String search_end_text = "] <font color";
    
    String hr_endtext = "</table><hr width=95% color=#a0e0f6 size=1>";
    
    int searchStartNo = 0;
    int searchEndNo = 0;
    
    while((out.indexOf(search_start_text))!=-1){
    
     searchStartNo = out.indexOf(search_start_text) + search_start_text.length();
     searchEndNo = out.indexOf(search_end_text);

     String indexNo = out.substring(searchStartNo,searchEndNo);//No
     
     //String outnext = out.substring(searchEndNo+1);//次の対象テキスト
     String outnext = out.substring(out.indexOf(hr_endtext)+hr_endtext.length()+1);//次の対象テキスト
     
     int searchStartNoNext = outnext.indexOf(search_start_text) + search_start_text.length();  

     String outputText =out.substring(out.indexOf(search_start_text),out.length()-outnext.length()
+ searchStartNoNext – search_start_text.length());

     outputText = headHtml() + outputText + footer();
     //ファイル書き込み開始
     File outfile = new File(outputPath + outputname + indexNo + ".html");
     outfile.createNewFile();

     FileOutputStream outfs = new FileOutputStream(outfile);
     OutputStreamWriter ow = new OutputStreamWriter(outfs,"SJIS");
     BufferedWriter bw = new BufferedWriter(ow);
     
     StringBuffer outbuffer = new StringBuffer(outputText);
     bw.write(outbuffer.toString(), 0, outbuffer.length());//書き出し
     bw.flush();
     bw.close();
     ow.close();
     
     out = outnext;
     
     debug_count++;
     debugstr = indexNo;

    }    
    
    fs.close();//ファイル読み込み1つ終了
   
   }
   System.out.print("ファイル出力正常終了\n");
  }catch (Exception e){
    e.printStackTrace();
    System.out.print("i="+ i + "\n");
    System.out.print("debug_count = " + debug_count + "\n");
    System.out.print("index=" + debugstr + "\n");
  }

 }
 

package comm;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

public class Fileuse {
 /**
  * <DL>
  * <DT><b>メソッド概要:</b><DD>
  * ファイル一覧取得<BR>
  * <BR>
  * @param pass
  * @return
  * </DD><BR>
  * </DL>
  */
 public ArrayList getFileIchiran(String pass){
  ArrayList array = new ArrayList();

  File f = new File( pass );
  String files[] = f.list();
  int N = files.length;
  int k;

  for( k=0; k<N; k++ )
  {
   //System.out.println( pass + "/" + files[k] );
   array.add(files[k]);
  }
  
  return array;
 }

}

上のプログラムを実行すると自動的にファイルを分割して、新しいログのHTMLを生成してくれます。

その後、検索ページのヒット件数の表紙を作成する必要があります。

表紙作成は2つの工程を得て作成しています。

(1)分割したHTMLをDBに登録
(2)DBに登録したデータをSQLを使って、結果をHTMLで保存

(1)のHTMLをDBに登録するという作業も、以前手作業でエクセルを使って
Insert文を列挙してDB登録していたのですが、面倒くさいので
これもJavaのプログラムを組んで、登録する仕組みを作りました。

package log;
import java.sql.*;
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import org.apache.xml.utils.Trie;

import comm.Fileuse;
import comm.Struse;
/**
* <UL>
* <LI>ProjectName :</LI>
* <LI>SystemName :</LI>
* <LI>PackageName :</LI>
* <LI>@author :<BR>
* </UL>
* <DL>
* <DT><b>クラス概要:</b></DT><DD>
* 雑談掲示板の過去ログをDBに登録するプログラム]<BR>
* </DD><BR>
* </DL>
*/
public class log_mainbbs_insert {
 private static String DBname = "●●●";//DB名
 private static String userid = "●●●";//ID
 private static String password = "●●●";//PASSWORD
 private static String sv = "●●●";//ホスト名
 private static String encode = "Shift_JIS";//文字コード
 private Connection conn;
 
 public static void main(String[] args) throws Exception {
  log_mainbbs_insert app = new log_mainbbs_insert();
  String r[] ={DBname,userid,password,sv,encode};
  app.init(r);
 }
 private void setParameter(String[] args) {
  // 引数の数チェック
  if (args == null || args.length < 3) {
   System.out.println("引数の数が足りません。");
   System.exit(1);
  }
  DBname = args[0];
  userid = args[1];
  password = args[2];
  sv = args[3];
  encode = args[4];
 }
 
 private void init(String[] args) throws Exception {
  int i=0;
  String sql = "";
  try {
   
   String inputPath = "C:\\data\\log\\mainbbs\\";//取得元
  
   //ファイル一覧取得
   Fileuse fu = new Fileuse();
   ArrayList fileichiran = fu.getFileIchiran(inputPath);
   
   // 引数よりパラメータをセットします。
   setParameter(args);

   Class.forName("com.mysql.jdbc.Driver").newInstance();
   
   //DB接続
   //conn =DriverManager.getConnection("jdbc:mysql:///" + DBname,
userid, password);
   String db_url = "jdbc:mysql://" + sv + "/" + DBname
      + "?user=" + userid
      + "&password=" + password
      + "&useUnicode=true&characterEncoding=" + encode;
   conn =DriverManager.getConnection(db_url);
   
   
   conn.setAutoCommit(false);//Autocommit解除
   
   for(i=0;i<fileichiran.size();i++){

    //ファイル解析
    File file = new File(inputPath + "\\" +fileichiran.get(i).toString());
    FileInputStream fs = new FileInputStream(file);
    InputStreamReader ir = new InputStreamReader(fs,"SJIS");
    BufferedReader br = new BufferedReader(ir);
    StringBuffer buffer = new StringBuffer();
    char[] readBuffer = new char[8192];
    int length = br.read(readBuffer, 0, 8192);
   
    while(length>=0){
     buffer.append(readBuffer,0,length);
     length = br.read(readBuffer, 0, 8192);
    }
   
   
    String out = buffer.toString();
   
    String index_search_start_text = "<center>[";
    String index_search_end_text = "] <font color";
    String txt_search_end_text = "</div>";
    
    
    int searchStartNo = 0;
    int searchEndNo = 0;
       
       
    searchStartNo = out.indexOf(index_search_start_text) + index_search_start_text.length();
    searchEndNo = out.indexOf(index_search_end_text);
    
    String indexNo = out.substring(searchStartNo,searchEndNo);//No

       
    //String outnext = out.substring(searchEndNo+1);//次の対象テキスト
    //int searchStartNoNext = outnext.indexOf(index_search_start_text) + index_search_start_text.length();
    int searchEndNo_text = out.indexOf(txt_search_end_text);
    
    String outputText =out.substring(searchStartNo-1,searchEndNo_text);
    outputText = outputText.trim();
     
    //ステートメントオブジェクトを生成
    Statement stmt = conn.createStatement();
    Statement stmt_in = conn.createStatement();
    
    sql = "select id from mainbbs where id = ‘" + indexNo + "’";
    //クエリーを実行
    ResultSet rs = stmt.executeQuery(sql);

    //検索された行数分ループする
    int rs_cnt=0;
    while(rs.next()) {
     String temp = rs.getString("id");//Maxid取得
     rs_cnt++;
    }

    //レコードが存在しなかったらinsert作業
    if(rs_cnt == 0){
     //String del_str[] = {"&nbsp;"," ","%",".","’","\\"};
     Struse stru = new Struse();
     outputText = stru.conv_text(outputText,"’","\\’");//文字列置換
     //outputText = stru.delete_text(del_str, outputText);
     sql = "INSERT INTO mainbbs(id,comment) values (" + indexNo +
",’" + outputText + "’)";
     stmt_in.executeUpdate(sql);
    }
   }

   conn.commit();
   System.out.print("正常終了しました");
  } catch (InstantiationException e) {
   rollbackAndPrint(sql, e);
  } catch (IllegalAccessException e) {
   rollbackAndPrint(sql, e);
  } catch (ClassNotFoundException e) {
   rollbackAndPrint(sql, e);
  } catch (SQLException e) {
   rollbackAndPrint(sql, e);
  } catch(Exception e){
   rollbackAndPrint(sql, e);
  }
  finally{
   conn.close();//切断
  }
  
 }
 private void rollbackAndPrint(String sql, Exception e) throws SQLException
{
  e.printStackTrace();
  System.out.print(sql);
  conn.rollback();
 }

}

上のプログラムは分割したHTMLがあるディレクトリを指定すると、
ファイルを解析し、必要なデータのみを取得してファイル名をキーとしDBに登録するプログラムです。
これでデータの登録も楽になりました。

今までフリーソフトを何本も使って作業をしていたのですが
プログラムを組むことで、上のプログラムを一回ずつ実行するだけで処理が行われるようになりました。

この後、(2)のDBに登録したデータからHTMLを作成するという作業をするのですが
これは以前PHPを使って作成していたので、PHPを実行します。

過去ログが10000件以上あるので、このHTMLを作成するだけでも2時間くらいかかるのですが
ここで1つ不具合を発見してしまいました。

それは、実際にログを検索していただくとわかるのですがnamazuの方で
「POPS」というキーワードで検索すると30件以上検索結果がでるのに
PHPで作成された表紙の方では(5)という表示になっています。

これが何故起こるのか、検証していたのですが
どうやらnamazuでの過去ログ検索システムでは大文字小文字を区別しないで検索を行い、
PHP+Mysqlで組んでいる方では大文字小文字を区別して検索結果を出しているようでした。

つまり、namazu検索システムでは「POPS」で検索すると「Pops」という小文字を含んだものも検索されますが
PHP+Mysqlの方では完全一致全部大文字のものしかされていませんでした。

使い勝手としては大文字小文字を区別しない方がよいので、
PHP+Mysqlの方を直そうと思い、ネットでいろいろ情報を探していたのですが
原因がわかりませんでした。

日記なのに専門的な話ばかりで申し訳ないのですが
ネットでわかったことはMysqlでは文字列を格納するにも2つのタイプ

・テキストとバイナリオブジェクトがあること
・CHAR ・ VARCHAR の場合は、テーブル定義の際に BINARY 属性がつけられない限り、
大文字・小文字を区別しないでソート・比較されること
(BINARY 属性があれば大文字・小文字を区別してソート・比較される)
・BLOBとTEXTの違いはTEXTとBLOB の違いは、 TEXT はケースに依存しないでソートと比較され
BLOB はケースに依存して比較されること

でした。

文字列を格納するデータの型としては

char(文字数) 固定長文字列 (最大 256 文字)
varchar(文字数) 可変長文字列 (最大 256 文字)
text ラージ文字列 (最大 65535 文字)
mediumtext ラージ文字列 (最大 1677215 文字)
largetext ラージ文字列 (最大 4294967295 文字)
blob ラージバイナリ(最大 65535 bytes)
mediumblob ラージバイナリ(最大 1677215 bytes)
largeblob ラージバイナリ(最大 4294967295 bytes)

という型があります。

掲示板の過去ログはかなりテキスト容量が多いので「mediumtext」型で定義しているのですが
mediumtext型って、大文字小文字の区別がついちゃうのでしょうか。

調べた感じでは、テキスト型の場合は大文字小文字の比較がされないので
大丈夫のような気がするのですが。

参考URL

・Mysqlの型
http://homepage2.nifty.com/sak/w_sak3/doc/sysbrd/php_s01.htm
・テキストとバイナリオブジェクトについて
http://www.rfs.jp/sitebuilder/sql/02/03.html

もう1つの可能性としてはMysqlの方でSQLを発行した後、
タグの有無で検索結果をPHPで再度カウントしなおしているので
そちらで大文字小文字を判定してしまっている可能性もあります。

まぁ、判定するには実際にSQLを流せば特定できるのでしょうが。

説明を長々と書きましたが、要約すると

「過去ログと検索結果の表紙は一致しないよん」

ということです。1行で書けることなら長々と書くなと怒られそうですが

最近DQ8レポートばかりアップしてこの遊び人め!!
と思われないために
ミディリンのことも考えているよという自己アピールをしてみました。

コメント

  1. まさのり@会社 より:

    不具合?おいらの?とちょっと心配しちゃいました(笑)
    表紙の検索数は無理に表記しなくてもいいのでは?
    それにしてもファイル数が膨大ですね(^^;
    そろそろ動かなくなるのではないかと心配だったり。

  2. パステル・ミディリン@安田まこと より:

    心配させちゃってごめんなさい。
    検索数はできればつけたいのですよね。
    普段掲示板を使っている方はどんな記事があるかわかると思うのですが
    サイトを訪れてすぐの方とか、どんな過去ログがあるか
    キーワードのイメージがつきにくいと思うのです。
    ファイル数、1万4千越えてしまいましたね。
    まだ検索した感じでは遅さは感じないところですが。

タイトルとURLをコピーしました