更新请求处理器

Solr 收到的每个更新请求都会通过一系列插件运行,这些插件称为更新请求处理器或URP

例如,这对于向正在编入索引的文档添加字段、更改特定字段的值或在传入文档不满足特定条件时删除更新非常有用。事实上,Solr 中数量惊人的功能都是作为更新处理器实现的,因此有必要了解此类插件如何工作以及它们在哪里配置。

URP 解剖和生命周期

更新请求处理器作为 的一部分创建,该链包含一个或多个更新处理器。Solr 创建了一个默认更新请求处理器链,其中包括几个更新请求处理器,这些处理器启用了 Solr 的基本功能。除非用户选择配置和指定不同的自定义更新请求处理器链,否则此默认链用于处理每个更新请求。

描述更新请求处理器最简单的方法是查看抽象类 UpdateRequestProcessor 的 Javadoc。每个 UpdateRequestProcessor 都必须具有一个对应的工厂类,该类扩展 UpdateRequestProcessorFactory。此工厂类由 Solr 用于创建此插件的新实例。这种设计提供了两个好处

  1. 更新请求处理器不需要线程安全,因为它只被一个请求线程使用,并且在请求完成后会被销毁。

  2. 工厂类可以接受配置参数并维护请求之间可能需要的任何状态。工厂类必须是线程安全的。

在加载 Solr 核心期间构建每个更新请求处理器链,并缓存直到核心卸载。在链中指定的每个 UpdateRequestProcessorFactory 也会实例化,并使用可能在 solrconfig.xml 中指定的配置进行初始化。

当 Solr 收到更新请求时,它会查找要用于此请求的更新链。使用相应的工厂为链中指定的每个 UpdateRequestProcessor 创建一个新实例。更新请求被解析为相应的 UpdateCommand 对象,这些对象通过链运行。每个 UpdateRequestProcessor 实例负责调用链中的下一个插件。它可以选择通过不调用下一个处理器来短路链,甚至可以通过抛出异常来中止进一步处理。

单个更新请求可能包含一批多个新文档或删除,因此 UpdateRequestProcessor 的相应 processXXX 方法将针对每个单独的更新调用多次。但是,可以保证单个线程将按顺序调用这些方法。

更新请求处理器配置

可以通过在 solrconfig.xml 中直接创建整个链,或在 solrconfig.xml 中创建单个更新处理器,然后通过请求参数指定所有处理器,在运行时动态创建链,来创建更新请求处理器链。

但是,在我们了解如何配置更新处理器链之前,我们必须了解默认更新处理器链,因为它提供了大多数自定义请求处理器链也需要的重要功能。

默认更新请求处理器链

如果在 solrconfig.xml 中未配置任何更新处理器链,Solr 将自动创建一个默认更新处理器链,该链将用于所有更新请求。此默认更新处理器链包含以下处理器(按顺序)

  1. LogUpdateProcessorFactory - 跟踪此请求期间处理的命令并记录它们

  2. DistributedUpdateProcessorFactory - 负责将更新请求分配到正确的节点,例如,将请求路由到正确分片的领导者,并将更新从领导者分配到每个副本。此处理器仅在 SolrCloud 模式下激活。

  3. RunUpdateProcessorFactory - 使用内部 Solr API 执行更新。

每个处理器都执行一项基本功能,因此任何自定义链通常都包含所有这些处理器。RunUpdateProcessorFactory 通常是任何自定义链中的最后一个更新处理器。

自定义更新请求处理器链

以下示例演示如何在 solrconfig.xml 中配置自定义链。

示例 dedupe updateRequestProcessorChain
<updateRequestProcessorChain name="dedupe">
  <processor class="solr.processor.SignatureUpdateProcessorFactory">
    <bool name="enabled">true</bool>
    <str name="signatureField">id</str>
    <bool name="overwriteDupes">false</bool>
    <str name="fields">name,features,cat</str>
    <str name="signatureClass">solr.processor.Lookup3Signature</str>
  </processor>
  <processor class="solr.LogUpdateProcessorFactory" />
  <processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>

在上述示例中,创建了一个名为“dedupe”的新更新处理器链,其中包含链中的 SignatureUpdateProcessorFactoryLogUpdateProcessorFactoryRunUpdateProcessorFactorySignatureUpdateProcessorFactory 进一步配置了不同的参数,例如“signatureField”、“overwriteDupes”等。此链是 Solr 如何配置为通过使用 name、features、cat 字段的值计算签名来执行文档去重,然后将其用作“id”字段的示例。您可能已经注意到,此链未指定 DistributedUpdateProcessorFactory。由于此处理器对于 Solr 正常运行至关重要,因此 Solr 将自动在不包含它的任何链中插入 DistributedUpdateProcessorFactory,紧靠在 RunUpdateProcessorFactory 之前。

RunUpdateProcessorFactory

不要忘记在 solrconfig.xml 中定义的任何链的末尾添加 RunUpdateProcessorFactory。否则,由该链处理的更新请求实际上不会影响已编入索引的数据。

将各个处理器配置为顶级插件

更新请求处理器也可以独立于 solrconfig.xml 中的链进行配置。

updateProcessor 配置
<updateProcessor class="solr.processor.SignatureUpdateProcessorFactory" name="signature">
  <bool name="enabled">true</bool>
  <str name="signatureField">id</str>
  <bool name="overwriteDupes">false</bool>
  <str name="fields">name,features,cat</str>
  <str name="signatureClass">solr.processor.Lookup3Signature</str>
</updateProcessor>
<updateProcessor class="solr.RemoveBlankFieldUpdateProcessorFactory" name="remove_blanks"/>

在这种情况下,配置了名为“signature”的 SignatureUpdateProcessorFactory 实例,并定义了名为“remove_blanks”的 RemoveBlankFieldUpdateProcessorFactory。一旦在 solrconfig.xml 中指定了上述内容,我们就可以在 solrconfig.xml 中的更新请求处理器链中引用它们,如下所示

updateRequestProcessorChain 配置
<updateProcessorChain name="custom" processor="remove_blanks,signature">
  <processor class="solr.RunUpdateProcessorFactory" />
</updateProcessorChain>

在 SolrCloud 中更新处理器

在用户管理的集群或单节点安装中,每个更新都将按顺序在链中的所有更新处理器中运行一次。但是,在 SolrCloud 中,更新请求处理器的行为需要特别考虑。

SolrCloud 的一项关键功能是路由和分发请求。对于更新请求,此路由由 DistributedUpdateRequestProcessor 实现,并且由于其重要功能,Solr 为此处理器赋予了特殊状态。

在 SolrCloud 集群中,链中 DistributedUpdateProcessor 之前 的所有处理器都将在从客户端接收更新的第一个节点上运行,而不管此节点是领导者还是副本。然后,DistributedUpdateProcessor 将更新转发到更新的相应分片领导者(或者在更新影响多个文档(例如按查询删除或提交)的情况下转发到多个领导者)。分片领导者使用事务日志应用部分文档更新,然后将更新转发到所有分片副本。领导者和每个副本运行链中 DistributedUpdateProcessor 之后 列出的所有处理器。

例如,考虑我们在上面一节中看到的“dedupe”链。假设存在一个 3 节点 SolrCloud 集群,其中节点 A 托管分片 1 的领导者,节点 B 托管分片 2 的领导者,节点 C 托管分片 2 的 NRT 类型副本。假设将更新请求发送到节点 A,该节点将更新转发到节点 B(因为更新属于分片 2),然后将更新分发到其副本节点 C。让我们看看每个节点上发生的情况

  • 节点 A:通过 SignatureUpdateProcessor(计算签名并将其放入“id”字段)运行更新,然后通过 LogUpdateProcessor,再通过 DistributedUpdateProcessor。此处理器确定更新实际上属于节点 B,并将其转发到节点 B。更新未进一步处理。这是必需的,因为下一个处理器 RunUpdateProcessor 将针对本地分片 1 索引执行更新,这会导致分片 1 和分片 2 上的数据重复。

  • 节点 B:接收更新并看到它是由另一个节点转发。更新直接发送到 DistributedUpdateProcessor,因为它已经通过了节点 A 上的 SignatureUpdateProcessor,再次执行相同的签名计算是多余的。DistributedUpdateProcessor 确定更新确实属于此节点,将其分发到节点 C 上的副本,然后将更新进一步转发到链中的 RunUpdateProcessor

  • 节点 C:接收更新并看到它是由其领导者分发的。更新直接发送到 DistributedUpdateProcessor,它执行一些一致性检查,并将更新进一步转发到链中的 RunUpdateProcessor

总结

  1. DistributedUpdateProcessor 之前的处理器仅在接收更新请求的第一个节点上运行,无论它是转发节点(例如,上述示例中的节点 A)还是领导者(例如,节点 B)。我们称这些为“预处理器”或“处理器”。

  2. DistributedUpdateProcessor 之后的所有处理器仅在领导者和副本节点上运行。它们不会在转发节点上执行。此类处理器称为“后处理器”。

在上一节中,我们看到 updateRequestProcessorChain 配置为 processor="remove_blanks, signature"。这意味着此类处理器属于 #1 类型,并且仅在转发节点上运行。同样,我们可以通过指定属性“post-processor”将它们配置为 #2 类型,如下所示

后处理器配置
<updateProcessorChain name="custom" processor="signature" post-processor="remove_blanks">
  <processor class="solr.RunUpdateProcessorFactory" />
</updateProcessorChain>

但是,仅在转发节点上执行处理器是通过负载均衡器随机发送请求来跨 SolrCloud 集群分发昂贵计算(例如去重)的绝佳方式。否则,昂贵的计算将在领导者和副本节点上重复。

自定义更新链后处理器可能永远不会在恢复副本上调用

当副本处于 恢复 中时,入站更新请求会缓冲到事务日志。恢复成功完成后,将重放那些缓冲的更新请求。然而,在撰写本文时,自定义更新链后处理器永远不会针对缓冲的更新请求调用。请参见 SOLR-8030。要在 SOLR-8030 修复之前解决此问题,请避免在自定义更新链中指定后处理器

原子更新处理器工厂

如果 AtomicUpdateProcessorFactory 在更新链中位于 DistributedUpdateProcessor 之前,链中的传入文档将是部分文档。

由于 DistributedUpdateProcessor 负责将 原子更新处理为领导节点上的完整文档,这意味着仅在转发节点上执行的预处理器只能对部分文档进行操作。如果您有一个必须处理完整文档的处理器,那么唯一的选择就是将其指定为后处理器。

使用自定义链

update.chain 请求参数

update.chain 参数可用于任何更新请求中,以选择在 solrconfig.xml 中配置的自定义链。例如,为了选择前面部分中描述的“dedupe”链,可以发出以下请求

使用 update.chain
curl "http://localhost:8983/solr/gettingstarted/update/json?update.chain=dedupe&commit=true" -H 'Content-type: application/json' -d '
[
  {
    "name" : "The Lightning Thief",
    "features" : "This is just a test",
    "cat" : ["book","hardcover"]
  },
  {
    "name" : "The Lightning Thief",
    "features" : "This is just a test",
    "cat" : ["book","hardcover"]
  }
]'

上述内容应取消对两个相同文档的重复,并仅为其中一个文档建立索引。

处理器和后处理器请求参数

我们可以使用 processorpost-processor 请求参数动态构建自定义更新请求处理器链。多个处理器可以指定为这两个参数的逗号分隔值。例如

将 solrconfig.xml 中配置的处理器作为(预)处理器执行
curl "http://localhost:8983/solr/gettingstarted/update/json?processor=remove_blanks,signature&commit=true" -H 'Content-type: application/json' -d '
[
  {
    "name" : "The Lightning Thief",
    "features" : "This is just a test",
    "cat" : ["book","hardcover"]
  },
  {
    "name" : "The Lightning Thief",
    "features" : "This is just a test",
    "cat" : ["book","hardcover"]

  }
]'
将 solrconfig.xml 中配置的处理器作为预处理器和后处理器执行
curl "http://localhost:8983/solr/gettingstarted/update/json?processor=remove_blanks&post-processor=signature&commit=true" -H 'Content-type: application/json' -d '
[
  {
    "name" : "The Lightning Thief",
    "features" : "This is just a test",
    "cat" : ["book","hardcover"]
  },
  {
    "name" : "The Lightning Thief",
    "features" : "This is just a test",
    "cat" : ["book","hardcover"]
  }
]'

在第一个示例中,Solr 将动态创建一个链,其中“signature”和“remove_blanks”作为仅在转发节点上执行的预处理器,而在第二个示例中,“remove_blanks”将作为预处理器执行,而“signature”将作为后处理器在领导和副本上执行。

将自定义链配置为默认值

我们还可以指定一个自定义链,以便对发送到特定更新处理程序的所有请求默认使用,而不是为每个请求在请求参数中指定名称。

可以通过添加“update.chain”或“processor”和“post-processor”作为给定路径的默认参数来实现此目的,可以通过 InitParams 或在 “defaults”部分 中添加它们来实现此目的,所有请求处理程序都支持此部分。

以下是在 无模式模式 中定义的 initParam,它将自定义更新链应用于所有以“/update/”开头的请求处理程序。

示例 initParams
<initParams path="/update/**">
  <lst name="defaults">
    <str name="update.chain">add-unknown-fields-to-the-schema</str>
  </lst>
</initParams>

或者,可以使用“defaults”实现类似的效果,如下面的示例所示

示例 defaults
<requestHandler name="/update/extract" startup="lazy" class="solr.extraction.ExtractingRequestHandler" >
  <lst name="defaults">
    <str name="update.chain">add-unknown-fields-to-the-schema</str>
  </lst>
</requestHandler>

更新请求处理器工厂

以下是当前可用的更新请求处理器的简要说明。UpdateRequestProcessorFactory 可以根据需要集成到 solrconfig.xml 中的更新链中。强烈建议您查看这些类的 Javadoc;这些说明是摘自 Javadoc 的缩写片段。

通用更新处理器工厂

AddSchemaFieldsUpdateProcessorFactory

如果输入文档包含一个或多个与模式中任何字段或动态字段不匹配的字段,此处理器将动态地将字段添加到模式中。

AtomicUpdateProcessorFactory

此处理器将常规字段值文档转换为原子更新文档。此处理器可以在运行时使用(无需在 solrconfig.xml 中定义它),请参阅以下 AtomicUpdateProcessorFactory 部分。

ClassificationUpdateProcessorFactory

此处理器使用 Lucene 的分类模块来提供简单的文档分类。有关如何使用此处理器的更多详细信息,请参阅 https://cwiki.apache.org/confluence/display/solr/SolrClassification

CloneFieldUpdateProcessorFactory

将任何匹配的字段中的值克隆到配置的目标字段中。

DefaultValueUpdateProcessorFactory

一个简单的处理器,它向任何尚未在 fieldName 中具有值的文档添加一个默认值。

DocBasedVersionConstraintsProcessorFactory

此工厂生成一个 UpdateProcessor,它有助于使用 versionField 的配置名称基于每个文档的版本号来强制执行文档的版本约束。

DocExpirationUpdateProcessorFactory

用于管理文档自动“过期”的更新处理器工厂。

FieldNameMutatingUpdateProcessorFactory

通过使用配置的 replacement 替换与配置的 pattern 的所有匹配项来修改字段名称。

IgnoreCommitOptimizeUpdateProcessorFactory

允许您在 SolrCloud 模式下运行时忽略来自客户端应用程序的提交和/或优化请求,有关更多信息,请参阅:SolrCloud 中的分片和索引数据

IgnoreLargeDocumentProcessorFactory

允许您防止大小超过 limit(以 KB 为单位)的大型文档被索引。它有助于防止索引和恢复时因非常大的文档而导致的意外问题。

默认情况下,如果此处理器遇到超出其配置限制的文档,它将中止更新请求并将错误发送回用户。在违规者之前处理的文档由 Solr 索引;违规者之后的文档保持未处理状态。

或者,处理器提供“宽容”模式(permissiveMode=true),该模式跳过违规文档并记录警告,但不会中止批处理的其余部分或向用户返回错误。

RegexpBoostProcessorFactory

一个处理器,它将“inputField”的内容与“boostFilename”中找到的正则表达式进行匹配,如果匹配,它将从文件中返回相应的提升值,并以双精度值的形式将其输出到“boostField”。

SignatureUpdateProcessorFactory

使用一组已定义的字段为文档生成哈希“签名”。对于仅索引“相似”文档的一个副本非常有用。

ScriptUpdateProcessorFactory

一个处理器,它支持使用以脚本形式实现的更新处理器。在 脚本更新处理器 部分中了解更多信息。

TemplateUpdateProcessorFactory

允许基于模板模式向文档中添加新字段。此更新处理器还可以在运行时使用(无需在 solrconfig.xml 中定义它),请参阅下面的 TemplateUpdateProcessorFactory 部分。

TimestampUpdateProcessorFactory

一个更新处理器,它向任何正在添加的文档中添加一个新生成的“NOW”日期值,该文档在指定字段中还没有值。

URLClassifyProcessorFactory

更新处理器,它检查一个 URL 并输出到具有该 URL 特征的各种其他字段中,包括长度、路径级别的数量、它是否是顶级 URL(级别==0)、它是否看起来像登录/索引页面、URL 的规范表示(例如,删除 index.html)、URL 的域和路径部分等。

UUIDUpdateProcessorFactory

一个更新处理器,它向任何正在添加的文档中添加一个新生成的 UUID 值,该文档在指定字段中还没有值。此处理器还可以在运行时使用(无需在 solrconfig.xml 中定义它),请参阅下面的 UUIDUpdateProcessorFactory 部分。

FieldMutatingUpdateProcessorFactory 派生工厂

这些工厂都提供功能来修改文档中的字段,因为它们正在被索引。在使用任何这些工厂时,请查阅 FieldMutatingUpdateProcessorFactory javadoc,以了解它们在配置要修改的字段时支持的常见选项的详细信息。

ConcatFieldUpdateProcessorFactory

使用可配置的分隔符连接与指定条件匹配的字段的多个值。

CountFieldValuesUpdateProcessorFactory

使用该字段的值的数量替换与指定条件匹配的字段的任何值列表。

FieldLengthUpdateProcessorFactory

使用这些 CharSequences 的长度(作为整数)替换与指定条件匹配的字段中找到的任何 CharSequence 值。

FirstFieldValueUpdateProcessorFactory

仅保留与指定条件匹配的字段的第一个值。

HTMLStripFieldUpdateProcessorFactory

删除与指定条件匹配的字段中找到的任何 CharSequence 值中的所有 HTML 标记。

IgnoreFieldUpdateProcessorFactory

忽略并移除在添加到索引的任何文档中与指定条件匹配的字段。

LastFieldValueUpdateProcessorFactory

仅保留与指定条件匹配的字段的最后一个值。

MaxFieldValueUpdateProcessorFactory

一个更新处理器,它仅保留在找到多个值时从任何选定字段中找到的最大值。

MinFieldValueUpdateProcessorFactory

一个更新处理器,它仅保留在找到多个值时从任何选定字段中找到的最小值。

ParseBooleanFieldUpdateProcessorFactory

尝试将仅具有 CharSequence 类型值的选定字段变异为布尔值。

ParseDateFieldUpdateProcessorFactory

尝试将仅具有 CharSequence 类型值的选定字段变异为日期值。

ParseNumericFieldUpdateProcessorFactory 派生类
ParseDoubleFieldUpdateProcessorFactory

尝试将仅具有 CharSequence 类型值的选定字段变异为双精度值。

ParseFloatFieldUpdateProcessorFactory

尝试将仅具有 CharSequence 类型值的选定字段变异为浮点值。

ParseIntFieldUpdateProcessorFactory

尝试将仅具有 CharSequence 类型值的选定字段变异为整数值。

ParseLongFieldUpdateProcessorFactory

尝试将仅具有 CharSequence 类型值的选定字段变异为长整数值。

PreAnalyzedUpdateProcessorFactory

一个更新处理器,它使用配置的格式解析器解析使用 PreAnalyzedField 添加到任何文档的配置字段。

RegexReplaceProcessorFactory

一个更新处理器,它将配置的正则表达式应用于选定字段中找到的任何 CharSequence 值,并用配置的替换字符串替换任何匹配项。

RemoveBlankFieldUpdateProcessorFactory

移除找到的任何长度为 0(即空字符串)的 CharSequence 值。

TrimFieldUpdateProcessorFactory

修剪与指定条件匹配的字段中找到的任何 CharSequence 值的前导和尾随空格。

TruncateFieldUpdateProcessorFactory

将与指定条件匹配的字段中找到的任何 CharSequence 值截断到最大字符长度。

UniqFieldsUpdateProcessorFactory

移除在与指定条件匹配的字段中找到的重复值。

可作为插件加载的更新处理器工厂

这些处理器作为“模块”包含在 Solr 版本中,并且需要在运行时加载额外的 jar。有关详细信息,请参阅与每个模块关联的 README 文件

langid 模块提供
LangDetectLanguageIdentifierUpdateProcessorFactory

使用 http://code.google.com/p/language-detection 识别一组输入字段的语言。

TikaLanguageIdentifierUpdateProcessorFactory

使用 Tika 的 LanguageIdentifier 识别一组输入字段的语言。

analysis-extras 模块提供
OpenNLPExtractNamedEntitiesUpdateProcessorFactory

更新要编入索引的文档,其中包含使用 OpenNLP NER 模型提取的命名实体。请注意,要在 SolrCloud 上使用大于 1MB 的模型文件,您必须 配置 ZooKeeper 服务器和客户端将模型文件存储在文件系统中,该文件系统位于托管集合副本的每个节点上。

不应修改或删除的更新处理器工厂

这些处理器工厂列出是为了完整性,但它们是 Solr 基础设施的一部分,特别是 SolrCloud。除了确保在修改更新请求处理程序(或您制作的任何副本)时删除它们之外,您很少需要更改这些处理器工厂,甚至不需要更改。

DistributedUpdateProcessorFactory

用于将更新分发到所有必需的节点。

NoOpDistributingUpdateProcessorFactory

DistributingUpdateProcessorFactory 的替代 No-Op 实现,始终返回 null。专为希望绕过分布式更新并使用自己的自定义更新逻辑的专家设计。

LogUpdateProcessorFactory

日志记录处理器。它跟踪已通过链的所有命令,并在 finish() 中打印它们。

RunUpdateProcessorFactory

使用底层 UpdateHandler 执行更新命令。几乎所有处理器链都应以 RunUpdateProcessorFactory 的实例结束,除非用户明确地在备用自定义 UpdateRequestProcessorFactory 中执行更新命令。

可在运行时使用的更新处理器

这些更新处理器不需要在 solrconfig.xml 中进行任何配置。当将它们的名称添加到随更新请求一起发送的 processor 参数时,它们会自动初始化。可以通过附加多个以逗号分隔的处理器名称来使用多个处理器。

AtomicUpdateProcessorFactory

AtomicUpdateProcessorFactory 用于以原子方式更新文档。

使用参数 processor=atomic 来调用它。使用它将常规 update 操作转换为原子更新操作。当您使用诸如 /update/csv/update/json/docs 等端点时,这一点特别有用,否则这些端点没有原子操作的语法。

例如

processor=atomic&atomic.field1=add&atomic.field2=set&atomic.field3=inc&atomic.field4=remove&atomic.field4=remove

以上参数以以下方式转换常规 update 操作

  • field1 为原子 add 操作

  • field2 为原子 set 操作

  • field3 为原子 inc 操作

  • field4 为原子 remove 操作

TemplateUpdateProcessorFactory

TemplateUpdateProcessorFactory 可用于基于模板模式向文档添加新字段。

使用参数 processor=template 来使用它。模板参数 template.field(多值)定义要添加的字段和模式。模板可能包含占位符,这些占位符引用文档中的其他字段。您可以在单个请求中拥有多个 Template.field 参数。

例如

processor=template&template.field=fullName:Mr. {firstName} {lastName}

以上示例会向文档中添加一个名为 fullName 的新字段。字段 firstName and lastName 从文档字段中提供。如果其中任何一个字段缺失,则该部分将替换为空字符串。如果这些字段为多值,则仅使用第一个值。

UUIDUpdateProcessorFactory

UUIDUpdateProcessorFactory 用于向文档添加生成的 UUID。

使用参数 processor=uuid 来调用它。您还需要使用 uuid.fieldName 参数指定将添加 UUID 的字段。

例如

processor=uuid&uuid.fieldName=somefield_name