Coherenceでは、現在キャッシュされている、一定の基準を満たすデータに対して問合せおよび索引付けを実行できます。問合せおよび索引付けは、Coherenceにパッケージ化されているフィルタを採用して簡単に実行できるほか、コレクションや配列などの複数値属性に対して実行することもできます。
Coherenceには、一定の基準を満たすキャッシュ・エントリを検索する機能があります。結果セットは、必要に応じてソートできます。問合せは、コミット読取り分離レベルで評価されます。
問合せは、現在キャッシュされているデータにしか適用されないこと(CacheLoaderインタフェースを使用して問合せ基準を満たす追加データを取得しないこと)に注意してください。したがって、問合せを実行する前に、データセット全体をキャッシュにロードする必要があります。データセットが大きすぎて使用可能なメモリーに収まらない場合は、特定のディメンション(日付など)でキャッシュ・コンテンツを制限し、問合せの構造に基づいてキャッシュ問合せとデータベース問合せの間を手動で切り替えることができます。保守性の観点から、通常はキャッシュ対応のデータ・アクセス・オブジェクト(DAO)内に実装することをお薦めします。
索引付けには、パーティション・キャッシュ・ノードごとに属性を抽出する機能が必要です。専用のCacheServerインスタンスの場合、これは(通常)、アプリケーション・クラスをCacheServerクラスパスにインストールする必要があることを意味します。
ローカル・キャッシュおよびレプリケーション・キャッシュの場合、問合せは索引付けされていないデータに対してローカルで評価されます。パーティション・キャッシュの場合、問合せは、使用可能であれば索引を使用して、クラスタ全体でパラレル実行されます。Coherenceには、コストベース・オプティマイザ(CBO)が含まれています。索引付けされていない属性にアクセスするには、オブジェクトのデシリアライズが必要です(ただし、他の属性に索引付けすると、評価が必要なオブジェクトの数を削減できる)。
キャッシュ・コンテンツの問合せは非常に簡単です。
例6-1 フィルタを指定したキャッシュの問合せ
Filter filter = new GreaterEqualsFilter("getAge", 18);
for (Iterator iter = cache.entrySet(filter).iterator(); iter.hasNext(); )
{
Map.Entry entry = (Map.Entry) iter.next();
Integer key = (Integer) entry.getKey();
Person person = (Person) entry.getValue();
System.out.println("key=" + key + " person=" + person);
}
Coherenceのcom.tangosol.util.filterパッケージには、各種フィルタが用意されています。
LimitFilterを使用すると、クライアントに送信するデータ量が制限されると同時に、ページングが使用可能になります。これは、例6-2に示されています。
例6-2 LimitFilterクラスを使用した、クライアントに送信するデータ量の制限
int pageSize = 25;
Filter filter = new GreaterEqualsFilter("getAge", 18);
// get entries 1-25
Filter limitFilter = new LimitFilter(filter, pageSize);
Set entries = cache.entrySet(limitFilter);
// get entries 26-50
limitFilter.nextPage();
entries = cache.entrySet(limitFilter);
問合せ可能属性はすべて、QueryMapクラスのaddIndexメソッドを使用して索引付けできます。これは、例6-3に示されています。
例6-3 問合せ可能属性の索引付け
// addIndex(ValueExtractor extractor, boolean fOrdered, Comparator comparator) cache.addIndex(extractor, true, null);
fOrdered引数は、索引の構造をソートするかどうかを指定します。ソートされた索引は、2つの日付の間に入るすべてのエントリを選択、姓がSで始まるすべての従業員を選択など、範囲の問合せに便利です。等価の問合せの場合は、順序付けされていない索引も使用可能で、そのほうが容量や時間の点でより効率的な場合があります。
comparator引数を使用すると、索引の順序付け用にカスタムのjava.util.Comparatorを指定できます。
このメソッドは、キャッシュ実装のヒントになることのみを目的としているため、索引がサポートされていない場合や、必要な索引(または同様の索引)がすでに存在する場合は無視されることがあります。索引がすでに存在する場合でも、索引が推奨されていることを確認するだけの目的で、アプリケーションでこのメソッドがコールされることが予測されます。たとえば、分散環境では、各サーバーが起動時に同じ索引セットを推奨する可能性が高いため、別のサーバーが同じ索引をリクエスト済かどうかにかかわらず、これらの索引を無条件にリクエストしたとしても、アプリケーションにとってマイナス面はありません。
索引は、Coherence Enterprise Edition以降の機能です。このメソッドは、Coherence Standard Editionを使用している場合は機能しません。
問合せは、必要に応じてCoherenceで組み合せることができます。また、Coherenceには、索引の使用に優先順位を付けるためのコストベース・オプティマイザ(CBO)が含まれています。索引を利用するには、問合せで使用されているものと等価のエクストラクタ((Object.equals())を使用する必要があります。
適用される索引のリストは、StorageManagerMBeanから、JMXを使用して取得できます。詳細は、第21章「JMXを使用したCoherenceの管理方法」を参照してください。
パーティション・キャッシュでは、Coherence Enterprise Edition以降でのみ使用可能なパラレル問合せ機能を使用して、このメソッドを実装します。Coherence Standard Editionでパーティション・キャッシュを処理する場合、このメソッドは、クライアントに設定したデータを取得して処理します。
問合せは、ニア・キャッシュを使用して実行できますが、ニア・キャッシュのフロント部分は使用しません。問合せでニア・キャッシュを使用する場合は、例6-4の順序どおりに使用することをお薦めします。
この項では、問合せインタフェースの設計の詳細を、コア・コンポーネントから説明します。
問合せの概念は、ValueExtractorインタフェースに基づいています。値エクストラクタを使用して、所定のオブジェクトから問合せ用に属性を抽出します(索引付けの場合も同様)。ほとんどの開発者にとって、必要なのはこのインタフェースのReflectionExtractor実装のみです。ReflectionExtractorは、リフレクションを使用してメソッド名(通常はgetName()のようなgetterメソッド)を参照することにより、値オブジェクトから属性を抽出します。
ValueExtractor extractor = new ReflectionExtractor("getName");
toString()のようなObjectメソッド(プロトタイプやデバッグに便利)を含む、任意のvoid引数メソッドを使用できます。索引は、従来のフィールド索引(オブジェクトの索引付けされたフィールド)でも、関数索引(仮想オブジェクト属性の索引付け)でもかまいません。たとえば、getFirstNameおよびgetLastNameのフィールド・アクセッサを持つクラスの場合は、これらの名前を連結する関数getFullNameを定義して、この関数を索引付けできます。
getName属性を持つオブジェクトを含むキャッシュに対して問合せを実行するには、Filterを使用する必要があります。1つのフィルタには、所定のオブジェクトが基準を満たすかどうかを特定するメソッドが1つ含まれています。
フィルタには、メソッド名を受け入れてReflectionExtractorを内部で構成する、便利なコンストラクタも含まれています。
例6-7は、特定のフィルタ基準を満たすキャッシュ・エントリを選択するルーチンを示しています。
例6-7 フィルタ基準を満たすキャッシュ・エントリの選択
for (Iterator iter = cache.entrySet(filter).iterator(); iter.hasNext(); )
{
Map.Entry entry = (Map.Entry)iter.next();
Integer key = (Integer)entry.getKey();
Person person = (Person)entry.getValue();
System.out.println("key=" + key + " person=" + person);
}
例6-8は、フィルタを使用してキャッシュ・エントリを選択およびソートする方法を示しています。
例6-8 フィルタ基準を満たすキャッシュ・エントリの選択およびソート
// entrySet(Filter filter, Comparator comparator) Iterator iter = cache.entrySet(filter, null).iterator();
追加したnull引数は、キャッシュ内のComparableオブジェクトの自然な順序を使用して結果セットをソートする必要があることを指定します。クライアントでは、Comparatorの実装を用意することによって、結果セットの順序を明示的に指定する場合があります。ソートを行うと、Coherenceで適用可能な最適化が大幅に制限されることに注意してください。これは、ソート前に結果セット全体を使用可能にする必要があるためです。
例6-9は、keySet形式の問合せを、getAll()と組み合せて使用する方法を示しています。この方法により、メモリー使用量をより正確に制御できる場合があります。
例6-9 keySet問合せ形式の使用
// keySet(Filter filter)
Set setKeys = cache.keySet(filter);
Set setPageKeys = new HashSet();
int PAGE_SIZE = 100;
for (Iterator iter = setKeys.iterator(); iter.hasNext();)
{
setPageKeys.add(iter.next());
if (setKeyPage.size() == PAGE_SIZE || !iter.hasNext())
{
// get a block of values
Map mapResult = cache.getAll(setPageKeys);
// process the block
// ...
setPageKeys.clear();
}
}
Coherenceでは、コレクションや配列を含む、複数値属性の索引付けおよび問合せがサポートされています。オブジェクトが索引付けされると、これが複数値タイプであるかどうかが検証されてから、シングルトンではなくコレクションとして索引付けされます。これらのコレクションに対する問合せには、ContainsAllFilter、ContainsAnyFilterおよびContainsFilterを使用します。
例6-10 複数値属性の問合せ
Set searchTerms = new HashSet();
searchTerms.add("java");
searchTerms.add("clustering");
searchTerms.add("books");
// The cache contains instances of a class "Document" which has a method
// "getWords" which returns a Collection<String> containing the set of
// words that appear in the document.
Filter filter = new ContainsAllFilter("getWords", searchTerms);
Set entrySet = cache.entrySet(filter);
// iterate through the search results
// ...
ChainedExtractor実装を使用すると、引数なし(アクセッサ)のメソッドを連鎖起動できます。例6-11では、まずエクストラクタがリフレクションを使用して、キャッシュされているPersonオブジェクトごとにgetName()をコールします。その後、リフレクションを再度使用して、返されたStringでlength()をコールします。
このエクストラクタを問合せに渡すと、たとえば、名前が10文字以内の人全員を選択することができます。メソッドの起動は、getName.trim.lengthのように無限に連鎖できます。