建立內容連接器

內容連接器是一種軟體程式,可遍歷企業存放區中的資料,並填入資料來源。Google 提供下列選項,供您開發內容連接器:

  • 內容連接器 SDK。這個選項很適合 Java 程式設計師。這個 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 建立內容連接器。

設定依附元件

在建構檔案中加入這些依附元件。

Maven

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

Gradle

groovy 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 提供參數。您必須在設定檔中宣告下列項目:

  • 內容連結器:宣告 api.sourceIdapi.serviceAccountPrivateKeyFile。這些資訊會識別您的儲存庫,以及存取所需的私密金鑰。
  • 身分識別連接器:宣告 api.identitySourceId,以識別外部身分識別來源。如要同步處理使用者,請一併宣告 api.customerId (Google Workspace 帳戶的專屬 ID)。

如要覆寫其他 Google 提供的參數預設值,請一併宣告這些參數。如要瞭解如何產生 ID 和金鑰,請參閱「Google 提供的參數」。

您也可以在設定檔中定義存放區專屬參數。

將設定檔傳遞至連接器

設定 config 系統屬性,傳遞設定檔。啟動連接器時,請使用 -D 引數。例如:

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

如果省略這個引數,SDK 會嘗試使用本機目錄中名為 connector-config.properties 的檔案。

決定遍歷策略

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

完整遍歷策略
掃描整個存放區,並為每個項目建立索引。這項策略最適合小型存放區,因為您可以在每次建立索引時,負擔完整遍歷的額外負荷。適用於小型存放區,其中大多是靜態、非階層式資料,或難以偵測變更的存放區。
清單遍歷策略
掃描整個存放區,判斷每個項目的狀態,然後只為新項目或更新項目建立索引。如果系統不支援變更偵測,請使用這項功能,針對大型非階層式索引進行增量更新。
圖形遍歷
掃描父項節點,判斷當中項目的狀態,然後為該節點中的新項目或更新項目建立索引。然後遞迴處理子節點。如果存放區具有階層結構,列出所有 ID 並不實際,例如目錄結構或網站,請使用這項功能。

SDK 會在範本連接器類別中實作這些策略。這些範本可加快開發速度。如要使用範本,請參閱對應的章節:

使用範本類別建立完整遍歷的連接器

本節會參考 FullTraversalSample 中的程式碼。

實作連接器進入點

進入點是 main() 方法。這會建立 Application 執行個體,並呼叫 start() 來執行連接器。

呼叫 application.start() 前,請使用 IndexingApplication.Builder 類別例項化 FullTraversalConnector 範本。這個範本會接受 Repository 物件。

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. 確保 Configuration 尚未初始化。
  2. 使用 Google 提供的鍵/值組合初始化 Configuration 物件。

實作 Repository 介面

Repository 物件會遍歷存放區項目並建立索引。使用範本時,您只需要覆寫 Repository 介面中的特定方法。針對 FullTraversalConnector,覆寫:

  • init():用於存放區設定和初始化。
  • getAllDocs():遍歷並為所有項目建立索引。系統會針對每個排定的遍歷作業呼叫一次這個函式。
  • (選用) getChanges(): 如果存放區支援變更偵測,請覆寫此項目,以擷取及編製修改過的項目索引。
  • (選用) close(): 在關機期間清除存放區。

每個方法都會傳回 ApiOperation 物件,該物件會使用 IndexingService.indexItem() 執行索引作業。

取得自訂設定參數

如要處理連接器的設定,您必須從 Configuration 物件擷取任何自訂參數。在 Repository 類別的 init() 方法中執行這項工作。

Configuration 類別包含擷取不同資料類型的方法。每個方法都會傳回 ConfigValue 物件。使用 ConfigValue 物件的 get() 方法擷取值。以下程式碼片段來自 FullTraversalSample,說明如何擷取自訂整數值:

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() 即可執行完整遍歷。這個方法會接受檢查點,以便在索引作業中斷後繼續執行。針對每個項目:

  1. 設定權限。
  2. 設定中繼資料。
  3. 將這些值合併為RepositoryDoc
  4. 將每個項目封裝到 getAllDocs() 傳回的疊代器中。

如果項目集過大,無法一次呼叫,請使用檢查點並呼叫 hasMore(true)

設定項目的權限

存放區會使用存取控制清單 (ACL),識別有權存取項目的使用者或群組。ACL 會列出已獲授權的使用者或群組 ID。

為確保使用者只會看到有權存取的搜尋結果,您必須複製存放區的 ACL。為項目建立索引時,請一併加入 ACL,讓 Google Cloud Search 提供正確的存取層級。

Content Connector SDK 包含類別和方法,可為大多數存放區的 ACL 建立模型。分析存放區的 ACL,並在建立索引時為 Cloud Search 建立對應的 ACL。如要建立複雜的存取控制清單 (例如使用繼承的清單),需要仔細規劃。詳情請參閱「Cloud Search ACL」。

使用 Acl.Builder 類別設定存取權。這個程式碼片段來自完整遍歷範例,可讓所有網域使用者 (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),需要這篇文章中的 Cloud Search ACL 資訊。

設定項目的中繼資料

中繼資料會儲存在 Item 物件中。如要建立 Item,您需要專屬 ID、項目類型、ACL、網址和版本。使用 IndexingItemBuilder 輔助程式類別。

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 是執行 IndexingService.indexItem() 要求的 ApiOperation

使用 RepositoryDoc.Builder 類別的 setRequestMode() 方法,將索引要求設為 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
這個模式的索引編製到服務延遲時間較長,但可容納較大的輸送量配額。對整個存放區進行初始索引 (回填) 時,請使用非同步模式。
SYNCHRONOUS
這個模式的索引編製到放送延遲時間較短,但輸送量配額較小。使用同步模式為存放區更新和變更建立索引。如未指定,要求模式預設為 SYNCHRONOUS
將每個可建立索引的項目封裝在疊代器中

getAllDocs() 方法會傳回 CheckpointCloseableIterableRepositoryDoc 物件。使用 CheckpointCloseableIterableImpl.Builder 類別。

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

後續步驟

使用範本類別建立清單遍歷連接器

Cloud Search 索引佇列會保留存放區項目的 ID 和選用雜湊。清單遍歷連接器會將 ID 推送至這個佇列,並擷取 ID 以進行索引。Cloud Search 會維護這些佇列,以判斷項目狀態 (例如刪除)。請參閱「Cloud Search Indexing Queue」。

本節是指 ListTraversalSample

實作連接器進入點

main() 方法會建立 Application 執行個體並呼叫 start()。使用 IndexingApplication.Builder 例項化 ListingConnector 範本。

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();
}

實作 Repository 介面

覆寫 ListingConnector 的下列方法:

  • init():用於存放區設定。
  • getIds():擷取所有記錄的 ID 和雜湊值。
  • getDoc(): 用於在索引中新增、更新或刪除項目。
  • (選用) getChanges():使用變更偵測進行漸進式更新。
  • (選用) close():用於清除存放區。

執行清單遍歷

覆寫 getIds() 以擷取 ID 和雜湊值。覆寫 getDoc(),處理 Cloud Search 索引佇列中的每個項目。

推送項目 ID 和雜湊值

覆寫 getIds() 以擷取 ID 和內容雜湊。將這些項目封裝成索引佇列的要求。PushItems

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 和雜湊值封裝。

ListTraversalSample.java
ApiOperation pushOperation = allIds.build();
CheckpointCloseableIterable<ApiOperation> iterator =
  new CheckpointCloseableIterableImpl.Builder<>(
      Collections.singletonList(pushOperation))
  .build();
return iterator;
擷取及處理每個項目

覆寫 getDoc(),處理索引佇列中的項目。項目可以是新的、經過修改、未變更或已刪除。

  1. 檢查存放區中是否有該項目 ID。如果不需要,則請刪除。
  2. 輪詢索引的狀態。如果沒有變更 (ACCEPTED),請勿執行任何動作。
  3. 已變更或新增的索引項目:設定權限、設定中繼資料、合併為 RepositoryDoc,然後傳回。
處理已刪除的項目

這個程式碼片段說明如何判斷項目是否存在,如果不存在則刪除。

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);
}
處理未變更的項目

輪詢索引佇列,處理未變更的項目。

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,並在建立索引時為 Cloud Search 建立對應的 ACL。如要建立複雜的存取控制清單 (例如使用繼承的清單),需要仔細規劃。詳情請參閱「Cloud Search ACL」。

使用 Acl.Builder 類別設定存取權。這個程式碼片段來自完整遍歷範例,可讓所有網域使用者 (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),需要這篇文章中的 Cloud Search ACL 資訊。

設定項目的中繼資料
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();
建立可編列索引的項目
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.Builder 類別的 setRequestMode() 方法,將索引要求設為 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
這個模式的索引編製到服務延遲時間較長,但可容納較大的輸送量配額。對整個存放區進行初始索引 (回填) 時,請使用非同步模式。
SYNCHRONOUS
這個模式的索引編製到放送延遲時間較短,但輸送量配額較小。使用同步模式為存放區更新和變更建立索引。如未指定,要求模式預設為 SYNCHRONOUS

後續步驟

建議您採取下列後續步驟:

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

Cloud Search 索引佇列會保留存放區中每個項目的 ID 和選用雜湊值。圖形遍歷連接器會將項目 ID 推送至 Google Cloud Search 索引佇列,並一次擷取一個項目 ID 以建立索引。Google Cloud Search 會維護佇列並比較佇列內容,以判斷項目狀態,例如項目是否已從存放區刪除。如要進一步瞭解 Cloud Search 索引佇列,請參閱「Google Cloud Search 索引佇列」。

建立索引時,系統會從資料存放區擷取項目內容,並將所有子項 ID 推送到佇列。連接器會遞迴處理父項和子項 ID,直到處理完所有項目為止。

實作連接器的進入點

連接器的進入點是 main() 方法。這個方法會建立 Application 類別的例項,並呼叫其 start() 方法來執行連接器。

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

實作 Repository 介面

覆寫 init()getIds()getDoc(),以及選用的 getChanges()close()

執行圖形遍歷

覆寫 getIds() 以擷取初始 ID,並覆寫 getDoc() 以處理項目,然後將子項 ID 推送至佇列。

推送項目 ID 和雜湊值
GraphTraversalSample.java
PushItems.Builder allIds = new PushItems.Builder();
PushItem item = new PushItem();
allIds.addPushItem("root", item);
擷取及處理每個項目
  1. 檢查存放區中是否有該 ID。如果不需要,請刪除該項目。
  2. 如果是現有項目,請設定權限和中繼資料,然後將兩者合併為 RepositoryDoc
  3. 將子項 ID 推送至索引佇列。
  4. 傳回 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);
設定中繼資料並建立項目
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();
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);
將子項 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 建立內容連接器。

決定遍歷策略

這些策略 (完整、清單和圖表) 的概念與 SDK 相同。使用 REST API 實作所選策略。

實作遍歷策略並為項目建立索引

註冊結構定義,然後使用下列項目填入索引:

  1. (選用) items.upload 適用於超過 100 KiB 的檔案。
  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/",
        "objectType": "movie"
      },
      "structuredData": {
        "object": {
          "properties": [
            {
              "name": "movieTitle",
              "textValues": { "values": ["Titanic"] }
            }
          ]
        }
      },
      "content": {
        "inlineContent": "A seventeen-year-old aristocrat falls in love...",
        "contentFormat": "TEXT"
      },
      "version": "01",
      "itemType": "CONTENT_ITEM"
    }
    
  4. (選用) 使用 items.get 驗證索引。

處理存放區變更

定期為整個存放區重新建立索引,以完成完整索引作業。如要進行清單或圖形遍歷,請使用 Google Cloud Indexing Queue 追蹤變更,並只為變更的內容建立索引。使用 items.push 將項目加入待播清單。