建立內容連接器

內容連接器是一種軟體程式,可遍歷企業存放區中的資料,並填入資料來源。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 建立內容連接器

下列各節說明如何使用 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,確保只有有權存取項目的使用者,才能在搜尋結果中看到該項目。為項目建立索引時,必須一併加入項目的存取控制清單,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();

RepositoryDocApiOperation 的一種,可執行實際的 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 索引佇列,並一次擷取一個項目 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 索引佇列。系統通常會先推送根 ID 或父項 ID,然後推送子項 ID,直到處理完所有項目階層為止。

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

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

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

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,確保只有有權存取項目的使用者,才能在搜尋結果中看到該項目。為項目建立索引時,必須一併加入項目的存取控制清單,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();

RepositoryDocApiOperation 類型,可執行實際的 IndexingService.indexItem() 要求。

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

ASYNCHRONOUS
非同步模式會導致索引到服務的延遲時間較長,並可容納大量索引要求配額。建議使用非同步模式,為整個存放區建立初始索引 (回填)。
SYNCHRONOUS
同步模式可縮短從建立索引到提供服務的延遲時間,並配合有限的總處理量配額。建議使用同步模式,為存放區的更新和變更建立索引。如果未指定,要求模式預設為 SYNCHRONOUS

後續步驟

您可以採取下列幾個後續步驟:

使用範本類別建立圖形遍歷連接器

Cloud Search 索引佇列用於保存存放區中每個項目的 ID 和選用雜湊值。圖形遍歷連接器會將項目 ID 推送至 Google Cloud Search 索引佇列,並一次擷取一個項目 ID 以建立索引。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 和雜湊值,並將其插入 PushItemsPushItems 是將項目推送至 Cloud Search 索引佇列的 ApiOperation 要求。

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,確保只有有權存取項目的使用者,才能在搜尋結果中看到該項目。為項目建立索引時,必須一併加入項目的存取控制清單,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);

RepositoryDocApiOperation 的一種,可執行實際的 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 中,每個可建立索引的 Cloud Search 元素都稱為「項目」。項目可以是檔案、資料夾、CSV 檔案中的一行,或是資料庫記錄。

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

  1. (選用) 使用 items.upload 上傳超過 100 KiB 的檔案以供建立索引。如果是較小的檔案,請使用 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 Queue 做為追蹤變更的機制,只為變更的項目建立索引,不必定期使用索引呼叫為整個存放區建立索引。您可以使用 items.push 要求將項目推送到佇列,以供稍後輪詢及更新。如要進一步瞭解 Google Cloud 索引佇列,請參閱「Google Cloud 索引佇列」。

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