第4章 インデックス構造にエンティティをマッピング
エンティティをインデックス化するために必要なすべてのメタデータ情報は、アノテーションを使用して定義されます。xml マッピングファイルで作業する必要はありません。実際は、現在 xml 設定オプションは利用できません (HSEARCH-210 を参照)。基本的な Hibernate 設定には Hibernate マッピングファイルを引き続き使用できますが、Hibernate Search 固有の設定はアノテーションを使用して指定する必要があります。
4.1. エンティティのマッピング
4.1.1. 基本的なマッピング
まず、 永続クラスをインデックス可能として宣言する必要があります。クラスに
@Indexed アノテーションを付与してこれを行います (@Indexed アノテーションが付与されていないエンティティはすべてインデックス化のプロセスで無視されることになる)。
例4.1 @Indexed アノテーションを使用してクラスのインデックス化を可能にする
@Entity
@Indexed(index="indexes/essays")
public class Essay {
...
}index 属性は Lucene ディレクトリ名が何になるかを Hibernate に指示します (通常はファイルシステム上のディレクトリ)。設定ファイルで hibernate.search.default.indexBase プロパティを使用して、すべての Lucene インデックスにベースディレクトリを定義することが推奨されます。または、hibernate.search.<index>.indexBase (<index> はインデックス化されたエンティティの完全修飾クラス名) を指定してインデックス化された各エンティティに対してベースディレクトリを指定できます。各エンティティインスタンスは特定のインデックス (つまり Directory) の内側の Lucene Document で表されます。
エンティティの各プロパティ (または属性) に対してそれがどのようにインデックス化されるかを定義できます。デフォルトでは (つまりアノテーションなし) そのプロパティはインデックス化のプロセスで無視されます。
@Field はプロパティをインデックス化されたものとして宣言します。Lucene ドキュメントにエレメントをインデックス化する場合はどのようにインデックス化するかを指定することができます。
name: どの名前でプロパティが Lucene Document に格納されるかを指定します。 デフォルト値はそのプロパティ名です (JavaBeans 規則に準拠)。store: プロパティが Lucene インデックスに格納されるかされないかを指定します。Store.YESの値 (インデックス内の領域消費が多いが、プロジェクションを許可。詳細については、「プロジェクション」 を参照)、 圧縮形式で格納するStore.COMPRESS(CPU の消費が多い)、 またはまったく格納しないようにするStore.NO(デフォルトの値) のいずれかにセットできます。 プロパティが格納される場合はその元の値を Lucene Document から検索できます。これはエレメントがインデックス化されるか否かとは関係ありません。- index: どのようにエレメントのインデックス化を行うかと情報ストアのタイプを指定します。
Index.NO(インデックス化なし、 クエリでは見つけられない)、Index.TOKENIZED(プロパティの処理にアナライザを使用する)、Index.UN_TOKENISED(アナライザ事前処理なし)、Index.NO_NORM(正常化データを格納しない)などの値があります。デフォルト値はTOKENIZEDです。 - termVector: 期間と頻度のペアのコレクションを定義します。この属性により、インデックス化の間に期間ベクタが格納され、ドキュメント内で期間ベクタを利用できるようになります。デフォルト値は TermVector.NO です。この属性の他の値は次のとおりです。
値 定義 TermVector.YES 各ドキュメントの期間ベクタを格納します。これにより、同期化された 2 つのアレイが生成されます。1 つはドキュメント期間を含み、もう 1 つは期間の頻度を含みます。 TermVector.NO 期間ベクタは格納しないでください。 TermVector.WITH_OFFSETS 期間ベクタとトークンオフセット情報を格納します。これは、TermVector.YES と同じです。また、期間の開始/終了オフセット位置情報も含まれます。 TermVector.WITH_POSITIONS 期間ベクタおよびトークン位置情報を格納します。これは、TermVector.YES と同じです。また、ドキュメント内の各期間の順序の位置が含まれます。 TermVector.WITH_POSITIONS_OFFSETS 期間ベクタ、トークン位置、およびオフセット情報を格納します。これは、YES、WITH_OFFSETS、および WITH_POSITIONS の組み合わせです。
インデックスの元のデータを格納するかどうかは、インデックスクエリの結果をどのように使用するかに基づきます。通常の Hibernate Search の使用では、格納機能は必要ありません。ただし、一部のフィールドを格納して結果的にプロジェクションを行うことができます (詳細については、「プロジェクション」 を参照)。
プロパティをトークン化するかどうかは、エレメントをそのまま、または含まれるワードによって検索するかどうかに基づきます。テキストフィールドをトークン化することは適切ですが、日付フィールドをトークン化することは適切ではないことがあります。
注記
ソートに使用するフィールドはトークン化してはいけません。
最後に、エンティティの id プロパティは特定エンティティのインデックス単一性を確保するために Hibernate Search により使用される特殊なプロパティになります。 設計により、id を格納してトークン化しなければなりません。プロパティをインデックス id としてマークするには
@DocumentId アノテーションを使用します。Hibernate Annotations を使用し、@Id を指定した場合は、@DocumentId を省略できます。選択されたエンティティ id はドキュメント id としても使用されます。
例4.2 インデックス化されたエンティティに @DocumentId ad @Field アノテーションを追加
@Entity
@Indexed(index="indexes/essays")
public class Essay {
...
@Id
@DocumentId
public Long getId() { return id; }
@Field(name="Abstract", index=Index.TOKENIZED, store=Store.YES)
public String getSummary() { return summary; }
@Lob
@Field(index=Index.TOKENIZED)
public String getText() { return text; }
}
例4.2「インデックス化されたエンティティに
@DocumentId ad @Field アノテーションを追加」 では、id、Abstract、text の 3 つのフィールドでインデックスを定義します。デフォルトではフィールド名は JavaBean の仕様に従い小文字に変換されます。
4.1.2. プロパティを複数回マッピング
場合によっては、1 つのインデックスに対してプロパティを複数回 (若干異なるインデックス方針に従って) マップする必要があります。たとえば、query by フィールドをソートするには、フィールドが
UN_TOKENIZED である必要があります。このプロパティのワードで検索し、ソートする場合は、プロパティを 2 回インデックス化する必要があります (1 回はトークン化、もう 1 回はトークン化解除)。@Fields を使用すると、この目的を達成できます。
例4.3 @Fields を使用してプロパティを複数回マップ
@Entity
@Indexed(index = "Book" )
public class Book {
@Fields( {
@Field(index = Index.TOKENIZED),
@Field(name = "summary_forSort", index = Index.UN_TOKENIZED, store = Store.YES)
} )
public String getSummary() {
return summary;
}
...
}
例4.3「@Fields を使用してプロパティを複数回マップ」 では、フィールド
summary は 2 回インデックス化されます(1 回はトークン化の方法で summary として、もう 1 回は非トークン化の方法で summary_forSort として)。@Field は @Fields が使用された場合に役に立つ 2 つの属性をサポートします。
- アナライザ: プロパティごとではなくフィールドごとに @Analyzer アノテーションを定義します。
- ブリッジ: プロパティごとではなくフィールドごとに @FieldBridge アノテーションを定義します。
アナライザとフィールドブリッジの詳細については、以下を参照してください。
4.1.3. 組込みおよび関連付けられたオブジェクト
関連付けられたオブジェクトと組込みオブジェクトは、ルートエンティティインデックスの一部としてインデックス化できます。これは、関連付けられたオブジェクトのプロパティに基づいて特定のエンティティを検索する場合に役に立ちます。以下の例の目的は関連付けられた都市が Atlanta である場所を返します (Lucene クエリパーサー言語では、
address.city:Atlanta に変換されます)。
例4.4 関係をインデックス化するために @IndexedEmbedded を使用
@Entity
@Indexed
public class Place {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field( index = Index.TOKENIZED )
private String name;
@OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } )
@IndexedEmbedded
private Address address;
....
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
@Field(index=Index.TOKENIZED)
private String street;
@Field(index=Index.TOKENIZED)
private String city;
@ContainedIn
@OneToMany(mappedBy="address")
private Set<Place> places;
...
}
この例では、プレースフィールドが
Place インデックスでインデックス化されます。Place インデックスドキュメントには、フィールド address.id、address.street、および address.city (問い合わせることが可能) が含まれます。これは、@IndexedEmbedded アノテーションにより有効化されます。
以下の点にご注意ください:
@IndexedEmbedded テクニックを使用した場合、データは Lucene インデックスで非正規化されるため、Hibernate Search はインデックスを最新の状態に保つために Place オブジェクトと Address オブジェクトの変更を認識する必要があります。Address が変更したときに Place Lucene ドキュメントが更新されるように、@ContainedIn との双方向の関係の一方をマークする必要があります。
@ContainedIn は、組込みオブジェクト (オブジェクトコレクション) ではなくエンティティを参照する関係の場合のみ役に立ちます。
例をもう少し複雑にしてみましょう。
例4.5 @IndexedEmbedded と @ContainedIn のネストでの使用
@Entity
@Indexed
public class Place {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field( index = Index.TOKENIZED )
private String name;
@OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } )
@IndexedEmbedded
private Address address;
....
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
@Field(index=Index.TOKENIZED)
private String street;
@Field(index=Index.TOKENIZED)
private String city;
@IndexedEmbedded(depth = 1, prefix = "ownedBy_")
private Owner ownedBy;
@ContainedIn
@OneToMany(mappedBy="address")
private Set<Place> places;
...
}
@Embeddable
public class Owner {
@Field(index = Index.TOKENIZED)
private String name;
...
}@*ToMany, @*ToOne および @Embedded 属性は、 @IndexedEmbedded を使用してアノテートできます。関連付けられたクラスの属性は、主なエンティティインデックスに追加されます。前の例では、インデックスに以下のフィールドが含まれます。
- id
- name
- address.street
- address.city
- addess.ownedBy_name
デフォルトのプレフィックスは、従来のオブジェクトナビゲーション規則に基づき
propertyName. となります。これは、ownedBy プロパティで示されたように prefix 属性を使用してオーバーライドできます。
注記
プレフィックスは空白の文字列として設定できません。
オブジェクトグラフにクラス (インスタンスではなく) の周期的な依存関係が含まれる場合は、
depth プロパティが必要です。たとえば、Owner が Place を参照する場合、Hibernate Search は期待された深さに達した後 (または、オブジェクトグラフ境界に達した後) にインデックス化された組込み属性を含めることを止めます。自己参照を持つクラスは周期的な依存関係の例です。例では、depth が 1 に設定されているため、Owner (存在する場合) の @IndexedEmbedded 属性は無視されます。
オブジェクト関係に
@IndexedEmbedded を使用すると、以下のようにクエリを記述できます。
- 名前に JBoss が含まれ、住所の都市が Atlanta である場所を返します。Lucene クエリでは、以下のようになります。
+name:jboss +address.city:atlanta
- 名前に JBoss が含まれ、所有者の名前に Joe が含まれる場所を返します。Lucene クエリでは、以下のようになります。
+name:jboss +address.orderBy_name:joe
つまり、これはリレーショナル結合操作をより効率的な方法で模倣しています (ただし、データ重複が問題となります)。デフォルトでは、Lucene インデックスには関係が含まれず、結合操作は単に存在しません。完全テキストインデックスの速度と機能の豊富さという点でメリットがある一方で、リレーショナルモデルを正規化することが役に立つ場合があります。
注記
関連付けられたオブジェクト自体が
@Indexed である場合があります (ただし、これに限定されません)。
@IndexedEmbedded がエンティティを参照する場合、関係は両方向であり、一方を
@ContainedIn でアノテートする必要があります (前の例で示されたように)。これに該当しない場合、Hibernate Search は関連付けられたエンティティが更新されたときにルートインデックスを更新できません (例では、関連付けられた Address インスタンスが更新されたときに、Place インデックスドキュメントを更新する必要があります)。
場合によっては、
@IndexedEmbedded でアノテートされたオブジェクトタイプが Hibernate と Hibernate Search が対象とするオブジェクトタイプではないことがあります。これは、実装の代わりにインターフェイスが使用される場合に特に当てはまります。このため、Hibernate Search が対象とするオブジェクトタイプは targetElement パラメータを使用してオーバーライドできます。
例4.6 @IndexedEmbedded の targetElement プロパティの使用
@Entity
@Indexed
public class Address {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field(index= Index.TOKENIZED)
private String street;
@IndexedEmbedded(depth = 1, prefix = "ownedBy_", targetElement = Owner.class)
@Target(Owner.class)
private Person ownedBy;
...
}
@Embeddable
public class Owner implements Person { ... }4.1.4. ブーストファクタ
Lucene には ブーストファクタ という概念があります。インデックス化の処理中にあるフィールドまたはインデックス化エレメントを他のフィールドまたはエレメントよりも重視する方法です。
@Boost を @Field、メソッド、またはクラスレベルで使用することができます。
例4.7 ブーストファクタを使用してインデックス化エレメントの重みをさまざまな方法で増やす
@Entity @Indexed(index="indexes/essays") @Boost(1.7f) public class Essay { ... @Id @DocumentId public Long getId() { return id; } @Field(name="Abstract", index=Index.TOKENIZED, store=Store.YES, boost=@Boost(2f)) @Boost(1.5f) public String getSummary() { return summary; } @Lob @Field(index=Index.TOKENIZED, boost=@Boost(1.2f)) public String getText() { return text; } @Field public String getISBN() { return isbn; } }
ここの例では
Essay が検索一覧のトップに到達する確率は 1.7 で乗じられ、summary フィールドは isbn フィールドより 3.0 (2 * 1.5 - @Field.boost とプロパティの @Boost は累積されます) 重要になります。text フィールドは isbn よりも 1.2 場合重要になります。 この説明は実際には誤りですが、 説明としてはわかりやすく現実に近いものとなります。 Otis Gospodnetic および Erik Hatcher の 『Lucene In Action』 または Lucene 関連のドキュメントを確認してください。
4.1.5. 動的ブーストファクタ
「ブーストファクタ」 で使用する
例4.8「動的ブーストの例」 では、動的ブーストは、インデックス化の時に使用される
もちろん、エンティティの
@Boost アノテーションは、ランタイム時にインデックス化されたエンティティの状態から無関係の静的ブーストファクタを定義します。ただし、ブーストファクタが実際のエンティティの状態によって異なる使用事例もあります。この場合は、付随のカスタムの BoostStrategy とともに @DynamicBoost アノテーションを使用できます。
例4.8 動的ブーストの例
public enum PersonType {
NORMAL,
VIP
}
@Entity
@Indexed
@DynamicBoost(impl = VIPBoostStrategy.class)
public class Person {
private PersonType type;
// ....
}
public class VIPBoostStrategy implements BoostStrategy {
public float defineBoost(Object value) {
Person person = ( Person ) value;
if ( person.getType().equals( PersonType.VIP ) ) {
return 2.0f;
}
else {
return 1.0f;
}
}
}BoostStrategy インターフェースの実装として VIPBoostStrategy を指定するクラスレベルで定義されます。クラスレベルまたはフィールドレベルのどちらかに @DynamicBoost を配置することができます。アノテーションの配置により、エンティティ全体が defineBoost メソッドに渡されるか、アノテートされたフィールド/プロパティの値だけか異なります。渡されたオブジェクトを正しい種類にキャストするのはユーザー次第です。例では、重要人物が持つインデックス値はすべて、通常の人の値と比べ 2 倍重要になります。
注記
指定の
BoostStrategy の実装は、引数なしのパブリックコンストラクタを定義します。
@Boost と @DynamicBoost アノテーションを適合することができます。すべての定義されたブーストファクタは、「ブーストファクタ」 に記載のとおり累積されます。
4.1.6. アナライザ
トークン化フィールドのインデックス化に使用されるデフォルトのアナライザクラスは
hibernate.search.analyzer プロパティから設定することができます。 このプロパティのデフォルト値は org.apache.lucene.analysis.standard.StandardAnalyzer です。
また、エンティティ、プロパティ、@Field (単一のプロパティから複数のフィールドをインデックス化する場合に役に立ちます) ごとにアナライザクラスを定義することもできます。
例4.9 アナライザを指定するさまざまな方法
@Entity @Indexed @Analyzer(impl = EntityAnalyzer.class) public class MyEntity { @Id @GeneratedValue @DocumentId private Integer id; @Field(index = Index.TOKENIZED) private String name; @Field(index = Index.TOKENIZED) @Analyzer(impl = PropertyAnalyzer.class) private String summary; @Field(index = Index.TOKENIZED, analyzer = @Analyzer(impl = FieldAnalyzer.class) private String body; ... }
この例では、すべてのトークン化プロパティ (
name など) をインデックス化するために EntityAnalyzer が使用されます (それぞれ PropertyAnalyzer と FieldAnalyzer でインデックス化される summary と body を除く)。
重要
ほとんどの場合、同じエンティティに異なるアナライザを混在させることは推奨されません。これにより、クエリの構築が複雑になり、結果が (初心者にとって) 予測不可能になります (特に QueryParser (クエリ全体に対して同じアナライザを使用) を使用する場合)。原則として、あるフィールドに対して、インデックス化と問い合わせのためにアナライザを使用する必要があります。
4.1.6.1. アナライザ定義
アナライザの使用は非常に複雑になることがあるため、Hibernate Search にはアナライザ定義の概念が導入されました。アナライザ定義は、多くの
@Analyzer 宣言によって再利用できます。アナライザ定義は以下のものから構成されます。
- 名前: 定義を参照するために使用される一意の文字列
- トークナイザ: 個々のワードに入力ストリームをトークン化する
- フィルタのリスト: 各フィルタはトークナイザにより提供されたストリームに対してワードを削除、変更、または場合によっては追加する
このようにタスクを分離することにより (トークナイザとフィルタのリスト)、各コンポーネントを簡単に再利用し、非常に柔軟に (レゴブロックのように) カスタマイズアナライザを構築できます。一般的に、
Tokenizer は入力された文字をトークン (さらに TokenFilter によって処理される) に変換することによって分析プロセスを開始します。Hibernate Search は、Solr アナライザフレームワークを使用してこのインフラストラクチャをサポートします。アナライザ定義を使用するためにクラスパスに solr-core.jar and solr-common.jar を追加してください。スノーボールステマーも使用する場合は、lucene-snowball.jar. も含めてください。他の Solr アナライザはさらに多くのライブラリに依存することがあります。たとえば、PhoneticFilterFactory は commons-codec に依存します。Hibernate Search のディストリビューションは、これらの依存関係を lib ディレクトリで提供します。
例4.10 @AnalyzerDef および Solr フレームワーク
@AnalyzerDef(name="customanalyzer",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = ISOLatin1AccentFilterFactory.class),
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = StopFilterFactory.class, params = {
@Parameter(name="words", value= "org/hibernate/search/test/analyzer/solr/stoplist.properties" ),
@Parameter(name="ignoreCase", value="true")
})
})
public class Team {
...
}
トークナイザは、トークナイザを構築子、オプションのパラメータリストを使用するファクトリにより定義されます。この例では、標準的なトークナイザを使用します。フィルタはオプションのパラメータを使用してフィルタインスタンスを作成するファクトリにより定義されます。この例では、StopFilter フィルタが構築され、専用のワードプロパティファイルを読み取り、大文字と小文字を区別しません。パラメータのリストはトークナイザまたはフィルタファクトリに依存します。
警告
フィルタは
@AnalyzerDef アノテーションで定義された順序で適用されます。この順序についてよく考えてください。
定義されたアナライザ定義は、
@Analyzer 宣言により実装クラスを宣言するのではなく定義名を使用して再利用できます。
例4.11 名前によるアナライザの参照
@Entity
@Indexed
@AnalyzerDef(name="customanalyzer", ... )
public class Team {
@Id
@DocumentId
@GeneratedValue
private Integer id;
@Field
private String name;
@Field
private String location;
@Field @Analyzer(definition = "customanalyzer")
private String description;
}@AnalyzerDef により宣言されたアナライザインスタンスは SearchFactory の名前で利用できます。
Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer("customanalyzer");
これは、クエリを構築するときに非常に役に立ちます。クエリのフィールドは、フィールドをインデックス化するのに使用されたのと同じアナライザで分析する必要があります (したがって、これらは共通の「言語」を話します。クエリとインデックス化プロセス間で同じトークンが再利用されます)。このルールにはいくつかの例外がありますが、ほとんどの場合該当します。特に何を行うかがわかっていない限り、このルールに従ってください。
4.1.6.2. 利用可能なアナライザ
Solr と Lucene にはたくさんの役に立つデフォルトのトークナイザとフィルタが含まれます。トークナイザファクトリとフィルタファクトリの完全なリストは http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters で見つけることができます。これらのいくつかについて確認してみましょう。
表4.1 利用可能なトークナイザの一部
| ファクトリ | 説明 | パラメータ |
|---|---|---|
| StandardTokenizerFactory | Lucene 標準トークナイザを使用する | none |
| HTMLStripStandardTokenizerFactory | HTML タグを削除し、テキストを保持し、StandardTokenizer に渡す | none |
表4.2 利用可能なフィルタの一部
| ファクトリ | 説明 | パラメータ |
|---|---|---|
| StandardFilterFactory | 頭字語とワードからドットを削除する | none |
| LowerCaseFilterFactory | 小文字のワード | none |
| StopFilterFactory | ストップワードのリストに一致するワード (トークン) を削除する | words: ストップワードを含むリソースファイルを参照する
ignoreCase: ストップワードを比較するときに
case を無視する場合は true、その他の場合は false
|
| SnowballPorterFilterFactory | ワードを特定の言語の語幹に減らす (たとえば、protect、protects、protection は同じ語幹を共有する)。このようなフィルタを使用すると、関連ワードに一致する検索ができる。 | language: Danish、Dutch、English、Finnish、French、German、Italian、Norwegian、Portuguese、Russian、Spanish、Swedish
|
| ISOLatin1AccentFilterFactory | フランス語のような言語のアクセントを削除する | none |
IDE の
org.apache.solr.analysis.TokenizerFactory、org.apache.solr.analysis.TokenFilterFactory のすべての実装をチェックして利用可能な実装を確認することをお薦めします。
4.1.6.3. アナライザ判別子 (実験段階)
これまで説明したアナライザの指定方法はすべて静的でした。ただし、インデックス化するエンティティの現在の状態に応じてアナライザを選択することが役に立つ場合があります (マルチリンガルアプリケーションなど)。たとえば、
BlogEntry クラスの場合、アナライザは、エンティティの言語プロパティに依存することがあります。このプロパティに応じて、実際のテキストをインデックス化するために適切な言語固有ステマーを選択する必要があります。
この動的アナライザ選択を有効にするために、Hibernate Search には
AnalyzerDiscriminator アノテーションが導入されました。以下の例は、このアノテーションの使用方法を示しています。
例4.12 アナライザを選択するための @AnalyzerDiscriminator の使用方法はエンティティステータスによって異なる
@Entity
@Indexed
@AnalyzerDefs({
@AnalyzerDef(name = "en",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = EnglishPorterFilterFactory.class
)
}),
@AnalyzerDef(name = "de",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = GermanStemFilterFactory.class)
})
})
public class BlogEntry {
@Id
@GeneratedValue
@DocumentId
private Integer id;
@Field
@AnalyzerDiscriminator(impl = LanguageDiscriminator.class)
private String language;
@Field
private String text;
private Set<BlogEntry> references;
// standard getter/setter
...
}public class LanguageDiscriminator implements Discriminator {
public String getAnanyzerDefinitionName(Object value, Object entity, String field) {
if ( value == null || !( entity instanceof Article ) ) {
return null;
}
return (String) value;
}
}@AnalyzerDiscriminator を使用する前提条件は、使用されるすべてのアナライザが @AnalyzerDef 定義で事前に定義されていることです。これに該当する場合は、@AnalyzerDiscriminator アノテーションをクラス、またはアナライザを動的に選択するエンティティの特定のプロパティに配置できます。AnalyzerDiscriminator の impl パラメータを使用して、Discriminator インターフェイスの具体的な実装を指定します。実装する必要がある唯一のメソッドは、Lucene ドキュメントに追加される各フィールドに対して呼び出される getAnanyzerDefinitionName() です。インデックス化されるエンティティは、インターフェイスメソッドにも渡されます。value パラメータは、AnalyzerDiscriminator がクラスレベルではなくプロパティレベルに配置された場合にのみ設定されます。この場合、値はこのプロパティ現在の値を表します。
Discriminator インターフェイスの実装は、アナライザを動的に設定する場合は既存のアナライザ定義の名前、デフォルトのアナライザをオーバーライドしない場合は null を返す必要があります。示された例では、言語パラメータが @AnalyzerDef で指定された名前に一致する 'de' または 'en' のいずれかであることを前提とします。
注記
@AnalyzerDiscriminator は現在まだ実験段階にあり、API は変わる可能性があります。この機能の利便性と使用感についてコミュニティからのフィードバックを募集しています。
4.1.6.4. アナライザの取得
インデックス作成中に、Hibernate Search は背後でアナライザを使用します。場合によっては、アナライザの取得が便利です。ドメインモデルが複数のアナライザを使用する場合 (ステミングのメリットを得るための音声近似の使用など)、クエリを構築する場合に同じアナライザを使用する必要があります。
注記
このルールに従わないこともできますが、その場合は理由が必要です。よくわからない場合は、同じアナライザを使用してください。
Hibernate Search によりインデックス作成時に使用される特定のエンティティに対してスコープ設定されたアナライザを取得できます。スコープ設定されたアナライザは、インデックス化されたフィールドに応じて適切なアナライザを適用するアナライザです。複数のアナライザは特定のエンティティ上で定義できます (それぞれは個々のフィールド上で動作します)。スコープ設定されたアナライザはこれらすべてのアナライザを 1 つのコンテキスト認識アナライザに統一します。理論的に少し複雑ですが、クエリで適切なアナライザを使用することは非常に簡単です。
例4.13 完全テキストクエリの構築時にスコープ設定されたアナライザを使用
org.apache.lucene.queryParser.QueryParser parser = new QueryParser(
"title",
fullTextSession.getSearchFactory().getAnalyzer( Song.class )
);
org.apache.lucene.search.Query luceneQuery =
parser.parse( "title:sky Or title_stemmed:diamond" );
org.hibernate.Query fullTextQuery =
fullTextSession.createFullTextQuery( luceneQuery, Song.class );
List result = fullTextQuery.list(); //return a list of managed objects
上記の例では、歌のタイトルは 2 つのフィールドでインデックス化されます。標準的なアナライザはフィールド
title で使用され、ステミングアナライザはフィールド title_stemmed で使用されます。検索ファクトリにより提供されたアナライザを使用することにより、クエリは対象となるフィールドに応じて適切なアナライザを使用します。
クエリが複数のクエリを対象とし、標準的なアナライザを使用する場合、アナライザ定義を使用してそれを定義する必要があります。アナライザは、
searchFactory.getAnalyzer(String) を使用して定義により取得できます。