创建内容连接器

内容连接器是一种软件程序,用于遍历企业代码库中的数据并填充数据源。针对内容连接器的开发,Google 提供以下选项:

  • 内容连接器 SDK。如果您正在编程 。内容连接器 SDK 是 REST API 的封装容器,可让您快速创建连接器。要使用此 SDK 创建内容连接器,请参阅使用内容连接器 SDK 创建内容连接器

  • 低层级 REST API 或 API 库。如果您不用 Java 编程,或者您的代码库更适合 REST API 或库,请使用这些选项。要使用 REST API 创建内容连接器,请参阅使用 REST API 创建内容连接器

一个典型的内容连接器会执行以下任务:

  1. 读取和处理配置参数。
  2. 从第三方内容代码库中提取离散的可索引数据块,即“项”
  3. 将 ACL、元数据和内容数据合并到可索引项中。
  4. 将项编入 Cloud Search 数据源的索引中。
  5. (可选)侦听来自第三方内容代码库的更改通知。更改通知将转换为索引请求,使 Cloud Search 数据源与第三方代码库保持同步。连接器仅在代码库支持更改检测的情况下执行此任务。

使用内容连接器 SDK 创建内容连接器

以下部分介绍如何使用内容连接器 SDK 创建内容连接器。

设置依赖项

您必须在构建文件中加入特定的依赖项才能使用 SDK。请点击下面的标签查看构建环境的依赖项:

Maven

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

Gradle

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

创建连接器配置

每个连接器都有一个配置文件,其中包含连接器使用的参数,例如代码库的 ID。参数定义为 键值对,例如 api.sourceId=1234567890abcdef

Google Cloud Search SDK 包含 Google 提供的若干个配置参数,可供所有连接器使用。您必须在配置文件中声明以下由 Google 提供的参数:

  • 对于内容连接器,您必须声明 api.sourceIdapi.serviceAccountPrivateKeyFile,因为这些参数用于标识营业地点 和访问代码库所需的私钥。
  • 对于身份连接器,您必须按如下方式声明 api.identitySourceId: 参数用于标识外部身份源的位置。如果您 同步用户,您还必须声明 api.customerId 作为 贵企业的 Google Workspace 账号。

除非您想要覆盖其他由 Google 提供的 参数,则无需在配置文件中声明它们。 有关 Google 提供的配置参数的其他信息, 有关如何生成特定 ID 和密钥的信息,请参阅 Google 提供的配置参数

此外,您还可以定义代码库的专属参数,以便在配置文件中使用。

将配置文件传递给连接器

设置系统属性 config 以将配置文件传递给 连接器。您可以在启动时使用 -D 参数设置该属性 连接。例如,以下命令会启动连接器 替换为 MyConfig.properties 配置文件:

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

如果缺少此参数,SDK 会尝试访问默认配置 文件名为 connector-config.properties 的文件。

确定您的遍历策略

内容连接器的主要功能是遍历代码库并为其数据编制索引。您必须根据代码库中数据的大小和布局实现遍历策略。您可以设计自己的专属策略,也可以从 SDK 中实现的以下策略中进行选择:

完全遍历策略

完全遍历策略扫描整个代码库,并不加分辨地将每一项都编入索引。如果您的代码库规模较小,并且能够负担得起每次编制索引都执行完全遍历的开销,通常可以使用此策略。

这一遍历策略适用于大部分数据都处于静态且不分层的小型代码库。当代码库难以执行或完全不支持更改检测时,您也可以使用此遍历策略。

列表遍历策略

列表遍历策略扫描整个代码库,包括所有子节点,来确定每一项的状态。然后,连接器进行第二次遍历,仅将自上次编制索引以来添加的新项或已更新的项编入索引。此策略通常用于对现有索引执行增量更新(无需在每次更新索引时都执行完全遍历)。

当代码库难以执行或不支持更改检测、您具有非分层数据,并且您要处理庞大的数据集时,此遍历策略非常适用。

图形遍历

图形遍历策略扫描整个父节点,确定每一项的状态。然后,连接器进行第二次遍历,仅创建索引 根节点中的项是新项或自上次编入索引后已更新的项。 最后,连接器传递所有子 ID,然后将子节点中的项编入索引 新问题或更新后的问题连接器以递归方式继续遍历所有子节点,直到处理完所有项。这一遍历方法通常用于分层代码库,在此种代码库中,很难列出所有 ID。

如果您拥有需要抓取的分层数据,例如一系列目录或网页,则此策略非常适用。

这些遍历策略中的每一个策略都由模板连接器实现 类。虽然您可以实施自己的遍历策略 模板可大大加快连接器的开发速度。要使用模板创建连接器,请转到与您的遍历策略对应的部分:

使用模板类创建完全遍历连接器

本文档的这一部分引用了 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 会在后台调用 initConfig() 方法(在连接器的 main() 方法调用之后) Application.build。 通过 initConfig() 种方式 执行以下任务:

  1. 调用 Configuation.isInitialized() 方法确保 Configuration 尚未初始化。
  2. 使用 Google 提供的键值对初始化 Configuration 对象 对。每个键值对都存储在 ConfigValue 包含在 Configuration 对象中。

实现 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,例如将其编入索引。

如果项太多,以致无法在单次调用中处理,请添加 检查点并设置 hasMore(true) 以指明有更多可编入索引的项。

设置项的权限

您的代码库使用访问控制列表 (ACL) 来标识对某一项有访问权限的用户或组。ACL 是可以访问该项的组或用户的 ID 列表。

您必须复制代码库使用的 ACL,以确保只有这些用户 拥有某项内容访问权限的用户可在搜索结果中看到该项。通过 在将某项内容编入索引时,必须包含该项的 ACL,以便 Google Cloud Search 获得所需的信息来向其提供正确的访问权限级别 内容。

内容连接器 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、网址和版本。 以下代码段展示了如何使用Item 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 是一种 ApiOperation,用于执行实际 IndexingService.indexItem() 请求。

您还可以使用 setRequestMode() 方法 RepositoryDoc.Builder 类将索引请求标识为 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
异步模式会导致从索引到服务的延迟时间更长, 提供用于索引请求的较大吞吐量配额。异步模式 建议对整个代码库进行初始索引编制(回填)。
SYNCHRONOUS
同步模式可缩短索引到服务的延迟时间, 可以满足有限的吞吐量配额建议在对代码库的更新和变更执行编入索引时使用同步模式。如果 未指定,请求模式默认为 SYNCHRONOUS

在迭代器中打包每个可索引项

getAllDocs() 方法会返回一个 Iterator,具体来说, CheckpointCloseableIterable, / RepositoryDoc 对象的操作。您可以使用 CheckpointClosableIterableImpl.Builder 类来构建和返回迭代器。以下代码段显示了如何构建和返回迭代器。

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

SDK 执行迭代器中包含的每个索引调用。

后续步骤

您可以执行以下几个后续步骤:

使用模板类创建列表遍历连接器

Cloud Search Indexing Queue 用于保存代码库中每一项的 ID 和可选哈希值。列表遍历连接器将项 ID 推送到 Google Cloud Search Indexing Queue,并以每次检索一个的形式以将其编入索引。Google Cloud Search 维护队列并比较队列内容以确定项的状态,例如是否已从代码库中删除项。如需详细了解 Cloud Search 索引队列,请参阅 Cloud Search Indexing Queue

本文档的这一部分引用了 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 会在后台调用 initConfig() 方法(在连接器的 main() 方法调用之后) Application.buildinitConfig() 方法:

  1. 调用 Configuation.isInitialized() 方法确保 Configuration 尚未初始化。
  2. 使用 Google 提供的键值对初始化 Configuration 对象 对。每个键值对都存储在 ConfigValue 包含在 Configuration 对象中。

实现 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 Indexing Queue 中的每一项。

推送项 ID 和哈希值

替换 getIds() 从 存储库然后将 ID 和哈希值对打包到指向 Cloud Search Indexing Queue 的推送操作请求中。一般来说,首先会推送根 ID 或父 ID,然后是子 ID,直到处理完整个项的层次结构。

getIds() 方法接受表示待执行的最后一项的检查点 编入索引。如果进程被中断,可使用该检查点在特定项处恢复索引。对代码库中的每个项执行以下命令: getIds() 方法中的步骤进行操作:

以下代码段展示了如何获取每个项 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 Indexing Queue 以进一步处理。

检索并处理每一项

替换 getDoc(),用于处理 Cloud Search Indexing Queue 中的每一项。 来源中的项可以是新增、已修改、未更改或不再存在的 存储库检索新的或已修改的各个项,并将其编入索引。从索引中移除不再存在于源代码库中的项。

getDoc() 方法接受来自 Google Cloud Search 的项 索引编制队列。对于队列中的每一项,请在 getDoc() 方法:

  1. 检查 Cloud Search Indexing Queue 中的项 ID 是否存在于代码库中。如果没有,请从索引中删除相应项。

  2. 轮询索引以获取项的状态,如果项未更改 (ACCEPTED),则不执行任何操作。

  3. 将已更改或新添加的项编入索引:

    1. 设置权限。
    2. 为要编入索引的项设置元数据。
    3. 将元数据和该项合并到一个可编入索引的内容中 RepositoryDoc
    4. 返回 RepositoryDoc

注意ListingConnector 模板不支持在null getDoc() 方法。返回 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 Indexing Queue 中轮询项的状态并处理未更改的项。

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 获得所需的信息来向其提供正确的访问权限级别 内容。

内容连接器 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、网址和版本。 以下代码段展示了如何使用Item IndexingItemBuilder 辅助类。

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() 请求。

您还可以使用 setRequestMode() 方法 RepositoryDoc.Builder 类将索引请求标识为 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
异步模式会导致从索引到服务的延迟时间更长, 提供用于索引请求的较大吞吐量配额。异步模式 建议对整个代码库进行初始索引编制(回填)。
SYNCHRONOUS
同步模式可缩短索引到服务的延迟时间, 可以满足有限的吞吐量配额建议在对代码库的更新和变更执行编入索引时使用同步模式。如果 未指定,请求模式默认为 SYNCHRONOUS

后续步骤

您可以执行以下几个后续步骤:

使用模板类创建图形遍历连接器

Cloud Search Indexing Queue 用于保存代码库中每一项的 ID 和可选哈希值。图形遍历连接器将项 ID 推送到 Google Cloud Search Indexing Queue 中的索引,并且每次检索一个索引, 编入索引。Google Cloud Search 维护队列并比较队列内容以确定项的状态,例如是否已从代码库中删除项。如需进一步了解 Cloud Search Indexing Queue,请参阅 更改为 Google Cloud Search Indexing Queue

在编入索引期间,将从数据代码库中提取项的内容,并将任何子项 ID 推送到队列。连接器以递归的方式处理父 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 会在后台调用 initConfig() 方法(在连接器的 main() 方法调用之后) Application.buildinitConfig() 方法:

  1. 调用 Configuation.isInitialized() 方法确保 Configuration 尚未初始化。
  2. 使用 Google 提供的键值对初始化 Configuration 对象 对。每个键值对都存储在 ConfigValue 包含在 Configuration 对象中。

实现 Repository 接口

Google Analytics 跟踪代码 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 Indexing Queue 中的每一项。

推送项 ID 和哈希值

替换 getIds() 从 存储库然后将 ID 和哈希值对打包到指向 Cloud Search Indexing Queue 的推送操作请求中。一般来说,首先会推送根 ID 或父 ID,然后是子 ID,直到处理完整个项的层次结构。

getIds() 方法接受表示待执行的最后一项的检查点 编入索引。如果进程被中断,可使用该检查点在特定项处恢复索引。对代码库中的每个项执行以下命令: getIds() 方法中的步骤进行操作:

  • 从存储库中获取每个项 ID 和关联的哈希值。
  • 将每个 ID 和哈希值对打包到 PushItems 中。
  • 将每个 PushItems 合并到由 getIds() 方法。请注意,getIds() 实际上会返回 CheckpointCloseableIterable 它是 ApiOperation 对象中,每个对象代表对 RepositoryDoc ,例如将项推送到队列中。

以下代码段展示了如何获取每个项 ID 和哈希值以及 将其插入 PushItemsPushItems 是 将项推送到 Cloud Search Indexing Queue 的 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 Indexing Queue 以进一步处理。

检索并处理每一项

替换 getDoc(),用于处理 Cloud Search Indexing Queue 中的每一项。 来源中的项可以是新增、已修改、未更改或不再存在的 存储库检索新的或已修改的各个项,并将其编入索引。从索引中移除不再存在于源代码库中的项。

getDoc() 方法接受 Cloud Search Indexing 中的项 队列。对于队列中的每一项,请在 getDoc() 方法:

  1. 检查 Cloud Search Indexing Queue 中的项 ID 是否存在于代码库中。如果没有,请从索引中删除相应项。如果该项存在,请继续执行下一步操作。

  2. 将已更改或新添加的项编入索引:

    1. 设置权限。
    2. 为要编入索引的项设置元数据。
    3. 将元数据和该项合并到一个可编入索引的内容中 RepositoryDoc
    4. 将子 ID 放入到 Cloud Search Indexing Queue 中以进一步处理。
    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 获得所需的信息来向其提供正确的访问权限级别 内容。

内容连接器 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、网址和版本。 以下代码段展示了如何使用Item IndexingItemBuilder 辅助类。

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() 请求。

您还可以使用 setRequestMode() 方法 RepositoryDoc.Builder 类将索引请求标识为 ASYNCHRONOUSSYNCHRONOUS

ASYNCHRONOUS
异步模式会导致从索引到服务的延迟时间更长, 提供用于索引请求的较大吞吐量配额。异步模式 建议对整个代码库进行初始索引编制(回填)。
SYNCHRONOUS
同步模式可缩短索引到服务的延迟时间, 可以满足有限的吞吐量配额建议在对代码库的更新和变更执行编入索引时使用同步模式。如果 未指定,请求模式默认为 SYNCHRONOUS

将子 ID 放入到 Cloud Search Indexing Queue 中

以下代码段显示了如何将当前处理的父项的子 ID 包含到队列中以进行处理。系统将父项编入索引后便会处理这些 ID。

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

RepositoryDoc doc = docBuilder.build();

后续步骤

您可以执行以下几个后续步骤:

使用 REST API 创建内容连接器

以下部分介绍如何使用 REST API 创建内容连接器。

确定您的遍历策略

内容连接器的主要功能是遍历代码库并为其数据编制索引。您必须根据代码库中数据的大小和布局实现遍历策略。以下是三种常见的遍历策略:

完全遍历策略

完全遍历策略扫描整个代码库,并不加分辨地将每一项都编入索引。如果您的代码库规模较小,并且能够负担得起每次编制索引都执行完全遍历的开销,通常可以使用此策略。

这一遍历策略适用于大部分数据都处于静态且不分层的小型代码库。当代码库难以执行或完全不支持更改检测时,您也可以使用此遍历策略。

列表遍历策略

列表遍历策略扫描整个代码库,包括所有子节点,来确定每一项的状态。然后,连接器进行第二次遍历,仅将自上次编制索引以来添加的新项或已更新的项编入索引。此策略通常用于对现有索引执行增量更新(无需在每次更新索引时都执行完全遍历)。

当代码库难以执行或不支持更改检测、您具有非分层数据,并且您要处理庞大的数据集时,此遍历策略非常适用。

图形遍历

图形遍历策略扫描整个父节点,确定每一项的状态。然后,连接器进行第二次遍历,仅创建索引 根节点中的项是新项或自上次编入索引后已更新的项。 最后,连接器传递所有子 ID,然后将子节点中的项编入索引 新问题或更新后的问题连接器以递归方式继续遍历所有子节点,直到处理完所有项。这一遍历方法通常用于分层代码库,在此种代码库中,很难列出所有 ID。

如果您拥有需要抓取的分层数据,例如一系列目录或网页,则此策略非常适用。

实现遍历策略和索引项

Cloud Search 的每个可索引元素都称为一项 Cloud Search API。项目可以是文件、文件夹、CSV 文件中的一行或 数据库记录

注册架构后,您可以通过以下方式填充索引:

  1. (可选)使用 items.upload 上传大于 100KiB 的文件以编入索引。对于较小的文件,可将内容嵌入为 inlineContent 使用 items.index

  2. (可选)使用 media.upload 上传媒体文件以编入索引。

  3. 使用 items.index 将项编入索引。 例如,如果架构使用 movie 架构,即针对单个项目的索引请求 如下所示:

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

要执行完全遍历,您需要定期将整个代码库重新编入索引。如需执行列表或图形遍历,您需要实现 来处理代码库更改

处理存储库更改

您可以定期收集代码库中的每一项并将其编入索引,以执行完全索引。虽然完全索引能够有效确保您的索引是最新的,但在处理较大代码库或分层代码库时,完全索引的费用可能非常高昂。

您可以使用 Google Cloud Indexing Queue 作为跟踪更改的机制,并仅将已更改的项编入索引,而不是时常使用索引调用为整个代码库编制索引。您可以使用 items.push 请求将项推送到队列中,以便稍后进行轮询和更新。有关 请参阅 Google Cloud Indexing Queue

如需进一步了解 Google Cloud Search API,请参阅 Cloud Search API