建立內容連接器

「內容連接器」是一種軟體程式,用於掃遍企業存放區中的資料並填入資料來源。Google 提供以下開發內容連接器的選項:

  • 內容連接器 SDK。如果您是以 Java 編寫程式碼,這個方法十分實用。Content Connector SDK 是 REST API 的包裝函式,可讓您快速建立連接器。如要使用 SDK 建立內容連接器,請參閱「使用 Content Connector SDK 建立內容連接器」一文。

  • 低階 REST API 或 API 程式庫。如果您未使用 Java 編寫程式,或您的程式碼集較適合 REST API 或程式庫,請使用這些選項。如要使用 REST API 建立內容連接器,請參閱使用 REST API 建立內容連接器

一般的內容連接器會執行下列工作:

  1. 可讀取及處理設定參數。
  2. 從第三方內容存放區提取獨立區塊資料,稱為「項目」。
  3. 將 ACL、中繼資料與內容資料結合成可建立索引的項目。
  4. 將項目索引到 Cloud Search 資料來源。
  5. (選用) 監聽第三方內容存放區的變更通知。變更通知會轉換為索引要求,讓 Cloud Search 資料來源與第三方存放區保持同步。只有在存放區支援變更偵測功能時,連接器才會執行這項工作。

使用 Content Connector SDK 建立內容連接器

以下各節說明如何使用內容連接器 SDK 建立內容連接器。

設定依附元件

您必須在建構檔案中加入某些依附元件,才能使用 SDK。點選下列分頁標籤,查看建構環境的依附元件:

Maven

<dependency>
<groupId>com.google.enterprise.cloudsearch</groupId>
<artifactId>google-cloudsearch-indexing-connector-sdk</artifactId>
<version>v1-0.0.3</version>
</dependency>

Gradle

compile group: 'com.google.enterprise.cloudsearch',
        name: 'google-cloudsearch-indexing-connector-sdk',
        version: 'v1-0.0.3'

建立連接器設定

每個連接器都有一個設定檔,其中包含連接器使用的參數,例如存放區的 ID。參數是由鍵/值組合定義,例如 api.sourceId=1234567890abcdef

Google Cloud Search SDK 包含多個 Google 提供的設定參數,供所有連接器使用。您必須在設定檔中宣告下列 Google 提供的參數:

  • 針對內容連接器,您必須宣告 api.sourceIdapi.serviceAccountPrivateKeyFile,因為這些參數會識別存放區位置,以及存取存放區所需的私密金鑰。
  • 如為識別資訊連接器,您必須將 api.identitySourceId 宣告為這個參數,用於識別外部識別資訊來源的位置。如要同步處理使用者,您也必須將 api.customerId 宣告為企業 Google Workspace 帳戶的專屬 ID。

除非您要覆寫其他 Google 參數的預設值,否則不需要在設定檔中宣告。如要進一步瞭解 Google 提供的設定參數 (例如如何產生特定 ID 和金鑰),請參閱 Google 提供的設定參數

您也可以自行定義要用於設定檔的存放區特定參數。

將設定檔傳送至連接器

設定系統屬性 config,將設定檔傳送至連接器。您可以在啟動連接器時使用 -D 引數來設定屬性。例如,下列指令會使用 MyConfig.properties 設定檔啟動連接器:

java -classpath myconnector.jar;... -Dconfig=MyConfig.properties MyConnector

如果缺少這個引數,SDK 會嘗試存取名為 connector-config.properties 的預設設定檔。

決定週遊策略

內容連接器的主要功能是掃遍存放區並為資料建立索引。您必須根據存放區中的資料大小和版面配置實作週遊策略。您可以自行設計策略,也可以選擇在 SDK 中實作的策略:

完整週遊策略

完整週遊策略會掃描整個存放區,並盲目為每個項目建立索引。這種策略通常適用於小型存放區,且可負擔每次建立索引時執行完整週遊的負擔。

此週遊策略適用於大部分靜態非階層資料的小型存放區。當存放區難以偵測變更或不支援變更時,您也可以使用這個週遊策略。

清單週遊策略

清單遍歷策略會掃描整個存放區 (包括所有子節點),以決定每個項目的狀態。然後,連接器會接受第二條傳遞,且只會為自上次索引建立後新增或已更新的項目建立索引。此策略通常用於對現有索引執行漸進式更新 (而不是在每次更新索引時執行完整週遊)。

當存放區難以偵測或不支援變更、包含非階層式資料,且您正在使用非常大型的資料集時,這個週遊策略就非常實用。

繪製週遊圖表

圖形週遊策略會掃描整個父項節點,以判斷每個項目的狀態。然後,連接器會進行第二次傳遞,且只會將根節點中的項目編入索引,或自上次索引建立後經過更新。最後,連接器會傳遞任何子 ID,然後將子節點中全新或已更新的項目編入索引。連接器會持續在所有子節點中遞迴,直到所有項目都得到解決。這類週遊通常用於階層存放區,其中列出所有 ID 都不實際。

如果您需要檢索的階層資料 (例如一系列的目錄或網頁),則適合採用此策略。

每個週遊策略都是由 SDK 中的範本連接器類別實作。雖然您可以實作自己的周遊策略,但這些範本可大幅加快連接器的開發速度。如要使用範本建立連接器,請前往與遍歷策略對應的章節:

使用範本類別建立完整的周遊連接器

說明文件的這節是指 FullTraversalSample 範例中的程式碼片段。

實作連接器的進入點

連接器的進入點為 main() 方法。這個方法的主要工作是建立 Application 類別的例項,並叫用其 start() 方法來執行連接器。

呼叫 application.start() 前,請使用 IndexingApplication.Builder 類別將 FullTraversalConnector 範本例項化。FullTraversalConnector 接受已實作方法的 Repository 物件。下列程式碼片段說明如何實作 main() 方法:

FullTraversalSample.java
/**
 * This sample connector uses the Cloud Search SDK template class for a full
 * traversal connector.
 *
 * @param args program command line arguments
 * @throws InterruptedException thrown if an abort is issued during initialization
 */
public static void main(String[] args) throws InterruptedException {
  Repository repository = new SampleRepository();
  IndexingConnector connector = new FullTraversalConnector(repository);
  IndexingApplication application = new IndexingApplication.Builder(connector, args).build();
  application.start();
}

在背景,SDK 會在連接器的 main() 方法呼叫 Application.build 後,呼叫 initConfig() 方法。initConfig() 方法會執行下列工作:

  1. 呼叫 Configuation.isInitialized() 方法,確保 Configuration 尚未初始化。
  2. 使用 Google 提供的鍵/值組合初始化 Configuration 物件。每個鍵/值組合都會儲存在 Configuration 物件中的 ConfigValue 物件。

導入 Repository 介面

Repository 物件的唯一用途是執行存放區項目的遍歷和索引。使用範本時,只需要覆寫 Repository 介面中的特定方法,即可建立內容連接器。您要覆寫的方法取決於使用的範本和周遊策略。針對 FullTraversalConnector,請覆寫下列方法:

  • init() 方法。如要執行任何資料存放區設定和初始化作業,請覆寫 init() 方法。

  • getAllDocs() 方法。如要掃遍資料存放區中的所有項目並建立索引,請覆寫 getAllDocs() 方法。根據您的設定,每個排定的周遊作業都會呼叫這個方法。

  • (選用) getChanges() 方法。如果您的存放區支援變更偵測功能,請覆寫 getChanges() 方法。根據您的設定,每排定一次的漸進式週遊作業都會呼叫此方法,以擷取已修改的項目並建立索引。

  • (選用) close() 方法。如果需要執行存放區清理,請覆寫 close() 方法。關機期間會呼叫這個方法一次。

Repository 物件的每個方法都會傳回某個類型的 ApiOperation 物件。ApiOperation 物件會以單一 (甚至多個) IndexingService.indexItem() 呼叫的形式執行動作,以執行存放區的實際索引建立作業。

取得自訂設定參數

處理連接器設定時,您需要從 Configuration 物件取得所有自訂參數。這項工作通常會在 Repository 類別的 init() 方法中執行。

Configuration 類別有多種方法,可以從設定取得不同的資料類型。每個方法都會傳回一個 ConfigValue 物件。接著,您將使用 ConfigValue 物件的 get() 方法擷取實際值。以下 FullTraversalSample 程式碼片段說明如何從 Configuration 物件擷取單一自訂整數值:

FullTraversalSample.java
@Override
public void init(RepositoryContext context) {
  log.info("Initializing repository");
  numberOfDocuments = Configuration.getInteger("sample.documentCount", 10).get();
}

如要取得並剖析含有多個值的參數,請使用 Configuration 類別的其中一個類型剖析器,將資料剖析為離散區塊。教學課程連接器的下列程式碼片段使用 getMultiValue 方法取得 GitHub 存放區名稱的清單:

GithubRepository.java
ConfigValue<List<String>> repos = Configuration.getMultiValue(
    "github.repos",
    Collections.emptyList(),
    Configuration.STRING_PARSER);

執行完整週遊

覆寫 getAllDocs() 以執行完整的周遊作業,並為存放區建立索引。getAllDocs() 方法會接受查核點。檢查點用於在特定項目恢復索引時,前提是程序可能會中斷。針對存放區中的每個項目,在 getAllDocs() 方法中執行下列步驟:

  1. 設定權限。
  2. 為要建立索引的項目設定中繼資料。
  3. 將中繼資料和項目合併成一個可建立索引的 RepositoryDoc
  4. 將每個可建立索引的項目封裝至 getAllDocs() 方法傳回的疊代器。請注意,getAllDocs() 實際上會傳回 CheckpointCloseableIterable,這是 ApiOperation 物件的疊代作業,每個物件都代表對 RepositoryDoc 執行的 API 要求,例如建立索引。

如果項目組合過大,無法在單一呼叫中處理,請加入查核點並設定 hasMore(true),表示有更多項目可供建立索引。

設定項目的權限

您的存放區使用存取控制清單 (ACL) 來識別擁有項目存取權的使用者或群組。ACL 是可存取項目的群組或使用者的 ID 清單。

您必須複製存放區使用的 ACL,以確保只有具備項目存取權的使用者才能在搜尋結果中看到該項目。為項目建立索引時,必須納入項目的 ACL,Google Cloud Search 才能具備該項目的正確存取層級資訊。

Content Connector SDK 提供豐富的 ACL 類別和方法,可模擬大多數存放區的 ACL。您必須分析存放區中每個項目的 ACL,並在將項目編入索引時,為 Google Cloud Search 建立對應的 ACL。如果存放區的 ACL 採用 ACL 繼承等概念,對 ACL 建立模型可能並不容易。如要進一步瞭解 Google Cloud Search ACL,請參閱 Google Cloud Search ACL

注意:Cloud Search Indexing API 支援單一網域 ACL。不支援跨網域 ACL。使用 Acl.Builder 類別即可透過 ACL 設定每個項目的存取權。下列程式碼片段取自完整週遊範例,可讓所有使用者或「主體」(getCustomerPrincipal()) 在搜尋時成為所有項目的「讀取者」(.setReaders())。

FullTraversalSample.java
// Make the document publicly readable within the domain
Acl acl = new Acl.Builder()
    .setReaders(Collections.singletonList(Acl.getCustomerPrincipal()))
    .build();

您必須瞭解 ACL 才能妥善為存放區建立 ACL。舉例來說,您可能會為使用某種繼承模型的檔案系統中的檔案建立索引,而子項資料夾會沿用上層資料夾的權限。建立模型 ACL 繼承需要 Google Cloud Search ACL 中說明的額外資訊

設定項目的中繼資料

中繼資料會儲存在 Item 物件中。如要建立 Item,您至少需要該項目的專屬字串 ID、項目類型、ACL、網址和版本。下列程式碼片段說明如何使用 IndexingItemBuilder 輔助類別建構 Item

FullTraversalSample.java
// Url is required. Use google.com as a placeholder for this sample.
String viewUrl = "https://www.google.com";

// Version is required, set to current timestamp.
byte[] version = Longs.toByteArray(System.currentTimeMillis());

// Using the SDK item builder class to create the document with appropriate attributes
// (this can be expanded to include metadata fields etc.)
Item item = IndexingItemBuilder.fromConfiguration(Integer.toString(id))
    .setItemType(IndexingItemBuilder.ItemType.CONTENT_ITEM)
    .setAcl(acl)
    .setSourceRepositoryUrl(IndexingItemBuilder.FieldOrValue.withValue(viewUrl))
    .setVersion(version)
    .build();

建立可建立索引的項目

設定項目的中繼資料後,即可使用 RepositoryDoc.Builder 類別建立實際的可建立索引項目。以下範例說明如何建立可建立索引的單一項目。

FullTraversalSample.java
// For this sample, content is just plain text
String content = String.format("Hello world from sample doc %d", id);
ByteArrayContent byteContent = ByteArrayContent.fromString("text/plain", content);

// Create the fully formed document
RepositoryDoc doc = new RepositoryDoc.Builder()
    .setItem(item)
    .setContent(byteContent, IndexingService.ContentFormat.TEXT)
    .build();

RepositoryDoc 是一種 ApiOperation 類型,會執行實際 IndexingService.indexItem() 要求。

您也可以使用 RepositoryDoc.Builder 類別的 setRequestMode() 方法,將索引要求識別為 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
非同步模式會縮短建立索引提供服務的延遲時間,並適用於索引要求的大量處理配額。對於整個存放區的初始索引 (補充作業),建議採用非同步模式。
SYNCHRONOUS
同步模式會造成建立索引的延遲時間較短,以有限的處理量配額。建議您採用同步模式來為存放區更新和變更建立索引。如果未指定,要求模式會預設為 SYNCHRONOUS

將每個可建立索引的項目封裝至疊代器

getAllDocs() 方法會傳回 Iterator,特別是 RepositoryDoc 物件的 CheckpointCloseableIterable。您可以使用 CheckpointClosableIterableImpl.Builder 類別建構並傳回疊代器。下列程式碼片段說明如何建構及傳回疊代器。

FullTraversalSample.java
CheckpointCloseableIterable<ApiOperation> iterator =
  new CheckpointCloseableIterableImpl.Builder<>(allDocs).build();

SDK 會執行疊代器中包含的每個索引呼叫。

後續步驟

以下是您可能的後續步驟:

使用範本類別建立週遊連接器清單

系統會使用 Cloud Search 索引佇列保留存放區中每個項目的 ID 和選用的雜湊值。清單週遊連接器會將項目 ID 推送到 Google Cloud Search Indexing 佇列,並逐一擷取這些 ID 來建立索引。Google Cloud Search 會保留佇列並比較佇列內容,藉此判斷項目狀態,例如項目是否已從存放區中刪除。如要進一步瞭解 Cloud Search 索引佇列,請參閱 Cloud Search 索引佇列

本文的本節參照 ListTraversalSample 範例中的程式碼片段。

實作連接器的進入點

連接器的進入點為 main() 方法。這個方法的主要工作是建立 Application 類別的例項,並叫用其 start() 方法來執行連接器。

呼叫 application.start() 前,請使用 IndexingApplication.Builder 類別將 ListingConnector 範本例項化。ListingConnector 接受 Repository 物件,該物件已實作您實作的方法。下列程式碼片段說明如何將 ListingConnector 及其相關聯的 Repository 執行個體化:

ListTraversalSample.java
/**
 * This sample connector uses the Cloud Search SDK template class for a
 * list traversal connector.
 *
 * @param args program command line arguments
 * @throws InterruptedException thrown if an abort is issued during initialization
 */
public static void main(String[] args) throws InterruptedException {
  Repository repository = new SampleRepository();
  IndexingConnector connector = new ListingConnector(repository);
  IndexingApplication application = new IndexingApplication.Builder(connector, args).build();
  application.start();
}

在背景,SDK 會在連接器的 main() 方法呼叫 Application.build 後,呼叫 initConfig() 方法。initConfig() 方法:

  1. 呼叫 Configuation.isInitialized() 方法,確保 Configuration 尚未初始化。
  2. 使用 Google 提供的鍵/值組合初始化 Configuration 物件。每個鍵/值組合都會儲存在 Configuration 物件中的 ConfigValue 物件。

導入 Repository 介面

Repository 物件的唯一用途是執行存放區項目的遍歷和索引。使用範本時,只需要覆寫 Repository 介面中的特定方法,即可建立內容連接器。你要覆寫的方法會因使用的範本和周遊策略而異。針對 ListingConnector,請覆寫下列方法:

  • init() 方法。如要執行任何資料存放區設定和初始化作業,請覆寫 init() 方法。

  • getIds() 方法。如要擷取存放區中所有記錄的 ID 和雜湊值,請覆寫 getIds() 方法。

  • getDoc() 方法。如要新增、更新、修改或刪除索引中的項目,請覆寫 getDoc() 方法。

  • (選用) getChanges() 方法。如果您的存放區支援變更偵測功能,請覆寫 getChanges() 方法。根據您的設定,每排定一次的漸進式週遊作業都會呼叫此方法,以擷取已修改的項目並建立索引。

  • (選用) close() 方法。如果需要執行存放區清理,請覆寫 close() 方法。關機期間會呼叫這個方法一次。

Repository 物件的每個方法都會傳回某個類型的 ApiOperation 物件。ApiOperation 物件會以單一 (甚至多個) IndexingService.indexItem() 呼叫的形式執行動作,以執行存放區的實際索引建立作業。

取得自訂設定參數

處理連接器設定時,您需要從 Configuration 物件取得所有自訂參數。這項工作通常會在 Repository 類別的 init() 方法中執行。

Configuration 類別有多種方法,可以從設定取得不同的資料類型。每個方法都會傳回一個 ConfigValue 物件。接著,您將使用 ConfigValue 物件的 get() 方法擷取實際值。以下 FullTraversalSample 程式碼片段說明如何從 Configuration 物件擷取單一自訂整數值:

FullTraversalSample.java
@Override
public void init(RepositoryContext context) {
  log.info("Initializing repository");
  numberOfDocuments = Configuration.getInteger("sample.documentCount", 10).get();
}

如要取得並剖析含有多個值的參數,請使用 Configuration 類別的其中一個類型剖析器,將資料剖析為離散區塊。教學課程連接器的下列程式碼片段使用 getMultiValue 方法取得 GitHub 存放區名稱的清單:

GithubRepository.java
ConfigValue<List<String>> repos = Configuration.getMultiValue(
    "github.repos",
    Collections.emptyList(),
    Configuration.STRING_PARSER);

執行清單週遊

覆寫 getIds() 方法,擷取存放區中所有記錄的 ID 和雜湊值。getIds() 方法會接受查核點。當程序遭到中斷,檢查點就能用來繼續為特定項目建立索引。

接著,請覆寫 getDoc() 方法,處理 Cloud Search 索引佇列中的每個項目。

推送項目 ID 和雜湊值

覆寫 getIds() 以從存放區擷取項目 ID 及其關聯內容雜湊值。接著,ID 和雜湊值組合會封裝至 Cloud Search Indexing 佇列的推送作業要求中。系統通常會先推送根 ID 或父項 ID,然後再推送子項 ID,直到整個項目階層處理完畢為止。

getIds() 方法接受檢查點,代表要建立索引的最後一個項目。當程序中斷時,查核點可用於繼續為特定項目建立索引。針對存放區中的每個項目,在 getIds() 方法中執行下列步驟:

  • 從存放區取得每個項目 ID 和相關雜湊值。
  • 將每個 ID 和雜湊值組合封裝至 PushItems
  • 將每個 PushItems 合併至 getIds() 方法傳回的疊代器。請注意,getIds() 實際上會傳回 CheckpointCloseableIterable,這是 ApiOperation 物件的疊代作業,每個物件都代表對 RepositoryDoc 執行的 API 要求,例如將項目推送至佇列。

下列程式碼片段說明如何取得每個項目 ID 和雜湊值,並將其插入 PushItems 中。PushItems 是一種 ApiOperation 要求,可將項目推送至 Cloud Search 索引佇列。

ListTraversalSample.java
PushItems.Builder allIds = new PushItems.Builder();
for (Map.Entry<Integer, Long> entry : this.documents.entrySet()) {
  String documentId = Integer.toString(entry.getKey());
  String hash = this.calculateMetadataHash(entry.getKey());
  PushItem item = new PushItem().setMetadataHash(hash);
  log.info("Pushing " + documentId);
  allIds.addPushItem(documentId, item);
}

下列程式碼片段說明如何使用 PushItems.Builder 類別,將 ID 和雜湊值封裝至單一推送 ApiOperation

ListTraversalSample.java
ApiOperation pushOperation = allIds.build();
CheckpointCloseableIterable<ApiOperation> iterator =
  new CheckpointCloseableIterableImpl.Builder<>(
      Collections.singletonList(pushOperation))
  .build();
return iterator;

系統會將項目推送至 Cloud Search 索引佇列,以便進行後續處理。

擷取及處理每項商品

覆寫 getDoc() 來處理 Cloud Search 索引佇列中的每個項目。項目可能是新增、修改、變更,也可能不再存在於來源存放區中。擷取全新或修改過的項目,並為其建立索引。從來源存放區已不存在的索引中移除項目。

getDoc() 方法可接受 Google Cloud Search 索引佇列中的項目。針對佇列中的每個項目,在 getDoc() 方法中執行下列步驟:

  1. 檢查存放區中是否存在 Cloud Search 索引佇列中的項目 ID。如果不是,請從索引中刪除項目。

  2. 輪詢項目狀態的索引;如果項目未變更 (ACCEPTED),則不採取任何行動。

  3. 已變更索引或新項目:

    1. 設定權限。
    2. 為要建立索引的項目設定中繼資料。
    3. 將中繼資料和項目合併成一個可建立索引的 RepositoryDoc
    4. 傳回 RepositoryDoc

注意:ListingConnector 範本不支援在 getDoc() 方法中傳回 null。傳回 null 筆結果,時間為 NullPointerException.

處理已刪除的項目

下列程式碼片段說明如何判斷存放區中的項目是否存在,如果不存在,則會刪除該項目。

ListTraversalSample.java
String resourceName = item.getName();
int documentId = Integer.parseInt(resourceName);

if (!documents.containsKey(documentId)) {
  // Document no longer exists -- delete it
  log.info(() -> String.format("Deleting document %s", item.getName()));
  return ApiOperations.deleteItem(resourceName);
}

請注意,documents 是代表存放區的資料結構。如果 documents 中找不到 documentID,請傳回 APIOperations.deleteItem(resourceName),將該項目從索引中刪除。

處理未變更的項目

下列程式碼片段說明如何在 Cloud Search 索引佇列中輪詢項目狀態,並處理未變更的項目。

ListTraversalSample.java
String currentHash = this.calculateMetadataHash(documentId);
if (this.canSkipIndexing(item, currentHash)) {
  // Document neither modified nor deleted, ack the push
  log.info(() -> String.format("Document %s not modified", item.getName()));
  PushItem pushItem = new PushItem().setType("NOT_MODIFIED");
  return new PushItems.Builder().addPushItem(resourceName, pushItem).build();
}

如要判斷項目是否尚未修改,請檢查項目狀態,以及其他可能代表變更的中繼資料。在此範例中,系統會使用中繼資料雜湊來判斷項目是否已變更。

ListTraversalSample.java
/**
 * Checks to see if an item is already up to date
 *
 * @param previousItem Polled item
 * @param currentHash  Metadata hash of the current github object
 * @return PushItem operation
 */
private boolean canSkipIndexing(Item previousItem, String currentHash) {
  if (previousItem.getStatus() == null || previousItem.getMetadata() == null) {
    return false;
  }
  String status = previousItem.getStatus().getCode();
  String previousHash = previousItem.getMetadata().getHash();
  return "ACCEPTED".equals(status)
      && previousHash != null
      && previousHash.equals(currentHash);
}

設定項目的權限

您的存放區使用存取控制清單 (ACL) 來識別擁有項目存取權的使用者或群組。ACL 是可存取項目的群組或使用者的 ID 清單。

您必須複製存放區使用的 ACL,以確保只有具備項目存取權的使用者才能在搜尋結果中看到該項目。為項目建立索引時,必須納入項目的 ACL,Google Cloud Search 才能具備該項目的正確存取層級資訊。

Content Connector SDK 提供豐富的 ACL 類別和方法,可模擬大多數存放區的 ACL。您必須分析存放區中每個項目的 ACL,並在將項目編入索引時,為 Google Cloud Search 建立對應的 ACL。如果存放區的 ACL 採用 ACL 繼承等概念,對 ACL 建立模型可能並不容易。如要進一步瞭解 Google Cloud Search ACL,請參閱 Google Cloud Search ACL

注意:Cloud Search Indexing API 支援單一網域 ACL。不支援跨網域 ACL。使用 Acl.Builder 類別即可透過 ACL 設定每個項目的存取權。下列程式碼片段取自完整週遊範例,可讓所有使用者或「主體」(getCustomerPrincipal()) 在搜尋時成為所有項目的「讀取者」(.setReaders())。

FullTraversalSample.java
// Make the document publicly readable within the domain
Acl acl = new Acl.Builder()
    .setReaders(Collections.singletonList(Acl.getCustomerPrincipal()))
    .build();

您必須瞭解 ACL 才能妥善為存放區建立 ACL。舉例來說,您可能會為使用某種繼承模型的檔案系統中的檔案建立索引,而子項資料夾會沿用上層資料夾的權限。建立模型 ACL 繼承需要 Google Cloud Search ACL 中說明的額外資訊

設定項目的中繼資料

中繼資料會儲存在 Item 物件中。如要建立 Item,您至少需要該項目的專屬字串 ID、項目類型、ACL、網址和版本。下列程式碼片段說明如何使用 IndexingItemBuilder 輔助類別建構 Item

ListTraversalSample.java
// Url is required. Use google.com as a placeholder for this sample.
String viewUrl = "https://www.google.com";

// Version is required, set to current timestamp.
byte[] version = Longs.toByteArray(System.currentTimeMillis());

// Set metadata hash so queue can detect changes
String metadataHash = this.calculateMetadataHash(documentId);

// Using the SDK item builder class to create the document with
// appropriate attributes. This can be expanded to include metadata
// fields etc.
Item item = IndexingItemBuilder.fromConfiguration(Integer.toString(documentId))
    .setItemType(IndexingItemBuilder.ItemType.CONTENT_ITEM)
    .setAcl(acl)
    .setSourceRepositoryUrl(IndexingItemBuilder.FieldOrValue.withValue(viewUrl))
    .setVersion(version)
    .setHash(metadataHash)
    .build();

建立可建立索引的項目

設定項目的中繼資料後,即可使用 RepositoryDoc.Builder 建立實際的可建立索引項目。以下範例說明如何建立可建立索引的單一項目。

ListTraversalSample.java
// For this sample, content is just plain text
String content = String.format("Hello world from sample doc %d", documentId);
ByteArrayContent byteContent = ByteArrayContent.fromString("text/plain", content);

// Create the fully formed document
RepositoryDoc doc = new RepositoryDoc.Builder()
    .setItem(item)
    .setContent(byteContent, IndexingService.ContentFormat.TEXT)
    .build();

RepositoryDoc 是一種 ApiOperation 類型,會執行實際 IndexingService.indexItem() 要求。

您也可以使用 RepositoryDoc.Builder 類別的 setRequestMode() 方法,將索引要求識別為 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
非同步模式會縮短建立索引提供服務的延遲時間,並適用於索引要求的大量處理配額。對於整個存放區的初始索引 (補充作業),建議採用非同步模式。
SYNCHRONOUS
同步模式會造成建立索引的延遲時間較短,以有限的處理量配額。建議您採用同步模式來為存放區更新和變更建立索引。如果未指定,要求模式會預設為 SYNCHRONOUS

後續步驟

以下是您可能的後續步驟:

使用範本類別建立圖形週遊連接器

系統會使用 Cloud Search 索引佇列保留存放區中每個項目的 ID 和選用的雜湊值。圖表週遊連接器會將項目 ID 推送到 Google Cloud Search Indexing 佇列,並逐一擷取這些項目以進行索引。Google Cloud Search 會維護佇列並比較佇列內容,進而判斷項目狀態,例如項目是否已從存放區中刪除。如要進一步瞭解 Cloud Search 索引佇列,請參閱 Google Cloud Search 索引佇列

在建立索引期間,系統會從資料存放區擷取項目內容,並將任何子項項目 ID 推送至佇列。連接器會以遞迴方式處理父項和子項 ID,直到所有項目都處理完成。

本節文件參照 GraphTraversalSample 範例中的程式碼片段。

實作連接器的進入點

連接器的進入點為 main() 方法。這個方法的主要工作是建立 Application 類別的例項,並叫用其 start() 方法來執行連接器。

呼叫 application.start() 之前,請使用 IndexingApplication.Builder 類別將 ListingConnector 範本例項化。ListingConnector 接受已實作方法的 Repository 物件。

下列程式碼片段說明如何將 ListingConnector 及其相關聯的 Repository 執行個體化:

GraphTraversalSample.java
/**
 * This sample connector uses the Cloud Search SDK template class for a graph
 * traversal connector.
 *
 * @param args program command line arguments
 * @throws InterruptedException thrown if an abort is issued during initialization
 */
public static void main(String[] args) throws InterruptedException {
  Repository repository = new SampleRepository();
  IndexingConnector connector = new ListingConnector(repository);
  IndexingApplication application = new IndexingApplication.Builder(connector, args).build();
  application.start();
}

在背景,SDK 會在連接器的 main() 方法呼叫 Application.build 後,呼叫 initConfig() 方法。initConfig() 方法:

  1. 呼叫 Configuation.isInitialized() 方法,確保 Configuration 尚未初始化。
  2. 使用 Google 提供的鍵/值組合初始化 Configuration 物件。每個鍵/值組合都會儲存在 Configuration 物件中的 ConfigValue 物件。

導入 Repository 介面

Repository 物件的唯一目的是執行存放區項目的遍歷和索引。使用範本時,只需要覆寫 Repository 介面中的特定方法,即可建立內容連接器。要覆寫的方法視您使用的範本和周遊策略而定。針對 ListingConnector,您需要覆寫下列方法:

  • init() 方法。如要執行任何資料存放區設定和初始化作業,請覆寫 init() 方法。

  • getIds() 方法。如要擷取存放區中所有記錄的 ID 和雜湊值,請覆寫 getIds() 方法。

  • getDoc() 方法。如要新增、更新、修改或刪除索引中的項目,請覆寫 getDoc() 方法。

  • (選用) getChanges() 方法。如果您的存放區支援變更偵測功能,請覆寫 getChanges() 方法。根據您的設定,每排定一次的漸進式週遊作業都會呼叫此方法,以擷取已修改的項目並建立索引。

  • (選用) close() 方法。如果需要執行存放區清理,請覆寫 close() 方法。關機期間會呼叫這個方法一次。

Repository 物件的每個方法都會傳回某個類型的 ApiOperation 物件。ApiOperation 物件會以單一 (可能) 多個 IndexingService.indexItem() 呼叫的形式執行動作,以執行存放區的實際索引建立作業。

取得自訂設定參數

處理連接器設定時,您需要從 Configuration 物件取得所有自訂參數。這項工作通常會在 Repository 類別的 init() 方法中執行。

Configuration 類別有多種方法,可以從設定取得不同的資料類型。每個方法都會傳回一個 ConfigValue 物件。接著,您將使用 ConfigValue 物件的 get() 方法擷取實際值。以下 FullTraversalSample 程式碼片段說明如何從 Configuration 物件擷取單一自訂整數值:

FullTraversalSample.java
@Override
public void init(RepositoryContext context) {
  log.info("Initializing repository");
  numberOfDocuments = Configuration.getInteger("sample.documentCount", 10).get();
}

如要取得並剖析含有多個值的參數,請使用 Configuration 類別的其中一個類型剖析器,將資料剖析為離散區塊。教學課程連接器的下列程式碼片段使用 getMultiValue 方法取得 GitHub 存放區名稱的清單:

GithubRepository.java
ConfigValue<List<String>> repos = Configuration.getMultiValue(
    "github.repos",
    Collections.emptyList(),
    Configuration.STRING_PARSER);

執行圖形週遊

覆寫 getIds() 方法,擷取存放區中所有記錄的 ID 和雜湊值。getIds() 方法會接受查核點。當程序遭到中斷,檢查點就能用來繼續為特定項目建立索引。

接著,請覆寫 getDoc() 方法,處理 Cloud Search 索引佇列中的每個項目。

推送項目 ID 和雜湊值

覆寫 getIds() 以從存放區擷取項目 ID 及其關聯內容雜湊值。接著,ID 和雜湊值組合會封裝至 Cloud Search Indexing 佇列的推送作業要求中。系統通常會先推送根 ID 或父項 ID,然後再推送子項 ID,直到整個項目階層處理完畢為止。

getIds() 方法接受檢查點,代表要建立索引的最後一個項目。當程序中斷時,查核點可用於繼續為特定項目建立索引。針對存放區中的每個項目,在 getIds() 方法中執行下列步驟:

  • 從存放區取得每個項目 ID 和相關雜湊值。
  • 將每個 ID 和雜湊值組合封裝至 PushItems
  • 將每個 PushItems 合併至 getIds() 方法傳回的疊代器。請注意,getIds() 實際上會傳回 CheckpointCloseableIterable,這是 ApiOperation 物件的疊代作業,每個物件都代表對 RepositoryDoc 執行的 API 要求,例如將項目推送至佇列。

下列程式碼片段說明如何取得每個項目 ID 和雜湊值,並將其插入 PushItems 中。PushItems 是一種 ApiOperation 要求,可將項目推送至 Cloud Search Indexing 佇列。

GraphTraversalSample.java
PushItems.Builder allIds = new PushItems.Builder();
PushItem item = new PushItem();
allIds.addPushItem("root", item);

下列程式碼片段說明如何使用 PushItems.Builder 類別,將 ID 和雜湊值封裝至單一推送 ApiOperation

GraphTraversalSample.java
ApiOperation pushOperation = allIds.build();
CheckpointCloseableIterable<ApiOperation> iterator =
  new CheckpointCloseableIterableImpl.Builder<>(
      Collections.singletonList(pushOperation))
  .build();

系統會將項目推送至 Cloud Search 索引佇列,以便進行後續處理。

擷取及處理每項商品

覆寫 getDoc() 來處理 Cloud Search 索引佇列中的每個項目。項目可能是新增、修改、變更,也可能不再存在於來源存放區中。擷取全新或修改過的項目,並為其建立索引。從來源存放區已不存在的索引中移除項目。

getDoc() 方法會接受 Cloud Search 索引佇列中的項目。針對佇列中的每個項目,在 getDoc() 方法中執行下列步驟:

  1. 檢查 Cloud Search Indexing 佇列中項目 ID 是否存在於存放區。如果不是,請從索引中刪除項目。如果該項目確實存在,請繼續執行下一個步驟。

  2. 已變更索引或新項目:

    1. 設定權限。
    2. 為要建立索引的項目設定中繼資料。
    3. 將中繼資料和項目合併成一個可建立索引的 RepositoryDoc
    4. 將子 ID 放在 Cloud Search 索引佇列中,以便進行後續處理。
    5. 傳回 RepositoryDoc

處理已刪除的項目

下列程式碼片段說明如何判斷項目是否存在於索引中,以及如何刪除該項目,而非刪除。

GraphTraversalSample.java
String resourceName = item.getName();
if (documentExists(resourceName)) {
  return buildDocumentAndChildren(resourceName);
}
// Document doesn't exist, delete it
log.info(() -> String.format("Deleting document %s", resourceName));
return ApiOperations.deleteItem(resourceName);

設定項目的權限

您的存放區使用存取控制清單 (ACL) 來識別擁有項目存取權的使用者或群組。ACL 是可存取項目的群組或使用者的 ID 清單。

您必須複製存放區使用的 ACL,以確保只有具備項目存取權的使用者才能在搜尋結果中看到該項目。為項目建立索引時,必須納入項目的 ACL,Google Cloud Search 才能具備該項目的正確存取層級資訊。

Content Connector SDK 提供豐富的 ACL 類別和方法,可模擬大多數存放區的 ACL。您必須分析存放區中每個項目的 ACL,並在將項目編入索引時,為 Google Cloud Search 建立對應的 ACL。如果存放區的 ACL 採用 ACL 繼承等概念,對 ACL 建立模型可能並不容易。如要進一步瞭解 Google Cloud Search ACL,請參閱 Google Cloud Search ACL

注意:Cloud Search Indexing API 支援單一網域 ACL。不支援跨網域 ACL。使用 Acl.Builder 類別即可透過 ACL 設定每個項目的存取權。下列程式碼片段取自完整週遊範例,可讓所有使用者或「主體」(getCustomerPrincipal()) 在搜尋時成為所有項目的「讀取者」(.setReaders())。

FullTraversalSample.java
// Make the document publicly readable within the domain
Acl acl = new Acl.Builder()
    .setReaders(Collections.singletonList(Acl.getCustomerPrincipal()))
    .build();

您必須瞭解 ACL 才能妥善為存放區建立 ACL。舉例來說,您可能會為使用某種繼承模型的檔案系統中的檔案建立索引,而子項資料夾會沿用上層資料夾的權限。建立模型 ACL 繼承需要 Google Cloud Search ACL 中說明的額外資訊

設定項目的中繼資料

中繼資料會儲存在 Item 物件中。如要建立 Item,您至少需要該項目的專屬字串 ID、項目類型、ACL、網址和版本。下列程式碼片段說明如何使用 IndexingItemBuilder 輔助類別建構 Item

GraphTraversalSample.java
// Url is required. Use google.com as a placeholder for this sample.
String viewUrl = "https://www.google.com";

// Version is required, set to current timestamp.
byte[] version = Longs.toByteArray(System.currentTimeMillis());

// Using the SDK item builder class to create the document with
// appropriate attributes. This can be expanded to include metadata
// fields etc.
Item item = IndexingItemBuilder.fromConfiguration(documentId)
    .setItemType(IndexingItemBuilder.ItemType.CONTENT_ITEM)
    .setAcl(acl)
    .setSourceRepositoryUrl(IndexingItemBuilder.FieldOrValue.withValue(viewUrl))
    .setVersion(version)
    .build();

建立可建立索引的項目

設定項目的中繼資料後,即可使用 RepositoryDoc.Builder 建立實際的可建立索引項目。以下範例說明如何建立可建立索引的單一項目。

GraphTraversalSample.java
// For this sample, content is just plain text
String content = String.format("Hello world from sample doc %s", documentId);
ByteArrayContent byteContent = ByteArrayContent.fromString("text/plain", content);

RepositoryDoc.Builder docBuilder = new RepositoryDoc.Builder()
    .setItem(item)
    .setContent(byteContent, IndexingService.ContentFormat.TEXT);

RepositoryDoc 是一種 ApiOperation 類型,會執行實際 IndexingService.indexItem() 要求。

您也可以使用 RepositoryDoc.Builder 類別的 setRequestMode() 方法,將索引要求識別為 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
非同步模式會縮短建立索引提供服務的延遲時間,並適用於索引要求的大量處理配額。對於整個存放區的初始索引 (補充作業),建議採用非同步模式。
SYNCHRONOUS
同步模式會造成建立索引的延遲時間較短,以有限的處理量配額。建議您採用同步模式來為存放區更新和變更建立索引。如果未指定,要求模式會預設為 SYNCHRONOUS

將子 ID 置於 Cloud Search Indexing 佇列

下列程式碼片段說明如何將目前處理父項項目的子 ID 加入佇列以供處理。系統會在父項項目建立索引後處理這些 ID。

GraphTraversalSample.java
// Queue the child nodes to visit after indexing this document
Set<String> childIds = getChildItemNames(documentId);
for (String id : childIds) {
  log.info(() -> String.format("Pushing child node %s", id));
  PushItem pushItem = new PushItem();
  docBuilder.addChildId(id, pushItem);
}

RepositoryDoc doc = docBuilder.build();

後續步驟

以下是您可能的後續步驟:

使用 REST API 建立內容連接器

以下各節說明如何使用 REST API 建立內容連接器。

決定週遊策略

內容連接器的主要功能是掃遍存放區並為資料建立索引。您必須根據存放區中的資料大小和版面配置實作週遊策略。以下是三種常見的周遊策略:

完整週遊策略

完整週遊策略會掃描整個存放區,並盲目為每個項目建立索引。這種策略通常適用於小型存放區,且可負擔每次建立索引時執行完整週遊的負擔。

此週遊策略適用於大部分靜態非階層資料的小型存放區。當存放區難以偵測變更或不支援變更時,您也可以使用這個週遊策略。

清單週遊策略

清單遍歷策略會掃描整個存放區 (包括所有子節點),以決定每個項目的狀態。然後,連接器會接受第二條傳遞,且只會為自上次索引建立後新增或已更新的項目建立索引。此策略通常用於對現有索引執行漸進式更新 (而不是在每次更新索引時執行完整週遊)。

當存放區難以偵測或不支援變更、包含非階層式資料,且您正在使用非常大型的資料集時,這個週遊策略就非常實用。

繪製週遊圖表

圖形週遊策略會掃描整個父項節點,以判斷每個項目的狀態。然後,連接器會進行第二次傳遞,且只會將根節點中的項目編入索引,或自上次索引建立後經過更新。最後,連接器會傳遞任何子 ID,然後將子節點中全新或已更新的項目編入索引。連接器會持續在所有子節點中遞迴,直到所有項目都得到解決。這類週遊通常用於階層存放區,其中列出所有 ID 都不實際。

如果您需要檢索的階層資料 (例如係列目錄或網頁),則適合採用此策略。

實作週遊策略並建立索引項目

Cloud Search 中每個可建立索引的元素都稱為 Cloud Search API 中的「項目」。項目可以是檔案、資料夾、CSV 檔案中的一行或資料庫記錄。

結構定義註冊完成後,您就可以透過下列方式填入索引:

  1. (選用) 使用 items.upload 上傳大於 100KiB 的檔案,然後建立索引。如果是較小的檔案,請使用 items.indexinlineContent 嵌入內容。

  2. (選用) 使用 media.upload 上傳要建立索引的媒體檔案。

  3. 使用 items.index 為項目建立索引。舉例來說,如果您的結構定義使用電影結構定義中的物件定義,單一項目的索引要求會如下所示:

    {
      "name": "datasource/<data_source_id>/items/titanic",
      "acl": {
        "readers": [
          {
            "gsuitePrincipal": {
              "gsuiteDomain": true
            }
          }
        ]
      },
      "metadata": {
        "title": "Titanic",
        "viewUrl": "http://www.imdb.com/title/tt2234155/?ref_=nv_sr_1",
        "objectType": "movie"
      },
      "structuredData": {
        "object": {
          "properties": [
            {
              "name": "movieTitle",
              "textValues": {
                "values": [
                  "Titanic"
                ]
              }
            },
            {
              "name": "releaseDate",
              "dateValues": {
                "values": [
                  {
                    "year": 1997,
                    "month": 12,
                    "day": 19
                  }
                ]
              }
            },
            {
              "name": "actorName",
              "textValues": {
                "values": [
                  "Leonardo DiCaprio",
                  "Kate Winslet",
                  "Billy Zane"
                ]
              }
            },
            {
              "name": "genre",
              "enumValues": {
                "values": [
                  "Drama",
                  "Action"
                ]
              }
            },
            {
              "name": "userRating",
              "integerValues": {
                "values": [
                  8
                ]
              }
            },
            {
              "name": "mpaaRating",
              "textValues": {
                "values": [
                  "PG-13"
                ]
              }
            },
            {
              "name": "duration",
              "textValues": {
                "values": [
                  "3 h 14 min"
                ]
              }
            }
          ]
        }
      },
      "content": {
        "inlineContent": "A seventeen-year-old aristocrat falls in love with a kind but poor artist aboard the luxurious, ill-fated R.M.S. Titanic.",
        "contentFormat": "TEXT"
      },
      "version": "01",
      "itemType": "CONTENT_ITEM"
    }
    
  4. (選用) 使用 items.get 呼叫來驗證項目已建立索引。

如要執行完整週遊,請定期重新建立索引整個存放區的索引。如要執行清單或圖形週遊,您必須實作程式碼來處理存放區變更

處理存放區變更

您可以定期從存放區收集每個項目並為其建立索引,以執行完整的索引。雖然可以有效確保索引隨時更新,但處理較大或階層的存放區時,可能會耗用大量索引。

您也可以使用 Google Cloud Indexing 佇列,追蹤變更情形,並只為已變更的項目建立索引,而不要透過索引呼叫頻繁地為整個存放區建立索引。您可以使用 items.push 要求,將項目推送到佇列,以供稍後輪詢及更新。如要進一步瞭解 Google Cloud Indexing 佇列,請參閱 Google Cloud 索引佇列

如要進一步瞭解 Google Cloud Search API,請參閱 Cloud Search API