脚本更新处理器

ScriptUpdateProcessorFactory 允许在 Solr 文档更新处理期间使用 Java 脚本引擎,从而在索引之前为表达自定义文档处理逻辑提供极大的灵活性。

它具有提交、删除、回滚等索引操作的钩子,但添加是最常见的用法。它被实现为一个 UpdateProcessor,放置在 UpdateChain 中。

这以前被称为 StatelessScriptingUpdateProcessor,现在已重命名以明确此更新处理器的关键方面是它启用了脚本功能。

脚本可以使用 JVM 支持的任何脚本语言(如 JavaScript)编写,并动态执行,因此无需预编译。

能够在索引管道中运行您选择的脚本是一个非常强大的工具,我有时称之为 免出狱 卡,因为您可以通过这种方式解决一些其他任何方法都无法解决的问题。但是,您正在引入一些潜在的安全漏洞。

模块

这通过 scripting Solr 模块提供,该模块需要在使用前启用。

启用 ScriptingUpdateProcessor 和脚本引擎

Java 11 及更早版本带有名为 Nashorn 的 JavaScript 引擎,但 Java 12 将要求您添加自己的 JavaScript 引擎。其他支持的脚本引擎(如 JRuby、Jython、Groovy)都要求您向 Solr 添加 JAR 文件。

了解有关将任何其他需要的 JAR 文件(取决于您的脚本引擎)添加到 Solr 的 Lib 目录的更多信息。

配置

<updateRequestProcessorChain name="script">
   <processor class="org.apache.solr.scripting.update.ScriptUpdateProcessorFactory">
     <str name="script">update-script.js</str>
   </processor>
   <!--  optional parameters passed to script
     <lst name="params">
       <str name="config_param">example config parameter</str>
     </lst>
   -->
   <processor class="solr.LogUpdateProcessorFactory" />
   <processor class="solr.RunUpdateProcessorFactory" />
 </updateRequestProcessorChain>
该处理器支持其配置的 defaults/appends/invariants 概念。但是,也可以跳过此级别,直接在 <processor> 标记下配置参数。

下面列出每个配置参数及其含义

script

必需

默认值:无

脚本文件名。脚本文件必须放置在 conf/ 目录中。可以指定一个或多个“script”参数;多个脚本按指定的顺序执行。

engine

可选

默认值:无

可选地指定要使用的脚本引擎。只有当脚本文件的扩展名不是标准映射到脚本引擎时才需要这样做。例如,如果您的脚本文件是用 JavaScript 编写的,但文件名是 update-script.foo,则使用 javascript 作为引擎名称。

params

可选

默认值:无

传递到脚本执行上下文中的可选参数。这被指定为一个具名列表 (<lst>) 结构,其中包含嵌套的类型化参数。如果指定了此参数,则脚本上下文将获得一个 "params" 对象,否则将没有 "params" 对象可用。

脚本执行上下文

每个脚本都会获得一些预先提供的变量。

logger

Logger (org.slf4j.Logger) 实例。这对于从脚本记录信息很有用。

req

SolrQueryRequest 实例。

rsp

SolrQueryResponse 实例。

params

“params” 对象,如果有的话,来自配置。

尝试一下

在 “techproducts” 配置集中,有一个 JavaScript 示例 update-script.js

要尝试脚本功能,请在文件 ./server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml 中启用 <updateRequestProcessorChain name="script"> 配置。然后通过 bin/solr start -e techproducts -Dsolr.modules=scripting 启动 Solr。

INFO: update-script#processAdd: id=1

您可以在 Solr 日志 UI 中看到记录的消息。

示例

processAdd() 和其他脚本方法可以返回 false 以跳过对文档的进一步处理。所有方法都必须定义,尽管通常 processAdd() 方法是操作发生的地方。

Javascript

注意:检查 solrconfig.xml 并取消注释更新请求处理器定义以启用此功能。

function processAdd(cmd) {

  doc = cmd.solrDoc;  // org.apache.solr.common.SolrInputDocument
  id = doc.getFieldValue("id");
  logger.info("update-script#processAdd: id=" + id);

// Set a field value:
//  doc.setField("foo_s", "whatever");

// Get a configuration parameter:
//  config_param = params.get('config_param');  // "params" only exists if processor configured with <lst name="params">

// Get a request parameter:
// some_param = req.getParams().get("some_param")

// Add a field of field names that match a pattern:
//   - Potentially useful to determine the fields/attributes represented in a result set, via faceting on field_name_ss
//  field_names = doc.getFieldNames().toArray();
//  for(i=0; i < field_names.length; i++) {
//    field_name = field_names[i];
//    if (/attr_.*/.test(field_name)) { doc.addField("attribute_ss", field_names[i]); }
//  }

}

function processDelete(cmd) {
  // no-op
}

function processMergeIndexes(cmd) {
  // no-op
}

function processCommit(cmd) {
  // no-op
}

function processRollback(cmd) {
  // no-op
}

function finish() {
  // no-op
}

Ruby

Ruby 支持通过 JRuby 项目实现。要使用 JRuby 作为脚本引擎,请将 jruby.jar 添加到 Solr。

这是一个 JRuby 更新处理脚本的示例(请注意,所有传入的变量都需要以 $ 为前缀,例如 $logger

def processAdd(cmd)
  doc = cmd.solrDoc  # org.apache.solr.common.SolrInputDocument
  id = doc.getFieldValue('id')

  $logger.info "update-script#processAdd: id=#{id}"

  doc.setField('source_s', 'ruby')

  $logger.info "update-script#processAdd: config_param=#{$params.get('config_param')}"
end

def processDelete(cmd)
  # no-op
end

def processMergeIndexes(cmd)
  # no-op
end

def processCommit(cmd)
  # no-op
end

def processRollback(cmd)
  # no-op
end

def finish()
  # no-op
end

已知问题

以下在 JRuby 中无法按预期工作,但在 JavaScript 中可以正常工作

#  $logger.info "update-script#processAdd: request_param=#{$req.params.get('request_param')}"
#  $rsp.add('script_processed',id)

Groovy

将 Groovy 发行版的 lib/ 目录中的 JAR 添加到 Solr。可能不需要 Groovy 发行版中的所有 JAR,但需要的不仅仅是主 groovy.jar 文件(至少在测试使用 Groovy 2.0.6 时是这样)。

def processAdd(cmd) {
  doc = cmd.solrDoc  // org.apache.solr.common.SolrInputDocument
  id = doc.getFieldValue('id')

  logger.info "update-script#processAdd: id=" + id

  doc.setField('source_s', 'groovy')

  logger.info "update-script#processAdd: config_param=" + params.get('config_param')

  logger.info "update-script#processAdd: request_param=" + req.params.get('request_param')
  rsp.add('script_processed',id)
}

def processDelete(cmd) {
 //  no-op
}

def processMergeIndexes(cmd) {
 // no-op
}

def processCommit(cmd) {
 //  no-op
}

def processRollback(cmd) {
 // no-op
}

def finish() {
 // no-op
}

Python

Python 支持通过 Jython 项目实现。将 独立 jython.jar(包含所有依赖项的 JAR)添加到 Solr 中。

def processAdd(cmd):
  doc = cmd.solrDoc
  id = doc.getFieldValue("id")
  logger.info("update-script#processAdd: id=" + id)

def processDelete(cmd):
    logger.info("update-script#processDelete")

def processMergeIndexes(cmd):
    logger.info("update-script#processMergeIndexes")

def processCommit(cmd):
    logger.info("update-script#processCommit")

def processRollback(cmd):
    logger.info("update-script#processRollback")

def finish():
    logger.info("update-script#finish")