SolrJ
SolrJ は、Java(または JVM ベースの任意の言語)で記述されたアプリケーションが Solr と簡単に通信できるようにする API です。SolrJ は Solr への接続の詳細を多く隠蔽し、アプリケーションがシンプルな高レベルメソッドを使用して Solr と対話できるようにします。SolrJ はほとんどの Solr API をサポートしており、高度に構成可能です。
SolrJ アプリケーションのビルドと実行
SolrJ API は Solr に同梱されているため、他に何もダウンロードまたはインストールする必要はありません。ただし、SolrJ とその依存関係を含めるようにビルドを設定する必要があります。
一般的なビルドシステム
ほとんどの主流のビルドシステムは依存関係管理を大幅に簡素化し、SolrJ をプロジェクトに追加しやすくなっています。
Ant(Ivy 使用)でビルドされたプロジェクトの場合、次のものを `ivy.xml` に配置します。
<dependency org="org.apache.solr" name="solr-solrj" rev="9.5.0"/>
Maven でビルドされたプロジェクトの場合、次のものを `pom.xml` に配置します。
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>9.5.0</version>
</dependency>
Gradle でビルドされたプロジェクトの場合、次のものを `build.gradle` に配置します。
compile group: 'org.apache.solr', name: 'solr-solrj', version: '9.5.0'
`CloudSolrClient` を使用し、ZooKeeper と直接通信する場合、`solr-solrj-zookeeper` アーティファクトへの依存関係を追加する必要があります。
ストリーミング式 クラスを Java コードで使用していない場合は、`solr-solrj-streaming` 依存関係を除外できます。
SolrJ のクラスパスへの手動追加
上記のビルドシステムを使用していない場合でも、SolrJ をビルドに追加するのは簡単です。
ビルド時には、SolrJ jar 自体(`solr-solrj-9.5.0.jar`)のみが必要です。SolrJ を使用するコードを手動でコンパイルするには、次のような `javac` コマンドを使用します。
javac -cp .:$SOLR_TIP/server/solr-webapp/webapp/WEB-INF/lib/solr-solrj-9.5.0.jar ...
実行時には、SolrJ 自体に加えて、SolrJ の依存関係がいくつか必要です。Solr 配布版では、これらの依存関係は Solr の依存関係から分離されていないため、すべてを含めるか、必要とされる正確なセットを手動で選択する必要があります。使用するバージョンに必要な正確な依存関係については、maven リリース を参照してください。次のようなクラスパスでプロジェクトを実行します。
java -cp .:$SOLR_TIP/server/lib/ext:$SOLR_TIP/server/solr-webapp/webapp/WEB-INF/lib/* ...
SolrJ ライブラリによってクライアントアプリケーションのサイズが大きくなることを懸念する場合は、ProGuard などのコード難読化ツールを使用して、使用していない API を削除できます。
SolrJ の概要
その柔軟性にもかかわらず、SolrJ はいくつかのシンプルなインターフェースを中心に構築されています。
Solr へのすべてのリクエストは、SolrClient
によって送信されます。SolrClient は SolrJ のコアにある主要な作業馬であり、Solr への接続と通信の処理を行い、ユーザー設定の大部分がここで行われます。
リクエストは SolrRequests
の形式で送信され、SolrResponses
として返されます。
SolrClients の種類
SolrClient
にはいくつかの具体的な実装があり、それぞれ異なる使用パターンまたは回復力モデルを対象としています。
-
HttpSolrClient
- クエリ中心のワークロードを対象としていますが、汎用クライアントとしても優れています。単一の Solr ノードと直接通信します。 -
Http2SolrClient
- HTTP/2 を活用した非同期、ノンブロッキング、汎用クライアントです。このクラスは実験的なものであるため、API は SolrJ のマイナーバージョンで変更または削除される可能性があります。 -
LBHttpSolrClient
- Solrノードのリスト全体にリクエスト負荷を分散します。ノードのヘルスに基づいて、「稼働中」ノードのリストを調整します。 -
LBHttp2SolrClient
-LBHttpSolrClient
と同様ですが、Http2SolrClient
を使用します。このクラスは実験的なものであるため、APIはSolrJのマイナーバージョンで変更または削除される可能性があります。 -
CloudSolrClient
- SolrCloudデプロイメントとの通信を目的としています。既に記録されたZooKeeperの状態を使用して、ヘルシーなSolrノードを検出し、リクエストをルーティングします。 -
ConcurrentUpdateSolrClient
- インデックス作成中心のワークロードを目的としています。Solrに大量のバッチを送信する前に、内部的にドキュメントをバッファリングします。 -
ConcurrentUpdateHttp2SolrClient
-ConcurrentUpdateSolrClient
と同様ですが、Http2SolrClient
を使用します。このクラスは実験的なものであるため、APIはSolrJのマイナーバージョンで変更または削除される可能性があります。
共通の構成オプション
SolrJの構成の大部分は、SolrClient
レベルで行われます。最も一般的/重要なものについては以下で説明します。SolrClient
を調整する方法に関する包括的な情報については、関連するクライアントとその対応するビルダーオブジェクトのJavadocを参照してください。
基本URL
ほとんどのSolrClient
実装(CloudSolrClient
とHttp2SolrClient
を除く)では、ユーザーは1つ以上のSolr基本URLを指定する必要があります。クライアントはこれを使用してSolrにHTTPリクエストを送信します。ユーザーが提供する基本URLに含めるパスは、作成されたクライアントの動作に影響します。
-
特定のコアまたはコレクションを指すパスを含むURL(例:
http://hostname:8983/solr/core1
)。基本URLでコアまたはコレクションが指定されている場合、そのクライアントで行われる後続のリクエストでは、影響を受けるコレクションを再指定する必要はありません。ただし、クライアントは、そのコア/コレクションへのリクエストの送信に限定され、他のコア/コレクションへのリクエストを送信することはできません。 -
ルートSolrパスを指すURL(例:
http://hostname:8983/solr
)。基本URLでコアまたはコレクションが指定されていない場合、任意のコア/コレクションにリクエストを行うことができますが、影響を受けるコア/コレクションはすべてのリクエストで指定する必要があります。
一般的に、SolrClient
を単一のコア/コレクションでのみ使用する場合は、そのエンティティをパスに含めるのが最も便利です。より柔軟性が求められる場合は、コレクション/コアを除外する必要があります。
Http2SolrClientの基本URL
Http2SolrClient
は、異なるノードへの接続を効率的に管理します。Http2SolrClient
はbaseUrl
を必要としません。baseUrl
が提供されない場合、SolrRequest.basePath
を設定する必要があります。これにより、Http2SolrClient
はリクエストを送信するノードを認識します。そうでない場合は、IllegalArgumentException
がスローされます。
CloudSolrClientの基本URL
CloudSolrClient
の基本URLを指定することもできますが、URLはルートSolrパスを指す必要があります(例:http://hostname:8983/solr
)。コレクション、コア、その他のパスコンポーネントを含めることはできません。
final List<String> solrUrls = new ArrayList<>();
solrUrls.add("http://solr1:8983/solr");
solrUrls.add("http://solr2:8983/solr");
return new CloudSolrClient.Builder(solrUrls).build();
baseUrl
が提供されない場合、ZooKeeperホスト(ポート付き)とZooKeeperルートのリストを提供する必要があります。ZooKeeperルートを使用しない場合は、メソッドの一部としてjava.util.Optional.empty()
を提供する必要があります。
final List<String> zkServers = new ArrayList<>();
zkServers.add("zookeeper1:2181");
zkServers.add("zookeeper2:2181");
zkServers.add("zookeeper3:2181");
return new CloudSolrClient.Builder(zkServers, Optional.empty()).build();
final List<String> zkServers = new ArrayList<>();
zkServers.add("zookeeper1:2181");
zkServers.add("zookeeper2:2181");
zkServers.add("zookeeper3:2181");
return new CloudSolrClient.Builder(zkServers, Optional.of("/solr")).build();
さらに、solr-solrj-zookeeper
アーティファクトに依存する必要があります。そうでない場合は、ClassNotFoundException
が発生します。
ZooKeeperベースの接続は、CloudSolrClientが動作するための最も信頼性が高く、パフォーマンスの高い手段です。一方、これは、Solrノード以外にZooKeeperをより広範囲に公開することを意味し、セキュリティリスクとなります。また、JARの依存関係も増えます。
タイムアウト
すべてのSolrClient
実装では、Solrとの通信の接続と読み取りのタイムアウトをユーザーが指定できます。これらは、以下の例のように、クライアント作成時に提供されます。
final String solrUrl = "https://127.0.0.1:8983/solr";
return new HttpSolrClient.Builder(solrUrl)
.withConnectionTimeout(10000, TimeUnit.MILLISECONDS)
.withSocketTimeout(60000, TimeUnit.MILLISECONDS)
.build();
これらの値が明示的に提供されない場合、SolrJは実行中のOS/環境のデフォルトを使用します。
ConcurrentUpdateSolrClient
とその対応物であるConcurrentUpdateHttp2SolrClient
は、応答しないノードへのリクエストを、ソケットタイムアウトを待つよりも早く失敗させることができるストール防止タイムアウトも実装しています。このタイムアウトのデフォルト値は15000msに設定されており、システムプロパティsolr.cloud.client.stallTime
で調整できます。この値はsolr.jetty.http.idleTimeout
(デフォルトは120000ms)よりも小さく、最大更新リクエストの処理時間よりも大きくする必要があります。
クラウドリクエストルーティング
SolrJのCloudSolrClient
実装(CloudSolrClient
およびCloudHttp2SolrClient
)は、shards.preferenceパラメーターを尊重します。したがって、上記のいずれかのクライアントを使用して単一シャードのコレクションに送信されたリクエストは、分散リクエストが個々のシャードにルーティングされるのと同じ方法でリクエストをルーティングします。shards.preference
パラメーターが提供されない場合、クライアントはレプリカをランダムにソートします。
更新リクエストの場合、レプリカはリクエストで定義された順序でソートされますが、リーダーレプリカは常に最初にソートされます。
SolrJでのクエリ
SolrClient
には、Solrから結果を取得するための多くのquery()
メソッドがあります。これらの各メソッドは、任意のクエリパラメーターをカプセル化するオブジェクトであるSolrParams
を受け取ります。そして、各メソッドは、結果ドキュメントおよびその他の関連メタデータにアクセスするために使用できるラッパーであるQueryResponse
を出力します。
次のスニペットは、SolrClientを使用してSolrの「techproducts」サンプルコレクションをクエリし、結果を反復処理します。
final SolrClient client = getSolrClient();
final Map<String, String> queryParamMap = new HashMap<>();
queryParamMap.put("q", "*:*");
queryParamMap.put("fl", "id, name");
queryParamMap.put("sort", "id asc");
MapSolrParams queryParams = new MapSolrParams(queryParamMap);
final QueryResponse response = client.query("techproducts", queryParams);
final SolrDocumentList documents = response.getResults();
print("Found " + documents.getNumFound() + " documents");
for (SolrDocument document : documents) {
final String id = (String) document.getFirstValue("id");
final String name = (String) document.getFirstValue("name");
print("id: " + id + "; name: " + name);
}
SolrParams
にはSolrQuery
サブクラスがあり、クエリ作成を大幅に簡素化するいくつかの便利なメソッドを提供します。次のスニペットは、前の例からのクエリを、SolrQuery
の便利なメソッドの一部を使用して構築する方法を示しています。
final SolrQuery query = new SolrQuery("*:*");
query.addField("id");
query.addField("name");
query.setSort("id", ORDER.asc);
query.setRows(numResultsToReturn);
SolrJでのインデックス作成
SolrJを使用すると、インデックス作成も簡単です。ユーザーは、インデックスを作成するドキュメントをSolrInputDocument
のインスタンスとして構築し、SolrClient
のadd()
メソッドのいずれかの引数として提供します。
次の例は、SolrJを使用してSolrの「techproducts」サンプルコレクションにドキュメントを追加する方法を示しています。
final SolrClient client = getSolrClient();
final SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", UUID.randomUUID().toString());
doc.addField("name", "Amazon Kindle Paperwhite");
final UpdateResponse updateResponse = client.add("techproducts", doc);
// Indexed documents must be committed
client.commit("techproducts");
上記のインデックス作成の例は、構文を示すことを目的としています。簡潔にするために、いくつかのSolrインデックス作成のベストプラクティスに違反しています。通常は、一度に1つずつではなく、より大きなバッチでドキュメントをインデックス作成する必要があります。また、Solr管理者は、明示的なcommit() 呼び出しを使用するのではなく、Solrの自動コミット設定を使用してドキュメントをコミットすることをお勧めします。 |
Javaオブジェクトバインディング
SolrJが提供するUpdateResponse
およびQueryResponse
インターフェースは便利ですが、アプリケーションでより簡単に理解できるドメイン固有のオブジェクトを使用する方が便利なことがよくあります。ありがたいことに、SolrJは、Field
アノテーションで特別にマークされたクラスとの間のドキュメントの暗黙的な変換をサポートしています。
Javaオブジェクトの各インスタンス変数は、Field
アノテーションを使用して対応するSolrフィールドにマップできます。Solrフィールドはデフォルトでアノテーション付き変数と同じ名前を共有しますが、明示的なフィールド名をアノテーションに提供することで上書きできます。
以下の例のスニペットは、Solrの「techproducts」サンプルコレクションからの結果を表すために使用できるアノテーション付きTechProduct
クラスを示しています。
public static class TechProduct {
@Field public String id;
@Field public String name;
public TechProduct(String id, String name) {
this.id = id;
this.name = name;
}
public TechProduct() {}
}
上記のアノテーション付きTechProduct
クラスにアクセスできるアプリケーションコードは、以下の例のスニペットのように、変換なしでTechProduct
オブジェクトを直接インデックス作成できます。
final SolrClient client = getSolrClient();
final TechProduct kindle = new TechProduct("kindle-id-4", "Amazon Kindle Paperwhite");
final UpdateResponse response = client.addBean("techproducts", kindle);
client.commit("techproducts");
同様に、QueryResponse
のgetBeans()
メソッドを使用して、検索結果をBeanオブジェクトに直接変換できます。
final SolrClient client = getSolrClient();
final SolrQuery query = new SolrQuery("*:*");
query.addField("id");
query.addField("name");
query.setSort("id", ORDER.asc);
final QueryResponse response = client.query("techproducts", query);
final List<TechProduct> products = response.getBeans(TechProduct.class);
その他のAPI
SolrJは、クエリとインデックス作成だけではありません。SolrのすべてのAPIをサポートしています。Solrの他のAPIにアクセスするには、適切なリクエストオブジェクトを見つけ、必要なパラメーターを提供し、SolrClient
のrequest()
メソッドに渡すだけです。request()
はNamedList
を返します。これは、リクエストによって返されたJSONまたはXMLの階層構造を反映する汎用オブジェクトです。
以下の例は、SolrJユーザーがSolrCloudデプロイメントのCLUSTERSTATUS APIを呼び出し、返されたNamedList
を操作する方法を示しています。
final SolrClient client = getSolrClient();
@SuppressWarnings({"rawtypes"})
final SolrRequest request = new CollectionAdminRequest.ClusterStatus();
final NamedList<Object> response = client.request(request);
@SuppressWarnings({"unchecked"})
final NamedList<Object> cluster = (NamedList<Object>) response.get("cluster");
@SuppressWarnings({"unchecked"})
final List<String> liveNodes = (List<String>) cluster.get("live_nodes");
print("Found " + liveNodes.size() + " live nodes");