SolrJ

SolrJ 是一個 API,可讓以 Java(或任何基於 JVM 的語言)編寫的應用程式輕鬆與 Solr 通訊。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.7.0"/>

對於使用 Maven 建置的專案,請將以下內容放置在您的 pom.xml

<dependency>
  <groupId>org.apache.solr</groupId>
  <artifactId>solr-solrj</artifactId>
  <version>9.7.0</version>
</dependency>

對於使用 Gradle 建置的專案,請將以下內容放置在您的 build.gradle

compile group: 'org.apache.solr', name: 'solr-solrj', version: '9.7.0'

如果您想要使用 CloudSolrClient 讓它直接與 ZooKeeper 通訊,您將需要在 solr-solrj-zookeeper 成品上新增相依性。

如果您未使用 Java 程式碼中的 串流表示式 類別,則可以排除 solr-solrj-streaming 相依性。

手動將 SolrJ 新增到類別路徑

如果您未使用上述任何一個建置系統,仍然可以輕鬆地將 SolrJ 新增到您的建置中。

在建置時,只需要 SolrJ jar 本身:solr-solrj-9.7.0.jar。若要手動編譯使用 SolrJ 的程式碼,請使用類似以下的 javac 命令

javac -cp .:$SOLR_TIP/server/solr-webapp/webapp/WEB-INF/lib/solr-solrj-9.7.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 的連線和通訊,並且是大多數使用者組態設定的地方。

請求以 SolrRequest 的形式發送,並以 SolrResponse 的形式返回。

SolrClient 的類型

SolrClient 有幾個具體的實作,每個實作都針對不同的使用模式或彈性模型。

  • HttpSolrClient - 適用於以查詢為中心的工作負載,但也適用於一般用途的客戶端。直接與單個 Solr 節點通訊。

  • Http2SolrClient - 非同步、非阻塞且一般用途的客戶端,使用 Jetty Http 函式庫來利用 HTTP/2。

  • HttpJdkSolrClient - 使用 JDK 內建 Http Client 的一般用途客戶端。支援 Http/2 和 Http/1.1。支援非同步。目標對象是希望盡量減少應用程式依賴關係的使用者。

  • LBHttpSolrClient - 在 Solr 節點清單之間平衡請求負載。根據節點健康狀況調整「服務中」節點清單。

  • LBHttp2SolrClient - 與 LBHttpSolrClient 類似,但改用 Http2SolrClient,並使用 Jetty Http 函式庫。

  • CloudSolrClient - 適用於與 SolrCloud 部署通訊。使用已記錄的 ZooKeeper 狀態來發現請求並將其路由到健康的 Solr 節點。

  • ConcurrentUpdateSolrClient - 適用於以索引為中心的工作負載。在將較大的批次發送到 Solr 之前,會在內部緩衝文件。

  • ConcurrentUpdateHttp2SolrClient - 與 ConcurrentUpdateSolrClient 類似,但改用 Http2SolrClient,並使用 Jetty Http 函式庫。

常見的組態選項

大多數 SolrJ 組態都發生在 SolrClient 層級。以下討論其中最常見/重要的選項。如需關於如何調整 SolrClient 的完整資訊,請參閱相關客戶端及其對應的建構器物件的 Javadoc 文件。

基礎 URL

大多數 SolrClient 實作(CloudSolrClientHttp2SolrClient 除外)都要求使用者指定一個或多個 Solr 基礎 URL,客戶端隨後會使用這些 URL 將 HTTP 請求發送到 Solr。使用者在提供的基礎 URL 中包含的路徑會影響從那時起建立的客戶端的行為。

  1. 路徑指向特定核心或集合的 URL(例如,http://hostname:8983/solr/core1)。當基礎 URL 中指定了核心或集合時,使用該客戶端發出的後續請求不需要在每個請求上重新指定受影響的集合。但是,使用這種 URL 形式的客戶端只能用於將請求發送到 URL 中包含的核心或集合。「管理」(即非核心)請求,或使用客戶端對其他索引提出的請求將會失敗。由於這些限制,不建議使用此類型的路徑,並且將在 Solr 10 中移除。建議使用者在建立客戶端時改為提供「根」基礎 URL(請參閱下文),並使用相關 SolrClient 建構器物件上可用的 withDefaultCollection(String) 方法指定核心。

  2. 指向根 Solr 路徑的 URL(例如 http://hostname:8983/solr)。希望指定預設集合的使用者可以使用相關 SolrClient 建構器物件上可用的 withDefaultCollection(String) 方法來執行此操作。

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 工作最可靠且效能最高的方式。另一方面,這意味著將 ZooKeeper 的公開範圍擴大到 Solr 節點之外,這是一個安全風險。它還會新增更多 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 會回復為使用作業系統/環境的預設值。

ConcurrentUpdateSolrClient 及其對應的 ConcurrentUpdateHttp2SolrClient 也實作了一個停滯預防逾時,允許對無回應節點的請求比等待通訊端逾時更快地失敗。此逾時的預設值設定為 15000 毫秒,並且可以通過系統屬性 solr.cloud.client.stallTime 進行調整。此值應小於 solr.jetty.http.idleTimeout(預設值為 120000 毫秒),且大於最大更新請求的處理時間。

雲端請求路由

SolrJ CloudSolrClient 實作 (CloudSolrClientCloudHttp2SolrClient) 會遵守 shards.preference 參數。因此,使用上述任一客戶端發送到單分片集合的請求,會以與將分散式請求路由到個別分片相同的方式來路由請求。如果未提供 shards.preference 參數,客戶端預設會隨機排序複本。

對於更新請求,雖然複本會以請求定義的順序排序,但領導者複本始終會先排序。

在 SolrJ 中查詢

SolrClient 有許多 query() 方法可用於從 Solr 擷取結果。這些方法中的每一個都接受 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 的執行個體,並將它們作為 SolrClientadd() 方法之一的引數提供。

以下範例說明如何使用 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 索引最佳實務。在正常情況下,應該以較大的批次對文件建立索引,而不是一次一個。也建議 Solr 管理員使用 Solr 的自動提交設定來提交文件,而不是使用明確的 commit() 調用。

Java 物件繫結

雖然 SolrJ 提供的 UpdateResponseQueryResponse 介面很有用,但通常更方便使用應用程式可以更容易理解的網域特定物件。值得慶幸的是,SolrJ 透過將文件隱式轉換為任何使用 Field 註解特別標記的類別,來支援此功能。

Java 物件中的每個執行個體變數都可以使用 Field 註解對應到相應的 Solr 欄位。Solr 欄位預設會與註解的變數共用名稱,但是,可以透過使用明確的欄位名稱為註解提供覆寫此設定。

以下範例程式碼片段顯示了一個已註解的 TechProduct 類別,可用於表示 Solr 的「techproducts」範例集合中的結果。

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 很簡單,只要找到適當的請求物件,提供任何必要的參數,並將其傳遞到 SolrClientrequest() 方法即可。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");