diff --git a/lucene/contrib/CHANGES.txt b/lucene/contrib/CHANGES.txt index 93e1dce5d89..9b7134204f2 100644 --- a/lucene/contrib/CHANGES.txt +++ b/lucene/contrib/CHANGES.txt @@ -142,6 +142,9 @@ New Features * LUCENE-3305: Added Kuromoji morphological analyzer for Japanese. (Christian Moen, Masaru Hasegawa, Simon Willnauer, Uwe Schindler, Mike McCandless, Robert Muir) + * LUCENE-3730: Refine Kuromoji search mode (Mode.SEARCH) decompounding + heuristics. (Christian Moen via Robert Muir) + * LUCENE-3685: Add ToChildBlockJoinQuery and renamed previous BlockJoinQuery to ToParentBlockJoinQuery, so that you can now do joins in both parent to child and child to parent directions. diff --git a/modules/analysis/kuromoji/src/java/org/apache/lucene/analysis/kuromoji/viterbi/Viterbi.java b/modules/analysis/kuromoji/src/java/org/apache/lucene/analysis/kuromoji/viterbi/Viterbi.java index e44235182eb..11b2a8dae73 100644 --- a/modules/analysis/kuromoji/src/java/org/apache/lucene/analysis/kuromoji/viterbi/Viterbi.java +++ b/modules/analysis/kuromoji/src/java/org/apache/lucene/analysis/kuromoji/viterbi/Viterbi.java @@ -54,11 +54,13 @@ public class Viterbi { private static final int DEFAULT_COST = 10000000; - private static final int SEARCH_MODE_LENGTH_KANJI = 3; - - private static final int SEARCH_MODE_LENGTH = 7; - - private static final int SEARCH_MODE_PENALTY = 10000; + private static final int SEARCH_MODE_KANJI_LENGTH = 2; + + private static final int SEARCH_MODE_OTHER_LENGTH = 7; // Must be >= SEARCH_MODE_KANJI_LENGTH + + private static final int SEARCH_MODE_KANJI_PENALTY = 3000; + + private static final int SEARCH_MODE_OTHER_PENALTY = 1700; private static final char[] BOS = "BOS".toCharArray(); @@ -137,7 +139,7 @@ public class Viterbi { char[] surfaceForm = node.getSurfaceForm(); int offset = node.getOffset(); int length = node.getLength(); - if (length > SEARCH_MODE_LENGTH_KANJI) { + if (length > SEARCH_MODE_KANJI_LENGTH) { boolean allKanji = true; // check if node consists of only kanji for (int pos = 0; pos < length; pos++) { @@ -148,9 +150,9 @@ public class Viterbi { } if (allKanji) { // Process only Kanji keywords - pathCost += (length - SEARCH_MODE_LENGTH_KANJI) * SEARCH_MODE_PENALTY; - } else if (length > SEARCH_MODE_LENGTH) { - pathCost += (length - SEARCH_MODE_LENGTH) * SEARCH_MODE_PENALTY; + pathCost += (length - SEARCH_MODE_KANJI_LENGTH) * SEARCH_MODE_KANJI_PENALTY; + } else if (length > SEARCH_MODE_OTHER_LENGTH) { + pathCost += (length - SEARCH_MODE_OTHER_LENGTH) * SEARCH_MODE_OTHER_PENALTY; } } } diff --git a/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/TestExtendedMode.java b/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/TestExtendedMode.java index 1212f00d608..ed9e2c13752 100644 --- a/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/TestExtendedMode.java +++ b/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/TestExtendedMode.java @@ -1,18 +1,5 @@ package org.apache.lucene.analysis.kuromoji; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; - -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.BaseTokenStreamTestCase; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.kuromoji.Segmenter.Mode; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.util.UnicodeUtil; -import org.apache.lucene.util._TestUtil; - /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -30,6 +17,19 @@ import org.apache.lucene.util._TestUtil; * limitations under the License. */ +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.BaseTokenStreamTestCase; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.kuromoji.Segmenter.Mode; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.util.UnicodeUtil; +import org.apache.lucene.util._TestUtil; + public class TestExtendedMode extends BaseTokenStreamTestCase { private final Segmenter segmenter = new Segmenter(Mode.EXTENDED); private final Analyzer analyzer = new Analyzer() { diff --git a/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/TestSearchMode.java b/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/TestSearchMode.java new file mode 100644 index 00000000000..f774982f4b1 --- /dev/null +++ b/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/TestSearchMode.java @@ -0,0 +1,72 @@ +package org.apache.lucene.analysis.kuromoji; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.Reader; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.BaseTokenStreamTestCase; +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.kuromoji.Segmenter.Mode; +import org.apache.lucene.util.IOUtils; + +public class TestSearchMode extends BaseTokenStreamTestCase { + private final static String SEGMENTATION_FILENAME = "search-segmentation-tests.txt"; + private final Segmenter segmenter = new Segmenter(Mode.SEARCH); + private final Analyzer analyzer = new Analyzer() { + @Override + protected TokenStreamComponents createComponents(String fieldName, Reader reader) { + Tokenizer tokenizer = new KuromojiTokenizer(segmenter, reader); + return new TokenStreamComponents(tokenizer, tokenizer); + } + }; + + /** Test search mode segmentation */ + public void testSearchSegmentation() throws IOException { + InputStream is = TestSearchMode.class.getResourceAsStream(SEGMENTATION_FILENAME); + if (is == null) { + throw new FileNotFoundException("Cannot find " + SEGMENTATION_FILENAME + " in test classpath"); + } + try { + LineNumberReader reader = new LineNumberReader(new InputStreamReader(is, IOUtils.CHARSET_UTF_8)); + String line = null; + while ((line = reader.readLine()) != null) { + // Remove comments + line = line.replaceAll("#.*$", ""); + // Skip empty lines or comment lines + if (line.trim().isEmpty()) { + continue; + } + if (VERBOSE) { + System.out.println("Line no. " + reader.getLineNumber() + ": " + line); + } + String[] fields = line.split("\t", 2); + String sourceText = fields[0]; + String[] expectedTokens = fields[1].split("\\s+"); + assertAnalyzesTo(analyzer, sourceText, expectedTokens); + } + } finally { + is.close(); + } + } +} diff --git a/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/search-segmentation-tests.txt b/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/search-segmentation-tests.txt new file mode 100644 index 00000000000..d217b4ebeba --- /dev/null +++ b/modules/analysis/kuromoji/src/test/org/apache/lucene/analysis/kuromoji/search-segmentation-tests.txt @@ -0,0 +1,140 @@ +### +### Tests for Kuromoji's search mode heuristic +### +### In search-mode, Kuromoji uses a heuristic to do extra splitting of words +### to get a decompounding effect useful for search. This file includes tests +### for this heuristic and demonstrates its usefulness, but also weaknesses. +### +### This file's format is as follows: +### ... +### +### This file should use UTF-8 encoding and there is one test per line. The +### text to be segmented and its expected surface form token sequence is +### separated by a tab ('\t'). Tokens are separated by a half-width space. +### Whitespace lines and lines starting with a '#' are ignored. Comments +### are not allowed on entry line. +### +### NOTE: These tests depends on IPADIC +### +### Revision history: +### - 2012-01-29: Initial version +### + +## +## Organizations +## + +# Kansai Internationl Airport +関西国際空港 関西 国際 空港 +# Narita Airport +成田空港 成田 空港 +# Haneda Airport +羽田空港 羽田 空港 +# Nara Institute of Science and Technology +奈良先端科学技術大学院大学 奈良 先端 科学 技術 大学院 大学 +# Tokyo University +東京大学 東京 大学 +# Kyoto University +京都大学 京都 大学 +# Kyoto University Baseball Club +京都大学硬式野球部 京都 大学 硬式 野球 部 + +## +## Katakana titles +## + +# Senior Software Engineer +シニアソフトウェアエンジニア シニア ソフトウェア エンジニア +# Software Engineer +ソフトウェアエンジニア ソフトウェア エンジニア +# Senior Project Manager +シニアプロジェクトマネジャー シニア プロジェクト マネジャー +# Project Manager +プロジェクトマネジャー プロジェクト マネジャー +# Senior Sales Engineer +シニアセールスエンジニア シニア セールス エンジニア +# System Architect +システムアーキテクト システム アーキテクト +# Senior System Architect +シニアシステムアーキテクト シニア システム アーキテクト +# System Administrator +システムアドミニストレータ システム アドミニストレータ +システムアドミニストレーター システム アドミニストレーター +# Senior System Administrator +シニアシステムアドミニストレーター シニア システム アドミニストレーター + +## +## Company names (several are fictitious) +## + +# SoftBank Mobile +ソフトバンクモバイル ソフトバンク モバイル +# Alpine Materials +アルパインマテリアルズ アルパイン マテリアルズ +# Sapporo Holdings +サッポロホールディングス サッポロ ホールディングス +# Yamada Corporation +ヤマダコーポレーション ヤマダ コーポレーション +# Canon Semiconductor equipement NOTE: Semiconductor becomes semi + conductor +キヤノンセミコンダクターエクィップメント キヤノン セミ コンダクター エクィップメント +# Orental Chain +オリエンタルチエン オリエンタル チエン +# Ally Projects Japan NOTE: Becomes one token as プロジェクツ is not in IPADIC +アーリープロジェクツジャパン アーリープロジェクツジャパン +# Peter Pan Corporation +ピーターパンコーポレーション ピーター パン コーポレーション +# AIM Create +エイムクリエイツ エイムクリエイツ +# Mars Engineering +マースエンジニアリング マース エンジニアリング +# Fuji Protein Technology +フジプロテインテクノロジー フジ プロテイン テクノロジー + +## +## Person names +## + +# Michael Jackson +マイケルジャクソン マイケル ジャクソン +# Steve Jobs +スティーブジョブズ スティーブ ジョブズ +# Harry Potter NOTE: Becomes one token (short word) +ハリーポッター ハリーポッター +# Bill Gates NOTE: Becomes one token (short work) +ビルゲイツ ビルゲイツ +# Sean Connery NOTE: Becomes one token (okay) +ショーンコネリー ショーンコネリー + +## +## Other nouns +## + +# Holdings +ホールディングス ホールディングス +# Engineering +エンジニアリング エンジニアリング +# Software Engineering +ソフトウェアエンジニアリング ソフトウェア エンジニアリング +# Shopping center +ショッピングセンター ショッピング センター +# Game center (arcade) NOTE: One token because of short word +ゲームセンター ゲームセンター +# Christmas shopping +クリスマスショッピング クリスマス ショッピング +# Download file +ダウンロードファイル ダウンロード ファイル +# Technology +テクノロジー テクノロジー +# Lillehammer Olympics +リレハンメルオリンピック リレハンメル オリンピック + +## +## Problematic terms +## + +# JT Engineering NOTE: Becomes J Tien ginia ring (substrings are in IPADIC) +ジェイティエンジニアリング ジェイ ティエン ジニア リング +# Anchovy pasta NOTE: Become Anch yvipasta +アンチョビパスタ アンチ ョビパスタ +# Surprise gift NOTE: Becomes one token (surprise not in IPADIC) +サプライズギフト サプライズギフト