Showing posts with label solr. Show all posts
Showing posts with label solr. Show all posts

Feb 16, 2011

Apache Solr 1.4 Filter の作成 DigitFilter, KatakanaStemFilter

以下、POSFilter と同様にして、
DigitFilter, KatakanaStemFilterを作成します。

各 Factory クラスは、create するだけなので、非常に単純で、以下のような内容です。
public class DigitFilterFactory extends BaseTokenFilterFactory {

    public TokenStream create(TokenStream input) {
        return new DigitFilter(input);
    }
}


そして、Filter クラスは以下のような感じです。
public class DigitFilter extends TokenFilter {
    boolean preRead;
    String preTerm;
    String preType;
    int    preStart;
    int    preEnd;

    protected DigitFilter(TokenStream input) {
        super(input);
        preRead = false;
        preTerm = preType = null;
        preStart = preEnd = 0;
    }

    public Token next(Token token) throws IOException {
        if (preRead) {
            preRead = false;
            return preTerm == null ?
                    null : token.reinit(preTerm, preStart, preEnd, preType);
        }

        Token t = input.next(token);
        if (t == null)
            return null;

        char[] c;  // for termBuffer

        if (t.termLength() == 1
          && Character.isDigit((c = t.termBuffer())[0])) {
            int start = t.startOffset();
            int end   = t.endOffset();
            String type = t.type();

            StringBuilder st = new StringBuilder();
            st.append(c[0]);
            while (true) {
                t = input.next(token);
                if (t == null) {
                    preRead = true;
                    preTerm = null;
                    break;
                }
                else if (t.termLength() != 1
                  || !Character.isDigit((c = t.termBuffer())[0])) {
                    preRead  = true;
                    preTerm  = new String(c, 0, t.termLength());
                    preStart = t.startOffset();
                    preEnd   = t.endOffset();
                    preType  = t.type();
                    break;
                }
                st.append(c[0]);
                end = t.endOffset();
            }
            return token.reinit(st.toString(), start, end, type);
        }
        return t;
    }
}

これで、以下の fieldType ができました。
<fieldType name="text_ja" class="solr.TextField">
    <analyzer>
      <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ja.txt" />
      <tokenizer class="SenTokenizerFactory" />
      <filter class="POSFilterFactory" deny="pos-deny.txt" />
      <filter class="DigitFilterFactory" />
      <filter class="solr.LowerCaseFilterFactory" />
      <filter class="KatakanaStemFilterFactory" />
      <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords-ja.txt" />
    </analyzer>
  </fieldType>

これで、SysnonymFilter も追加できますね。

※ 2/18 WordJoinFilter は問題があったので削除しました。

Apache Solr 1.4 Filter の作成 POSFilter

Tokenizer を作成したので、次はFilter を作ります。

Filter も Tokenizer と同様に、Factory クラスを作成し、Filter を create します。

Factoryクラスは、「 org.apache.solr.analysis.BaseTokenFilterFactory 」を継承し、
createメソッドを実装すればOKですが、設定ファイルをロードする必要がある場合などは、
「 org.apache.solr.util.plugin.ResourceLoaderAware 」を implement して、
inform メソッドを実装します。

このあたりは、Solr 本付属のソースや、
org.apache.solr.analysis.StopFilterFactory のソースなどが参考になります。

まずは、POSFilterFactory.class と、POSFilter.class を作成します。

POSFilterFactory.class で作成する 設定ファイルから読み込む POS情報ですが、
スレッド間で同期する必要がないので、Solr 本と同様に Set で実装します。

なので、POSFilterFactory のソースの抜粋は、以下のように。
public class POSFilterFactory extends BaseTokenFilterFactory implements ResourceLoaderAware {
    private Set<string> posSet;
    public void inform(ResourceLoader loader) {
        try {
            List<string> alist = loader.getLines(denyPOSFile);
            posSet = POSFilter.makePOSSet(alist);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public TokenStream create(TokenStream input) {
        return new POSFilter(input, posSet);
    }
}

public final class POSFilter extends TokenFilter {
    private final Set<string> posSet;

    public POSFilter(TokenStream input, Set<string> posSet) {
        super(input);
        this.posSet = posSet;
    }

    public final static Set<string> makePOSSet(List<string> posList) {
        if (posList == null)
            throw new NullPointerException("posList is null");
        return new HashSet<string>(posList);
    }

    public final Token next(Token token) throws IOException {
        Token t;
        while (true) {
            t = input.next(token);
            if (t == null)
                return null;
            if (posSet == null || !posSet.contains(t.type()))
                break;
        }
        return t;
    }
}


これで、fieldType の以下の部分までができました。
<fieldType name="text_ja" class="solr.TextField">
    <analyzer>
      <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ja.txt" />
      <tokenizer class="SenTokenizerFactory" />
      <filter class="POSFilterFactory" deny="pos-deny.txt" />
    </analyzer>
  </fieldType>

Apache Solr 1.4 Tokenizerの作成

JapaneseAnalyzer は、その名のとおり Analyzer クラ なので、schema.xml で細かなフィルターの設定はできません。
そこで、Tokenizerクラスを作成し、Filter、charFilter を柔軟に設定できる fieldType を作成します。

形態素解析には、同様に Sen を使用することとします。

TokenizerFactory を作る時は、「 org.apache.solr.analysis.BaseTokenizerFactory.class 」を実装し、
Factoryメソッドで、Tokenizer を作成します。
この辺りの流れは、ドキュメントを見るか、実際の solr や lucene のソースを見るのが参考になります。

JapaneseAnalyzer は、以下の流れで解析しています。

Reader を、NormalizeReader でラップし、全角⇔半角に文字を寄せる。
設定フィルで記述した tokenizerClass のインスタンスを作成。
POSFilter - 不要な型の term を除去
DigitFIlter - Senによって分割された数字を結合
LowerCaseFilter - 大文字を小文字に変換
KatakanaStemFilter - 4文字以降のカタカナの最後の「ー」を除去
StopFilter - stopwords を削除

最終的にはこのような fieldType を作成することを目標に、まずは、Tokenizerを作成します。

JapaneseAnalyzerを使うときは、システムプロパティからsen.home の値を取得しますが、
Solr 入門の本や、org.apache.solr.core.SolrResourceLoader.class の様に、
「 JNDI -> System Property -> デフォルト 」
とチェックするのがいいと思うので、そのように実装し、
sen/home は web.xml に記述することにします。

ただし、JapaneseAnalyzer を使う場合は、必ずシステムプロパティが必要なので、
同じ設定を2箇所に記述するよりは、システムプロパティに統一するほうがよさそうです。

そんなこんなで、Factoryクラスのソースは以下のような感じになります。
public class SenTokenizerFactory extends BaseTokenizerFactory {
  
    private static final Logger log
                    = LoggerFactory.getLogger(SenTokenizerFactory.class);

    static final String PROP_SEN_HOME = "sen.home";
    static final String JNDI_SEN_HOME = "sen/home";
    static final String FS = System.getProperty("file.separator");
    static final String SEN_XML = FS + "conf" + FS + "sen.xml";

    String configFile;
    String compositRule;

    @Override
    public void init(Map args) {
        String senHome = null;

        // Try JNDI
        try {
            Context c = new InitialContext();
            senHome = (String)c.lookup("java:comp/env/" + JNDI_SEN_HOME);
            log.info("Using JNDI sen/home: " + senHome);
        } catch (NoInitialContextException e) {
            log.info("JNDI not configured for Solr (NoInitialContextEx)");
        } catch (NamingException e) {
            log.info("No sen/home in JNDI");
        } catch (RuntimeException ex) {
            log.warn("Odd RuntimeException while testing for JNDI: " 
                    + ex.getMessage());
        } 

        // Now try system property
        if (senHome == null){
            senHome = System.getProperty(PROP_SEN_HOME);
            log.info("Using System property sen.home: " + senHome);
        }

        // Set current path
        if (senHome == null) {
            senHome = ".";
            log.info("sen.home defaulted to '.' (could not find system property or JNDI)");
        }

        configFile = senHome + SEN_XML;

        log.info( "config file for SenTokenizer is " + configFile );

        readConfig();

        log.info("conpositRule is: "
                + (compositRule == null ? "NULL" : compositRule));

    }

    protected String getConfigFile() {
        return configFile;
    }

    private void readConfig() {
        List<String> compositRuleList = new ArrayList<String>();

        try {
            DocumentBuilderFactory factory
                            = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new InputSource(configFile));
            NodeList nl = doc.getFirstChild().getChildNodes();

            for (int i = 0; i < nl.getLength(); i++) {
                org.w3c.dom.Node n = nl.item(i);
                if (n.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
                    String nn = n.getNodeName();
                    String value = n.getFirstChild().getNodeValue();

                    if (nn.equals("composit")) {
                        compositRuleList.add(value);
                        log.info("add composit rule: " + value);
                    }
                }
            }

            if (compositRuleList.size() > 0) {
                compositRule = StringUtils.join(compositRuleList, "\n");
            }

        } catch (ParserConfigurationException e) {
            throw new IllegalArgumentException(e.getMessage());
        } catch (FileNotFoundException e) {
            throw new IllegalArgumentException(e.getMessage());
        } catch (SAXException e) {
            throw new IllegalArgumentException(e.getMessage());
        } catch (IOException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }

    public Tokenizer create(Reader input) {
        try {
            return new SenTokenizer(input, configFile, compositRule);
        } catch (IOException e) {
            throw new RuntimeException("cannot initialize SenTokenizer: "
                                        + e.toString());
        }
    }
}

そして、SenTokenizerクラスは以下のように。
public class SenTokenizer extends Tokenizer {

    private StreamTagger tagger       = null;
    private String       configFile   = null;
    private String       compositRule = null;

    private static final HashSet hash = new HashSet();

    public SenTokenizer(Reader input, String configFile, String compositRule)
            throws IOException {
        super(input);
        this.configFile = configFile;
        this.compositRule = compositRule;
        init(input);
    }
    
    private void init(Reader input) throws IOException {
        tagger = new StreamTagger(input, configFile);

        synchronized(hash) {
            if (compositRule != null && !compositRule.equals("")) {
                if (!hash.contains(compositRule)) {
                    CompositPostProcessor p = new CompositPostProcessor();
                    p.readRules(new BufferedReader(new StringReader(
                            compositRule)));
                    hash.add(compositRule);
                    tagger.addPostProcessor(p);
                }
            }
        }
    }

    public Token next(Token token) throws IOException {
        if (!tagger.hasNext())
            return null;

        net.java.sen.Token t = tagger.next();

        if (t == null)
            return next(token);

        return token.reinit(
                t.getBasicString(),
                correctOffset(t.start()),
                correctOffset(t.end()),
                t.getPos());
    }

    @Override
    public void reset(Reader input) throws IOException {
        super.reset(input);
        init(input);
    }
}

これで、fieldType の以下の部分までができました。
<fieldType name="text_ja" class="solr.TextField">
    <analyzer>
      <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ja.txt" />
      <tokenizer class="mydomain.SenTokenizerFactory" />
    </analyzer>
  </fieldType>


追記:
2/18 sen.xml に、composit の設定を追加した際の処理をソースに追記しました。
まだマルチスレッドでうまく動作するかはテスト中です。



参考サイト

http://d.hatena.ne.jp/bowez/20090513


参考本

Feb 3, 2011

Apache Solr 1.4 Sen の辞書へ単語の追加

まずカスタム辞書を用意します。

カスタム辞書は以下の形式のCSVとします。

見出し語,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音

コストは単語の発生しやすさとのことです。小さい程よく発生する単語とのことで、
3000を基準に発生しやすいものは小さく、そうでないものは大きくするといいようです。

ある単語を登録する時に、何かの複合語になっている場合は、元のそれぞれの単語が
どの程度のコストで登録されているかを辞書ファイルを検索して調べると良いのかな?

あとは、カスタム辞書を用意して、ここでやったように、
辞書のCSVファイルの所に、カスタム辞書を追記して、antを実行して辞書を再生成します。

# vi $SEN_HOME/dic/build.xml

<arg line="customize_dic.csv dic.csv" />

# ant


しかし、、sen の辞書を追加(MkSenDic)したあとに、Tomcat を再起動しないとエラーになります。
実際には先にTomcatを停止した上で辞書の再生成をすることになると思いますが、どうにか起動したままできないものか。。



参考サイト



Feb 2, 2011

Apache Solr 1.4 スキーマの設定 schema.xml fieldType

Solr のスキーマの設定は、$SOLR_HOME/conf/schema.xml に記述します。

fieldTypeの設定

<fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>


sortMissingLast / SortMissingFirst の設定

sortMissingLast="true": ソートする際に値が空の場合に検索結果の最後に空の結果を配置する。
sortMissingFirst="true": ソートする際に値が空の場合に検索結果の最初に空の結果を配置する。

デフォルトは両方ともfalse。この場合は、以下のようになります。
昇順(asc)を指定した場合は、最初に空の結果を配置
降順(desc)を指定した場合は、最後に空の結果を配置

また、両方とも true にすると、いずれの場合も最後に配置されるようになります。

全パターンで試してみた所、どうやら sortMissingLastの設定が優先されるのかな?


omitNorms は、検索にヒットした時のスコアを計算する時の設定項目です。

平均値(Norm)を省略する(omit)かどうか、みたいな値です。

平均値とは、検索ワード/フィールドの文字長 の値で、この値が大きければスコアを上げるということになります。
元になるキーワードの量によって、スコアを変えたくない場合は、true に設定します。

ただし、boost 値を利用する場合は、omitNorms="false" にしておきます。

boost値とは、ドキュメントをアップロードする際に、各フィールドの重み付けをしたり、
検索クエリで、fieldname:value^boost のようにして、検索値の重み付けをすることができます。

この辺りがどう作用しているのかは、検索クエリに、debugQuery=on としてスコアの計算を見ることができます。


デフォルトで用意されているフィールドタイプ一覧

文字列、真偽、バイナリ

string
boolean(true, false)
binary(sent/retrived as Base64 encoded Strings)

数値、日付系

int
float
long
double
date

より速い範囲検索が必要な場合は、t~ を検討。

tint
tfloat
tlong
tdouble
tdate

以下は下位互換性のためにあるものなので、基本的には利用しない。

pint
plong
pfloat
pdouble
pdate

sint
slong
sfloat
sdouble

int, tint などは、いずれも solr.TrieXxxField の実装で、トライ木による木構造でIndexが生成されます。

違いは、precisionStepの値の設定。
しかし、precisionStep を変えるとどうなるのかは、分からなかった。。

schema.xml には以下のように書いてあるから、値が小さい方が範囲検索は速そう。

Smaller precisionStep values (specified in bits) will lead to more tokens
indexed per value, slightly larger index size, and faster range queries.
A precisionStep of 0 disables indexing at different precision levels.

でも、NumericField の、javadoc を見ると、デフォルト値は4になっており、
tint などでは、8に設定してあるから、デフォルトより大きい?と思ったり。。
これは今後の調査課題としておきます。


特殊なフィールドタイプ

random(ランダムソート時に利用)
ignored(完全に無視するフィールドに利用)

その他のカスタムフィールドタイプ

text_ws
text
textTight
textgen
text_rev
lowercase
....

これらがどういう働きをするかは、schema.xml を見ると分かります。


lucene-ja を使った、形態素解析のフィールドタイプは以下のように指定します。

<fieldType name="text_ja" class="solr.TextField">
 <analyzer class="org.apache.lucene.analysis.ja.JapaneseAnalyzer" />
</fieldType>

カスタムフィールドは、全て class="solr.TextField" と指定します。


これらたいていのことは、schema.xml を読めば、ほとんど説明が書いてあります。




Jan 31, 2011

Apache Solr 1.4 インストール on Tomcat

lucene-ja か、cmecab-java のどちらがいいか悩ましい。

検索エンジンの最新版を追わないのであれば、
JNI バインディングじゃない lucene-ja の方が好み。
でも、最新版を追いたくて、自分でメンテナンスする手間を省きたければ、
cmecab-java ってところか。

パフォーマンスは、両方の環境を作って比較するしかないかな。



環境:
CentOS 5.5 i386 on VMware Fusion3
Java6 SDK
Apache Ant
Apache Tomcat 5.5

インストールするもの:
Apache Solr 1.4
lucene-ja 2.0 + sen 1.2.2.1
cmecab-java + mecab 0.98


Apache Solr 1.4.1
download from http://www.apache.org/dyn/closer.cgi/lucene/solr/

# tar xzf apache-solr-1.4.1.tgz -C $INSTALL_DIR
# cd $INSTALL_DIR
# chown -R tomcat.tomcat apache-solr-1.4.1
# ln -s apache-solr-1.4.1 apache-solr
# export SOLR_HOME=$INSTALL_DIR/apache-solr


sen-1.2.2.1
インストール手順は /2011/01/sen-1221.html

Tomcat の起動オプションに sen.home を追加する。

# vi /etc/tomcat5/tomcat5.conf

JAVA_OPTS="$JAVA_OPTS -Dcatalina.ext.dirs=$CATALINA_HOME/shared/lib:$CATALINA_HOME/common/lib -Dsen.home=/usr/local/sen-1.2.2.1"


lucene-ja-2.0
インストール手順は /2011/01/lucene-ja-20.html




次にwebappsの設定

Tomcat に Solr の webapps を配置

# mkdir /var/lib/tomcat5/webapps/solr
# cd /var/lib/tomcat5/webapps/solr
# jar xf $SOLR_HOME/example/webapps/solr.war


sen.jar を webappsにコピーする。

# cp $SEN_HOME/lib/sen.jar /var/lib/tomcat5/webapps/solr/WEB-INF/lib


lucene-core.jar と lucene-ja.jar を webappsにコピーする。

# cd ~/lucene-ja/lib/
# cp lucene-core-2.0.0.jar lucene-ja-custom.jar /var/lib/tomcat5/webapps/solr/WEB-INF/lib/


solrのホームディレクトリを作成

サンプルをベースに利用します。

# cd $SOLR_HOME
# cp -a example/solr home

solrconfig.xml を編集して、データディレクトリを設定します。

# cd $SOLR_HOME
# vi home/conf/solrconfig.xml

  <dataDir>/usr/local/apache-solr/home/data</dataDir>


webapps の web.xml で、solr/home を設定します。

# vi /var/lib/tomcat5/webapps/solr/WEB-INF/web.xml

    <env-entry>
       <env-entry-name>solr/home</env-entry-name>
       <env-entry-value>/usr/local/apache-solr/home</env-entry-value>
       <env-entry-type>java.lang.String</env-entry-type>
    </env-entry>



Tomcat 起動

# /etc/init.d/tomcat5 start
# cat /var/log/tomcat5/catalina.out

ブラウザで、起動を確認する。

http://solrhost:8080/solr/


※ Tomcat の起動時に Xpath のエラーが出る場合は、xalan, xerces をインストールする。

download from http://xml.apache.org/xalan-j/downloads.html

# tar xzf xalan-j_2_7_1-bin-2jars.tar.gz
# cp xalan-j_2_7_1/*.jar /var/lib/tomcat5/shared/lib


※ 日本語の検索で文字化けする場合は、SetCharacterEncodingFilter を使う

ここではTomcat5のサンプルをそのまま利用します。

# cp SetCharacterEncodingFilter.class /var/lib/tomcat5/shared/classes/filters/
# vi /var/lib/tomcat5/webapps/solr/WEB-INF/web.xml

  <filter>
    <filter-name>Set Character Encoding</filter-name>
    <filter-class>filters.SetCharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>Set Character Encoding</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>


# vi /etc/tomcat5/tomcat5.conf

    <Connector port="8080" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true"
               URIEncoding="UTF-8" useBodyEncodingForURI="true" />




参考サイト

http://lucene.apache.org/solr/tutorial.html
http://d.hatena.ne.jp/knaka20blue/20080922/1222104683
http://kazuakey.blogspot.com/2010/12/solr-14-sen.html
http://d.hatena.ne.jp/gnarl/20080912/1221189985

lucene-ja 2.0 インストール

Lucene を、Solr1.4 で利用する場合には、一部ソースの修正が必要です。


環境:
CentOS 5.5 i386 on VMware Fusion3
Java6 SDK


download from https://sen.dev.java.net/servlets/ProjectDocumentList?folderID=755&expandFolder=755&folderID=755


修正するソースは、org.apache.lucene.analysis.ja パッケージの以下4ファイルです。

DigitFilter.java
KatakanaStemFilter.java
MonitorFilter.java
POSFilter.java


コンストラクタ中の input = in; をコメントアウトします。

# unzip lucene-ja-2.0test2.zip
# cd lucene-ja/src/org/apache/lucene/analysis/ja/
# vi DigitFilter.java KatakanaStemFilter.java MonitorFilter.java POSFilter.java

// input = in;


build.xml を配置してコンパイル

# cd ~/lucene-ja
# ant
# ls -l lib/lucene-ja-custom.jar



参考サイト

http://ameblo.jp/miralis/entry-10290203004.html
http://kazuakey.blogspot.com/2010/12/solr-14-sen.html
http://d.hatena.ne.jp/gnarl/20080912/1221189985

sen 1.2.2.1 インストール 辞書をnaist-jdic にする

Mecab の Java移植版。
構築する辞書の文字コードを、サーバの文字コードと同じUTF-8に変更します。
また、ipadic の後継版である、NAIST Japanese Dictional を使うように修正します。

download from https://sen.dev.java.net/servlets/ProjectDocumentList?folderID=755&expandFolder=755&folderID=755


環境:
CentOS 5.5 i386 on VMware Fusion3
Apache Ant


sen 1.2.2.1 インストール

# unzip sen-1.2.2.1.zip -d $INSTALL_DIR
# export SEN_HOME=$INSTALL_DIR/sen-1.2.2.1


バグFIX

http://d.hatena.ne.jp/gnarl/20080912/1221189985

※ sen のコンパイルには、gcc-c++ などが必要です。


sen の辞書作成

a. 構築する辞書の文字コードを、utf-8 に変更する。(デフォルトは euc-jp)
b. NAIST-jdic の辞書を利用するように変更する。

文字コードの編集対象ファイルは、sen.xml, sen-processor.xml, dictionary.properties の3つです。
元々の辞書ファイルは、euc-jp で記述されており、その設定は変更しません。

# cd $SEN_HOME
# vi conf/sen.xml
   <charset>utf-8</charset>

# vi conf/sen-processor.xml
   <charset>utf-8</charset>

# vi dic/dictionary.properties
   sen.charset=utf-8


ChaSen 用の NAIST辞書ファイル(NAIST-jdic)をダウンロード。
http://sourceforge.jp/projects/naist-jdic/

# cp naist-jdic-0.4.3.tar.gz $SEN_HOME/dic/


build.xml の編集
ipadic の設定を、naist-jdic に変更します。
また同時に、OutOfMemoryError を回避するために、maxmemory を増やしておきます。

# cd $SEN_HOME/dic
# vi build.xml

  <target name="create" depends="dics0" unless="dics.present">
    <java classname="net.java.sen.tools.MkSenDic"
          fork="true"
          maxmemory="512m">


ipadic のファイル名を、naist-jdic に変更。

# diff build.xml build_naist-jdic.xml
17,19c17,19
<   <property name="ipadic.version" value="2.6.0"/>
<   <property name="ipadic.archive"  value="ipadic-${ipadic.version}.tar.gz"/>
<   <property name="ipadic.dir"  value="ipadic-${ipadic.version}"/>
---
>   <property name="ipadic.version" value="0.4.3"/>
>   <property name="ipadic.archive"  value="naist-jdic-${ipadic.version}.tar.gz"/>
>   <property name="ipadic.dir"  value="naist-jdic-${ipadic.version}"/>
56c56
<         <available file="ipadic-${ipadic.version}/Noun.dic"/>
---
>         <available file="naist-jdic-${ipadic.version}/Noun.dic"/>
89c89
<       <arg line="${ipa2mecab} ipadic-${ipadic.version}"/>
---
>       <arg line="${ipa2mecab} naist-jdic-${ipadic.version}"/>


辞書ファイルの生成

# ant

確認

# sh $SEN_HOME/bin/sen.sh
done.
Please input Japanese sentence:
[INFO] Dictionary - token file = /usr/local/sen-1.2.2.1/dic/token.sen
[INFO] Dictionary - time to load posInfo file = 22[ms]
[INFO] Dictionary - double array trie dictionary = /usr/local/sen-1.2.2.1/dic/da.sen
[INFO] DoubleArrayTrie - loading double array trie dict = /usr/local/sen-1.2.2.1/dic/da.sen
[INFO] DoubleArrayTrie - loaded time = 0.631[ms]
[INFO] Dictionary - pos info file = /usr/local/sen-1.2.2.1/dic/posInfo.sen
[INFO] Dictionary - time to load pos info file = 0[ms]
[INFO] Tokenizer - connection file = /usr/local/sen-1.2.2.1/dic/matrix.sen
[INFO] Tokenizer - time to load connect cost file = 91[ms]

無事に形態素解析されますか?
無事 (無事) 名詞-形容動詞語幹(0,2,2) ブジ ブジ
に (に) 助詞-副詞化(2,3,1) ニ ニ
形態素 (形態素) 名詞-一般(3,6,3) ケイタイソ ケイタイソ
解析 (解析) 名詞-サ変接続(6,8,2) カイセキ カイセキ
さ (する) 動詞-自立(8,9,1) サ サ
れ (れる) 動詞-接尾(9,10,1) レ レ
ます (ます) 助動詞(10,12,2) マス マス
か (か) 助詞-副助詞/並立助詞/終助詞(12,13,1) カ カ
? (?) 記号-一般(13,14,1) ? ?

すもももももももものうち
すもも (すもも) 名詞-一般(0,3,3) スモモ スモモ
も (も) 助詞-係助詞(3,4,1) モ モ
もも (もも) 名詞-一般(4,6,2) モモ モモ
も (も) 助詞-係助詞(6,7,1) モ モ
もも (もも) 名詞-一般(7,9,2) モモ モモ
の (の) 助詞-連体化(9,10,1) ノ ノ
うち (うち) 名詞-非自立-副詞可能(10,12,2) ウチ ウチ

在庫品SALE
在庫 (在庫) 名詞-サ変接続(0,2,2) ザイコ ザイコ
品 (品) 名詞-接尾-一般(2,3,1) ヒン ヒン
S (S) 記号-アルファベット(3,4,1) エス エス
A (A) 記号-アルファベット(4,5,1) エイ エイ
L (L) 記号-アルファベット(5,6,1) エル エル
E (E) 記号-アルファベット(6,7,1) イー イー

NAIST-jdic への移行は無事できましたが、ただ、英単語を分つことができない。。
原因はアルファベットが辞書に登録してあるためでした。

そこで、アルファベットを取り除いてみると。

# diff ipa2mecab_org.pl ipa2mecab.pl
188,189d187
<     next if ($pos2 eq 'アルファベット');
<

# ant
# sh $SEN_HOME/bin/sen.sh

在庫品SALE
在庫 (在庫) 名詞-サ変接続(0,2,2) ザイコ ザイコ
品 (品) 名詞-接尾-一般(2,3,1) ヒン ヒン
SALE (SALE) 未知語(3,7,4) null null

未知語でも良いので、連結してくれる方がいいよね。


さて、あとは、mecab用の最新の辞書を取込んでみたいところ。
mecab-naist-jdic-0.6.3-20100801/naist-jdic.csv
の方が約30,000語も多いし。。

どなたか NAIST-jdic for mecab の辞書を sen に取込む方法をご存知の方がいれば、
ご連絡くださいますと幸いです。


追記:

2/18
その後、sen.xml に、composit の設定を追加することによって、アルファベットや数字を結合できることが
分かりました。
そのため、辞書からアルファベットを取り除くよりも、composit を設定して結合する方がいいかと思い、
再度アルファベットを辞書に登録しました。
また、「-」をアルファベットとすると、wi-fi のような単語を作ることができるので、
- を記号、アルファベットとして、カスタム辞書に登録しました。
Sen は奥深いですね。関係者の方々に心から感謝です。


参考サイト

http://ultimania.org/sen/
http://kazuakey.blogspot.com/2010/12/solr-14-sen.html
http://d.hatena.ne.jp/gnarl/20080912/1221189985
http://d.hatena.ne.jp/code46/comment?date=20090531