Apache lucene で名寄せを実現する その三 luceneのインデックスを構築する
プロジェクトができたので、既存システムの商品マスタを投入してluceneのインデックスを構築します。
データ投入
テーブル作成
evolutionを利用してテーブルを作成します。作成するテーブルは既存システムの商品マスタです。
# --- !Ups drop table if exists older_item; create table older_item( id int auto_increment, item_code varchar(8) not null, item_name varchar(255) not null, data_classify varchar(8) not null, data_classify_name varchar(255) not null, item_classify varchar(8) , item_classify_name varchar(255), item_type varchar(8), item_type_name varchar(255), store_code char(4) not null, store_name varchar(32) not null, daily_report varchar(8), daily_report_name varchar(255), primary key (id) ) ; # --- !Downs drop table if exists older_item;
model作成
Play FrameworkでDBにアクセスするために モデルのクラスを作成します。
package models; import javax.persistence.Entity; import javax.persistence.Table; import play.db.jpa.Model; /** * 既存システムの商品 * */ @Entity @Table(name = "older_item") public class OlderItem extends Model { /** 商品コード */ public String item_code; /** 商品名 */ public String item_name; /** データ区分 */ public String data_classify; /** データ区分名 */ public String data_classify_name; /** 区分コード */ public String item_classify; /** 区分名 */ public String item_classify_name; /** 分類コード */ public String item_type; /** 分類名 */ public String item_type_name; /** 店舗コード */ public String store_code; /** 店舗名 */ public String store_name; /** 日報コード */ public String daily_report; /** 日報コード名 */ public String daily_report_name; }
lucene のインデックスを構築する
conf/dependencies.yml にluceneを定義する。
現時点での最新版 5.2.1 を利用します。
# Application dependencies require: - play - org.apache.lucene -> lucene-core 5.2.1 - org.apache.lucene -> lucene-queryparser 5.2.1 - org.apache.lucene -> lucene-analyzers-common 5.2.1
サービスクラスを作成する
controllerとJAPのモデルの間にサービスクラスを入れて処理を使いまわせるようにします。
package service.lucene; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import models.OlderItem; import org.apache.commons.lang.StringUtils; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.cjk.CJKAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.LongField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexNotFoundException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexWriterConfig.OpenMode; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import play.Play; public class Index { /** * インデックスを配置するディレクトリ */ private File dir; /** * インスタンスを生成します。 * * @return */ public static Index create() { Index index = new Index(); index.dir = index.indexDir(); return index; } /** * インデックスされているドキュメント数を取得します。 * @return * @throws IOException */ public int numDocs() throws IOException { try( IndexReader reader = DirectoryReader.open(FSDirectory.open(this.getDir().toPath())) ) { return reader.numDocs(); }catch (IndexNotFoundException e) { return 0; } } /** * インデックスを構築します。 * @throws IOException */ public void build() throws IOException { // インデックスの出力先を定義 Directory indexDir = FSDirectory.open(this.getDir().toPath()); // テキストの解析方法(アナライザー)を定義 Analyzer analyzer = new CJKAnalyzer(); // 解析方法の設定 IndexWriterConfig config = new IndexWriterConfig(analyzer); // インデックスが既に存在する場合の動作を定義する(OpenMode.CREATE の場合、新規に作成して上書きする) config.setOpenMode(OpenMode.CREATE); try (IndexWriter writer = new IndexWriter(indexDir, config)) { for(OlderItem item: OlderItem.<OlderItem>findAll()) { // Document に、インデックスに保存する各ファイルの情報を設定する Document doc = new Document(); doc.add( new LongField("id", item.id, Field.Store.YES )); doc.add( new TextField("item_name", item.item_name, Field.Store.YES )); // インデックスを書き出す writer.addDocument(doc); } } } /** * インデックスのファイルを配置するディレクトリを取得します。 * * @return */ public File getDir() { return dir; } /** * インデックスを配置するディレクトリを取得します。 * * @return インデックスを配置するディレクトリ */ private File indexDir() { final String defaultIndexDir = Play.applicationPath.getAbsolutePath() + File.separator + "lucene"; File indexDir = new File(Play.configuration.getProperty("lucene.dir", defaultIndexDir)); return indexDir; } }
conf/routesにURLを定義
# mentenance index GET /lucene/ LuceneController.index GET /lucene/status LuceneController.status POST /lucene/build LuceneController.build
luceneのインデックスを管理する処理を LuceneConrollerクラスに実装します。
package controllers; import java.io.IOException; import play.mvc.Controller; import service.lucene.Index; /** * lucene のメンテナンス * @author masahiro * */ public class LuceneController extends Controller { public static void index() { status(); } /** * Luceneの状態を表示します。 */ public static void status() { String active_navi = "reports"; Index index = Index.create(); render(active_navi, index); } /** * Luceneの状態を表示します。 * @throws IOException */ public static void build() throws IOException { Index index = Index.create(); index.build(); flash.success("インデックスを作成しました。"); status(); } }
インデックスの状態表示のviewテンプレート
#{extends 'main.html' /} #{set title:'Luceneの状態' /} #{set 'moreStyles'} #{/set} #{set 'moreScripts'} #{/set} <h1 class="page-header">Luceneの状態</h1> #{if flash.success } <div class="alert alert-success">${flash.success}</div> #{/if} <div class="row"> <div class="col-lg-4"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"> <i class="fa fa-long-arrow-right fa-fw"></i>ディレクトリ </h3> </div> <div class="panel-body"> <div id="morris-donut-chart"> ${index.dir.getAbsolutePath()}</div> </div> </div> </div> <div class="col-lg-4"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"> <i class="fa fa-long-arrow-right fa-fw"></i>登録件数 </h3> </div> <div class="panel-body"> <div id="morris-donut-chart"> ${index.numDocs()}件 </div> </div> </div> </div> </div> <div class="row"> <div class="col-lg-4"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"> <i class="fa fa-long-arrow-right fa-fw"></i>アクション </h3> </div> <div class="panel-body"> <div id="morris-donut-chart"> #{form @LuceneController.build(), method:'post', class:'form'} <input type="submit" class="btn btn-lg btn-warning" type="button" value="作成"></input> #{/form} </div> </div> </div> </div> </div>
インデックス作成を実行
http://localhost:9000/lucene/status にアクセスして、「作成」ボタンを押すとインデックスを作成します。 処理後に「登録件数」が更新されます。