huruyosi’s blog

プログラミングとかインフラとかのメモです。

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 にアクセスして、「作成」ボタンを押すとインデックスを作成します。 処理後に「登録件数」が更新されます。