建立內容連接器

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

  • Content Connector 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 建立內容連接器

以下各節說明如何使用 Content Connector 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 Indexing 佇列可用來保留存放區中每個項目的 ID 和選用雜湊值。清單週遊連接器會將項目 ID 推送至 Google Cloud Search 索引佇列,並逐一擷取這些項目來建立索引。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 索引佇列。根 ID 或父項 ID 通常會先推送子項 ID,直到整個項目階層處理完畢。

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

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

下列程式碼片段說明如何取得每個項目 ID 和雜湊值,並插入 PushItems 中。PushItemsApiOperation 要求,用於將項目推送至 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 Indexing 佇列可用來保留存放區中每個項目的 ID 和選用雜湊值。圖表週遊連接器會將項目 ID 推送至 Google Cloud Search 索引佇列,並逐一擷取這些項目來建立索引。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 索引佇列。根 ID 或父項 ID 通常會先推送子項 ID,直到整個項目階層處理完畢。

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

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

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

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 索引佇列中的項目 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 索引佇列

下列程式碼片段說明如何將目前處理父項項目的子項 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 API 中,每個可建立索引的元素都稱為「項目」。項目可以是檔案、資料夾、CSV 檔案中的行,或資料庫記錄。

註冊結構定義後,即可透過以下方式填入索引:

  1. (選用) 使用 items.upload 上傳大小超過 100KiB 的檔案以建立索引。如果是較小的檔案,請使用 items.index 將內容嵌入為 inlineContent

  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 呼叫來驗證 item 是否已建立索引。

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

處理存放區變更

您可以定期收集存放區中的每個項目並建立索引,以利執行完整索引。雖然能有效確保索引是最新的,但在處理大型或階層存放區時,完整索引可能會耗費成本。

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

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