Lucene WildCardのマッチングは、化学物質表記で失敗します(?)
-
04-10-2019 - |
質問
Hibernate Search Annotationsを使用します(ほとんどの場合 @Field(index = Index.TOKENIZED)
)私は、化合物と呼ばれる鉱山の持続クラスに関連する多くのフィールドにインデックスを付けました。すべてのインデックス化されたフィールドでテキスト検索をセットアップし、 MultiFieldQueryParser
, 、これはこれまでに正常に機能しています。
インデックス化および検索可能なフィールドの中には、サンプル値を備えたCompountNameと呼ばれるフィールドがあります。
3-Hydroxyflavone
6,4'-Dihydroxyflavone
これらの値のいずれかを完全に検索すると、関連する複合インスタンスが返されます。ただし、部分名を使用してワイルドカードを導入すると問題が発生します。
- 探している
3-Hydroxyflav*
まだ正しいヒットを与えますが、 - 探している
6,4'-Dihydroxyflav*
何も見つけられません。
今、私はルーセン / hibernate-searchに非常に慣れていないので、この時点でどこを見るべきかよくわかりません。 '
2番目のクエリに掲載されていますが、進む方法がわかりません。トークナーザー /アナライザー / QueryParsersまたは他の何かを完全に調べる必要がありますか?
それとも、マルチフィールド検索の動作を破らずに、2回目のワイルドカード検索をどのようにマッチさせることができるかを教えてもらえますか?
Hibernate-Search 3.1.0.ga&Lucene-Core 2.9.3を使用しています。
私の現在のアプローチを説明するためのいくつかの関連するコードビット:
インデックス化された化合物クラスの関連部分:
@Entity
@Indexed
@Data
@EqualsAndHashCode(callSuper = false, of = { "inchikey" })
public class Compound extends DomainObject {
@NaturalId
@NotEmpty
@Length(max = 30)
@Field(index = Index.TOKENIZED)
private String inchikey;
@ManyToOne
@IndexedEmbedded
private ChemicalClass chemicalClass;
@Field(index = Index.TOKENIZED)
private String commonName;
...
}
現在、インデックス化されたフィールドで検索する方法:
String[] searchfields = Compound.getSearchfields();
MultiFieldQueryParser parser =
new MultiFieldQueryParser(Version.LUCENE_29, searchfields, new StandardAnalyzer(Version.LUCENE_29));
FullTextSession fullTextSession = Search.getFullTextSession(getSession());
FullTextQuery fullTextQuery =
fullTextSession.createFullTextQuery(parser.parse("searchterms"), Compound.class);
List<Compound> hits = fullTextQuery.list();
解決
あなたの問題は、アナライザーとクエリの言語の問題の組み合わせだと思います。何が問題を引き起こすのかを言うのは難しいです。これを見つけるには、Luceneインデックスツールを使用してインデックスを検査することをお勧めします ルーク.
冬眠検索構成では、カスタムアナライザーを使用していないため、デフォルト - StandardAnalyzer - 使用されている。これはあなたが使用するという事実と一致します StandardAnalyzer のコンストラクターで MultifieldQueryParser (常に同じアナライザーを使用して、インデックス作成と検索に使用してください!)。私がよくわからないのは、「6,4'-dihydroxyflavone」がどのようにトークン化されるかということです StandardAnalyzer. 。あなたが最初に見つけなければならないこと。たとえば、Javadocは次のように述べています。
トークンに数がある場合を除き、ハイフンで単語を分割します。その場合、トークン全体が製品番号として解釈され、分割されていません。
化学名をユースケースに必要な方法でトークン化する独自のアナライザーを作成する必要があるかもしれません。
次にクエリパーサー。クエリの構文を理解してください - ルーセンクエリ構文. 。一部の文字には、特別な意味があります。たとえば、「 - 」です。クエリが間違った方法で解析されている可能性があります。
いずれにせよ、あなたの化学名がどのようにトークン化されるかを調べるための最初のステップOS。それが役立つことを願っています。
他のヒント
StandardAnalyzerの代わりにWhitespaceanalyzerを使用します。それは、コンマ、ハイフンなどではなく、ホワイトスパースで分割されます(ただし、小文字は小文字ではないので、検索がケースに依存しないと仮定して、独自のホワイトスパース +小文字を構築する必要があります)。さまざまなフィールドに対して違うことをする必要がある場合は、パフォーマンスアリザーを使用できます。
それをトークン化していないように設定することはできません。なぜなら、それはあなたのテキスト全体を1つのトークンとして解釈するからです。
私は自分のアナライザーを書きました:
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.lucene.index.memory.PatternAnalyzer;
import org.apache.lucene.util.Version;
public class ChemicalNameAnalyzer extends PatternAnalyzer {
private static Version version = Version.LUCENE_29;
private static Pattern pattern = compilePattern();
private static boolean toLowerCase = true;
private static Set stopWords = null;
public ChemicalNameAnalyzer(){
super(version, pattern, toLowerCase, stopWords);
}
public static Pattern compilePattern() {
StringBuilder sb = new StringBuilder();
sb.append("(-{0,1}\\(-{0,1})");//Matches an optional dash followed by an opening round bracket followed by an optional dash
sb.append("|");//"OR" (regex alternation)
sb.append("(-{0,1}\\)-{0,1})");
sb.append("|");//"OR" (regex alternation)
sb.append("((?<=([a-zA-Z]{2,}))-(?=([^a-zA-Z])))");//Matches a dash ("-") preceded by two or more letters and succeeded by a non-letter
return Pattern.compile(sb.toString());
}
}