建立內容連接器

內容連接器是一種軟體程式,用於遍歷企業存放區中的資料,並填入資料來源。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,確保只有有權存取項目的使用者,才能在搜尋結果中看到該項目。建立項目索引時,請務必納入項目的 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、項目類型、存取控制清單、網址和版本。下列程式碼片段說明如何使用 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() 方法會傳回 RepositoryDoc 物件的 Iterator,具體來說是 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,直到整個項目階層都已處理完畢為止。

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

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

下列程式碼片段說明如何取得每個項目 ID 和雜湊值,並將這些值插入 PushItemsPushItemsApiOperation 要求,用於將項目推送至 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。在 NullPointerException. 中傳回 null 結果

處理已刪除的項目

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

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、項目類型、存取控制清單、網址和版本。下列程式碼片段說明如何使用 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 索引佇列,並逐一擷取項目 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,直到整個項目階層都已處理完畢為止。

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

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

下列程式碼片段說明如何取得每個項目 ID 和雜湊值,並將這些值插入 PushItemsPushItemsApiOperation 要求,用於將項目推送至 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、項目類型、存取控制清單、網址和版本。下列程式碼片段說明如何使用 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 中的每個可索引元素在 Cloud Search API 中都稱為「項目」。項目可以是檔案、資料夾、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 索引佇列追蹤變更,並只為已變更的項目建立索引,而非使用索引呼叫來定期為整個存放區建立索引。您可以使用 items.push 要求將項目推送至佇列,以便日後進行輪詢和更新。如要進一步瞭解 Google Cloud 索引佇列,請參閱 Google Cloud 索引佇列

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