4.4. リモートクエリー
クライアントからアクセスする Data Grid Sever クラスターを設定する場合は、リモートクエリーを使用します。
リモートクエリーを実行するには、キャッシュ内のデータで、ネットワーク上の送信とストレージ両方のエンコーディングとして Google Protocol Buffers を使用する必要があります。さらに、リモートクエリーでは、データ構造とインデックス要素を定義するために Protobuf スキーマ (.proto ファイル) が必要です。
リモートクエリーで Protobuf を使用する利点は、言語に依存せず、Hot Rod Java クライアントだけでなく、REST、C ++、C#、および Node.js クライアントでも機能することです。
4.4.1. リモートクエリーの例 リンクのコピーリンクがクリップボードにコピーされました!
Book と呼ばれるオブジェクトは、books と呼ばれる Data Grid キャッシュに保存されます。Book インスタンスはインデックス化されるため、キャッシュのインデックスを有効にします。
infinispan.xml
<replicated-cache name="books">
<indexing>
<indexed-entities>
<indexed-entity>book_sample.Book</indexed-entity>
</indexed-entities>
</indexing>
</replicated-cache>
または、キャッシュにインデックスが付けられていない場合は、<encoding> を application/x-protostream として設定して、ストレージがクエリー可能であることを確認します。
infinispan.xml
<replicated-cache name="books">
<encoding media-type="application/x-protostream"/>
</replicated-cache>
各 Book は以下の例のように定義されます。@Protofield アノテーションを使用してメッセージフィールドを識別し、フィールドの @ProtoDoc アノテーションを使用してインデックス属性を設定します。
Book.java
import org.infinispan.protostream.annotations.ProtoDoc;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
@ProtoDoc("@Indexed")
public class Book {
@ProtoDoc("@Field(index=Index.YES, analyze = Analyze.YES, store = Store.NO)")
@ProtoField(number = 1)
final String title;
@ProtoDoc("@Field(index=Index.YES, analyze = Analyze.YES, store = Store.NO)")
@ProtoField(number = 2)
final String description;
@ProtoDoc("@Field(index=Index.YES, analyze = Analyze.YES, store = Store.NO)")
@ProtoField(number = 3, defaultValue = "0")
final int publicationYear;
@ProtoFactory
Book(String title, String description, int publicationYear) {
this.title = title;
this.description = description;
this.publicationYear = publicationYear;
}
// public Getter methods omitted for brevity
}
コンパイル時に、前述の例のアノテーションは、Book インスタンスの読み取り、書き込み、およびクエリーに必要なアーティファクトを生成します。この生成を有効にするには、空のコンストラクターまたはインターフェイスを使用して新しく作成されたクラスで @AutoProtoSchemaBuilder アノテーションを使用します。
RemoteQueryInitializer.java
import org.infinispan.protostream.SerializationContextInitializer;
import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
@AutoProtoSchemaBuilder(
includeClasses = {
Book.class
},
schemaFileName = "book.proto",
schemaFilePath = "proto/",
schemaPackageName = "book_sample")
public interface RemoteQueryInitializer extends SerializationContextInitializer {
}
コンパイル後、ファイル book.proto ファイルが、アノテーションが付いたインターフェイスの実装 RemoteQueryInitializerImpl.java とともに、設定された schemaFilePath に作成されます。この具体的なクラスは Hot Rod クライアントコードで直接使用して、シリアル化コンテキストを初期化します。
すべてを 1 つにまとめます。
RemoteQuery.java
package org.infinispan;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.Search;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants;
public class RemoteQuery {
public static void main(String[] args) throws Exception {
ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
// RemoteQueryInitializerImpl is generated
clientBuilder.addServer().host("127.0.0.1").port(11222)
.security().authentication().username("user").password("user")
.addContextInitializers(new RemoteQueryInitializerImpl());
RemoteCacheManager remoteCacheManager = new RemoteCacheManager(clientBuilder.build());
// Grab the generated protobuf schema and registers in the server.
Path proto = Paths.get(RemoteQuery.class.getClassLoader()
.getResource("proto/book.proto").toURI());
String protoBufCacheName = ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME;
remoteCacheManager.getCache(protoBufCacheName).put("book.proto", Files.readString(proto));
// Obtain the 'books' remote cache
RemoteCache<Object, Object> remoteCache = remoteCacheManager.getCache("books");
// Add some Books
Book book1 = new Book("Infinispan in Action", "Learn Infinispan with using it", 2015);
Book book2 = new Book("Cloud-Native Applications with Java and Quarkus", "Build robust and reliable cloud applications", 2019);
remoteCache.put(1, book1);
remoteCache.put(2, book2);
// Execute a full-text query
QueryFactory queryFactory = Search.getQueryFactory(remoteCache);
Query<Book> query = queryFactory.create("FROM book_sample.Book WHERE title:'java'");
List<Book> list = query.execute().list(); // Voila! We have our book back from the cache!
}
}
4.4.2. Protobuf スキーマの登録 リンクのコピーリンクがクリップボードにコピーされました!
protobuf エンティティーをクエリーするには、クライアントおよびサーバーに、Protobuf スキーマ (.proto ファイル) のエンティティーに関連するメタデータを提供する必要があります。
記述子はサーバーの専用の ___protobuf_metadata キャッシュに保存されます。このキャッシュのキーと値はどちらもプレーンテキストの文字列です。したがって、新しいスキーマの登録は、スキーマ名をキーとして、スキーマファイル自体を値として使用して、このキャッシュに対して put() 操作を実行するのと同じくらい簡単です。
キャッシュが承認を使用する場合は、__protobuf_metadata キャッシュにエントリーを追加するのに、CREATE パーミッションが必要です。デフォルトの承認設定を使用する場合は、ユーザーに少なくとも deployer ロールを割り当てます。
または、Data Grid CLI、Data Grid Console、REST エンドポイント /rest/v2/schemas 、または JMX 経由の ProtobufMetadataManager MBean で schema コマンドを使用できます。
キャッシュに対してインデックスが有効になっている場合でも、 Protobuf スキーマドキュメントアノテーション (@ProtoDoc) 内の @Indexed および @Field を使用してインデックスする必要のあるフィールドを指定しない限り、Protobuf でエンコードされたエントリーのフィールドはインデックス付けされません。
4.4.3. 分析 リンクのコピーリンクがクリップボードにコピーされました!
分析は、入力データを、インデックスを作成してクエリーできる 1 つ以上の用語に変換するプロセスです。Embedded Query のマッピングは、Lucene ベースのアナライザーのセットをサポートする Hibernate Search アノテーション を介して行われますが、クライアントサーバーモードでは、アナライザー定義はプラットフォームに依存しない方法で宣言されます。
デフォルトのアナライザー
Data Grid は、以下のようにリモートクエリーに対してデフォルトのアナライザーのセットを提供します。
| 定義 | 説明 |
|---|---|
|
| テキストフィールドをトークンに分割し、空白と句読点を区切り文字として扱います。 |
|
| 非文字で区切り、すべての文字を小文字に変換することにより、入力ストリームをトークン化します。空白と非文字は破棄されます。 |
|
| テキストストリームを空白で分割し、空白以外の文字のシーケンスをトークンとして返します。 |
|
| テキストフィールド全体を単一トークンとして扱います。 |
|
| SnowballPorter フィルターを使用して英語の単語を語幹にします。 |
|
| デフォルトでサイズ 3 つのグラムである n-gram トークンを生成します。 |
|
|
テキストフィールドを |
これらのアナライザー定義は Apache Lucene をベースとし、as-is で提供されます。tokenizers、filters、および CharFilters に関する詳細は、適切な Lucene のドキュメントを参照してください。
アナライザー定義の使用
アナライザー定義を使用するには、.proto スキーマファイルで名前でそれらを参照します。
-
Analyze.YES属性を追加して、プロパティーが分析されていることを示します。 -
@Analyzerアノテーションで アナライザー定義を指定します。
以下は、参照されたアナライザー定義の例になります。
/* @Indexed */
message TestEntity {
/* @Field(store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "keyword")) */
optional string id = 1;
/* @Field(store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "simple")) */
optional string name = 2;
}
@ProtoField アノテーションが付けられた Java クラスを使用する場合、宣言は以下のようになります。
@ProtoDoc("@Field(store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = \"keyword\"))")
@ProtoField(number = 1)
final String id;
@ProtoDoc("@Field(store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = \"simple\"))")
@ProtoField(number = 2)
final String description;
カスタムアナライザー定義の作成
カスタムアナライザー定義が必要な場合は、以下を行います。
-
JARファイルにパッケージ化されたProgrammaticSearchMappingProviderインターフェイスの実装を作成します。 -
JARのMETA-INF/services/ディレクトリーにorg.infinispan.query.spi.ProgrammaticSearchMappingProviderという名前のファイルを指定します。このファイルには、実装の完全修飾クラス名が含まれている必要があります。 JARを Data Grid Server インストールのlib/ディレクトリーにコピーします。重要JAR は、起動時に Data Grid Server で利用できる必要があります。実行しているサーバーに追加することはできません。
以下は、ProgrammaticSearchMappingProvider インターフェイスの実装例です。
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
import org.apache.lucene.analysis.core.StopFilterFactory;
import org.apache.lucene.analysis.standard.StandardFilterFactory;
import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
import org.hibernate.search.cfg.SearchMapping;
import org.infinispan.Cache;
import org.infinispan.query.spi.ProgrammaticSearchMappingProvider;
public final class MyAnalyzerProvider implements ProgrammaticSearchMappingProvider {
@Override
public void defineMappings(Cache cache, SearchMapping searchMapping) {
searchMapping
.analyzerDef("standard-with-stop", StandardTokenizerFactory.class)
.filter(StandardFilterFactory.class)
.filter(LowerCaseFilterFactory.class)
.filter(StopFilterFactory.class);
}
}