使用 Solr Cell 和 Apache Tika 进行索引
如果您需要索引的文档是二进制格式,例如 Word、Excel、PDF 等,Solr 包含一个请求处理程序,该处理程序使用 Apache Tika 提取文本以索引到 Solr。
Solr 使用 Tika 项目中的代码为将许多不同的文件格式解析器(例如 Apache PDFBox 和 Apache POI)整合到 Solr 本身提供了一个框架。
使用此框架,Solr 的 ExtractingRequestHandler
在内部使用 Tika 来支持上传二进制文件以进行数据提取和索引。使用 Solr Cell 不需要下载 Tika。
当此框架正在开发时,它被称为 Solr 内容提取库,或 CEL;从这个缩写中得出了这个框架的名称:Solr Cell。名称 Solr Cell 和 ExtractingRequestHandler
可互换使用来指代此功能。
Solr Cell 关键概念
使用 Solr Cell 框架时,请牢记以下几点
-
Tika 会自动尝试确定输入文档类型(例如,Word、PDF、HTML)并适当地提取内容。如果您愿意,可以使用
stream.type
参数显式指定 Tika 的 MIME 类型。有关支持的文件类型,请参阅 http://tika.apache.org/1.28.5/formats.html。 -
简而言之,Tika 在内部通过从解析文档的核心内容合成一个 XHTML 文档来工作,该文档传递给 Solr Cell 提供的已配置的 SAX 内容处理程序。Solr 响应 Tika 的 SAX 事件以从内容创建一个或多个文本字段。Tika 还公开文档元数据(除了 XHTML)。
-
Tika 根据 DublinCore 等规范生成元数据,例如标题、主题和作者。可用的元数据高度依赖于文件类型以及它们反过来包含的内容。下面 Tika 生成的元数据 部分描述了一些创建的通用元数据。Solr Cell 也提供了一些自己的元数据。
-
Solr Cell 将内部 XHTML 中的文本连接到
content
字段。您可以配置应包含/忽略哪些元素,以及哪些元素应映射到另一个字段。 -
Solr Cell 将每个元数据片段映射到一个字段。默认情况下,它映射到相同的名称,但几个参数控制如何执行此操作。
-
当 Solr Cell 完成创建内部
SolrInputDocument
后,索引堆栈的其余部分接管。任何更新处理程序之后的下一步是 更新请求处理器 链。
模块
这是通过 extraction
Solr 模块 提供的,该模块需要在使用前启用。
Solr 附带的“techproducts”示例预先配置为具有 Solr Cell 配置。如果您没有使用该示例,您将需要关注下面 solrconfig.xml 配置 部分。
Solr Cell 性能影响
富文档格式通常没有很好的文档记录,即使在有格式文档的情况下,并非所有创建文档的人都会忠实地遵循规范。
这会导致 Tika 遇到无法优雅处理的情况,尽管它已经尽力支持尽可能多的格式。PDF 文件尤其麻烦,主要是因为 PDF 格式本身。
如果在处理任何文件时出现错误,ExtractingRequestHandler
不会有辅助机制来尝试从文件中提取一些文本;它将抛出异常并失败。
如果任何异常导致 ExtractingRequestHandler
和/或 Tika 崩溃,Solr 将整体崩溃,因为请求处理程序在 Solr 用于其他操作的同一个 JVM 中运行。
索引也会消耗所有可用的 Solr 资源,特别是对于大型 PDF、演示文稿或其他包含大量富媒体的文件。
出于这些原因,不建议在生产系统中使用 Solr Cell。
最佳实践是在开发过程中使用 Solr Cell 作为概念验证工具,然后将 Tika 作为外部进程运行,将提取的文档发送到 Solr(通过 SolrJ)进行索引。这样,任何发生的提取失败都将与 Solr 本身隔离,并且可以优雅地处理。
有关如何执行此操作的一些示例,请参阅 Erick Erickson 的这篇博文,使用 SolrJ 进行索引。
尝试 Solr Cell
您可以使用 Solr 中包含的 schemaless
示例来尝试 Tika 框架。
此命令将启动 Solr,创建一个名为 gettingstarted
的核心/集合,使用 _default
配置集,并启用提取模块。然后将 /update/extract
处理程序添加到 gettingstarted
核心/集合中以启用 Solr Cell。
bin/solr start -e schemaless -Dsolr.modules=extraction
curl -X POST -H 'Content-type:application/json' -d '{
"add-requesthandler": {
"name": "/update/extract",
"class": "solr.extraction.ExtractingRequestHandler",
"defaults":{ "lowernames": "true", "captureAttr":"true"}
}
}' 'http://localhost:8983/solr/gettingstarted/config'
Solr 启动后,您可以使用 curl 通过 HTTP POST 发送 Solr 中包含的示例 PDF。
curl 'http://localhost:8983/solr/gettingstarted/update/extract?literal.id=doc1&commit=true' -F "myfile=@example/exampledocs/solr-word.pdf"
上面的 URL 调用 ExtractingRequestHandler
,上传文件 solr-word.pdf
,并为其分配唯一 ID doc1
。以下是对此命令组件的更详细说明
-
literal.id=doc1
参数为要索引的文档提供唯一 ID。如果没有它,ID 将设置为文件的绝对路径。还有其他方法,例如将元数据字段映射到 ID、生成新的 UUID 或从内容的签名(哈希)生成 ID。
-
commit=true 参数
导致 Solr 在索引文档后执行提交,使其立即可搜索。为了在加载大量文档时获得最佳性能,请在完成之前不要调用提交命令。 -
-F
标志指示 curl 使用 Content-Typemultipart/form-data
POST 数据,并支持上传二进制文件。@
符号指示 curl 上传附加的文件。 -
参数
myfile=@example/exampledocs/solr-word.pdf
上传示例文件。请注意,这包括路径,因此如果您上传不同的文件,请务必始终包含文件的相对路径或绝对路径。
您也可以使用 bin/solr post
来执行相同的操作
bin/solr post -c gettingstarted example/exampledocs/solr-word.pdf -params "literal.id=doc1"
现在您可以执行查询并使用类似 http://localhost:8983/solr/gettingstarted/select?q=pdf
的请求找到该文档。该文档将类似于以下内容
您可能会注意到与该文档关联的许多元数据字段。Solr 的配置默认情况下处于“无模式”(数据驱动)模式,因此所有提取的元数据字段都将获得自己的字段。
您可能希望忽略它们,除了您指定的几个。为此,请使用 uprefix
参数将未知(对模式而言)元数据字段名称映射到实际上被忽略的模式字段名称。动态字段 ignored_*
非常适合此目的。
对于您要映射的字段,请使用 fmap.IN=OUT
明确设置它们,或确保该字段在模式中定义。以下是一个示例
bin/solr post -c gettingstarted example/exampledocs/solr-word.pdf -params "literal.id=doc1&uprefix=ignored_&fmap.last_modified=last_modified_dt"
如果您在已经索引文档一次或多次后运行它,上面的示例将无法按预期工作。 之前我们添加了没有这些参数的文档,因此所有字段都在那时被添加到索引中。 尝试 |
ExtractingRequestHandler 参数和配置
Solr Cell 参数
ExtractingRequestHandler
接受以下参数。
这些参数可以为每个索引请求设置(作为请求参数),也可以通过在 solrconfig.xml
中定义它们来为对请求处理程序的所有请求设置。
capture
-
可选
默认值:无
捕获具有指定名称的 XHTML 元素,以作为 Solr 文档的补充添加。此参数对于将 XHTML 的块复制到单独的字段很有用。例如,它可以用来获取段落 (
<p>
) 并将它们索引到单独的字段中。请注意,内容仍然也会被捕获到content
字段中。示例:
capture=p
(在请求中)或<str name="capture">p</str>
(在solrconfig.xml
中)输出:
"p": {"这是我文档中的一个段落。"}
此参数也可以与
fmap.source_field
参数一起使用,将内容从属性映射到新字段。 captureAttr
-
可选
默认值:
false
将 Tika XHTML 元素的属性索引到单独的字段中,字段名称以元素命名。如果设置为
true
,从 HTML 中提取时,Tika 可以将<a>
标签中的 href 属性作为名为“a”的字段返回。示例:
captureAttr=true
输出:
"div": {"classname1", "classname2"}
commitWithin
-
可选
默认值:无
在指定毫秒数内对索引发出提交。
示例:
commitWithin=10000
(10 秒) defaultField
-
可选
默认值:无
如果未指定
uprefix
参数且无法确定字段,则使用的默认字段。示例:
defaultField=_text_
extractOnly
-
可选
默认值:
false
如果为
true
,则返回从 Tika 提取的内容,而不索引文档。这将提取的 XHTML 作为字符串返回到响应中。在屏幕上查看时,设置extractFormat
参数以获得除 XML 之外的响应格式可能有助于查看嵌入的 XHTML 标签。示例:
extractOnly=true
extractFormat
-
可选
默认值:
xml
控制提取内容的序列化格式。选项是
xml
或text
。xml
格式实际上是 XHTML,与将-x
命令传递给 Tika 命令行应用程序所产生的格式相同,而文本格式类似于 Tika 的-t
命令所产生的格式。此参数仅在
extractOnly
设置为 true 时有效。示例:
extractFormat=text
输出:有关示例输出(以 XML 格式),请参阅 https://cwiki.apache.org/confluence/display/solr/TikaExtractOnlyExampleOutput。
fmap.source_field
-
可选
默认值:无
将一个字段名称映射(移动)到另一个字段名称。
source_field
必须是传入文档中的字段,而值是要映射到的 Solr 字段。示例:
fmap.content=text
会导致 Tika 生成的content
字段中的数据被移动到 Solr 的text
字段。 ignoreTikaException
-
可选
默认值:无
如果为
true
,则会跳过处理过程中发现的异常。但是,任何可用的元数据都将被索引。示例:
ignoreTikaException=true
literal.fieldname
-
可选
默认值:无
使用提供的名称填充字段,并为每个文档提供指定的值。如果字段是多值字段,则数据可以是多值的。
示例:
literal.doc_status=published
输出:
"doc_status": "published"
literalsOverride
-
可选
默认值:
true
如果为
true
,则文字字段值将覆盖具有相同字段名称的其他值。如果为
false
,则使用literal.fieldname
定义的文字值将附加到已从 Tika 提取的字段中的数据。将literalsOverride
设置为false
时,该字段必须是多值字段。示例:
literalsOverride=false
lowernames
-
可选
默认值:
false
如果为
true
,则所有字段名称将被映射为小写,并使用下划线(如果需要)。示例:
lowernames=true
输出:假设输入为“Content-Type”,则文档中的结果将是字段
content_type
multipartUploadLimitInKB
-
可选
默认值:
2048
千字节定义允许的文档大小(以千字节为单位)。如果您有非常大的文档,则应增加此值,否则它们将被拒绝。
示例:
multipartUploadLimitInKB=2048000
parseContext.config
-
可选
默认值:无
如果正在使用的 Tika 解析器允许参数,则可以通过创建解析器配置文件并将其指向 Solr 来将参数传递给 Tika。有关如何使用此参数的更多信息,请参阅 解析器特定属性 部分。
示例:
parseContext.config=pdf-config.xml
passwordsFile
-
可选
默认值:无
定义文件名到密码映射文件的路径和名称。有关使用密码文件的更多信息,请参阅 索引加密文档 部分。
示例:
passwordsFile=/path/to/passwords.txt
resource.name
-
可选
默认值:无
指定要索引的文件的名称。这是可选的,但 Tika 可以将其用作检测文件 MIME 类型的提示。
示例:
resource.name=mydoc.doc
resource.password
-
可选
默认值:无
定义用于受密码保护的 PDF 或 OOXML 文件的密码。有关使用此参数的更多信息,请参阅 索引加密文档 部分。
示例:
resource.password=secret
tika.config
-
可选
默认值:无
定义自定义 Tika 配置文件的路径和名称。只有在您自定义了 Tika 实现时才需要此文件。
示例:
tika.config=/path/to/tika.config
uprefix
-
可选
默认值:无
为模式中未定义的所有字段添加给定前缀。这与动态字段定义结合使用时非常有用。
示例:
uprefix=ignored_
将在所有未知字段之前添加ignored_
作为前缀。在这种情况下,您还可以额外在模式中定义一个规则,以不索引这些字段<dynamicField name="ignored_*" type="ignored" />
xpath
-
可选
默认值:无
提取时,仅返回满足给定 XPath 表达式的 Tika XHTML 内容。有关 Tika XHTML 格式的详细信息,请参阅 http://tika.apache.org/1.28.5/,它随解析的格式而异。另请参阅 定义 XPath 表达式 部分以获取示例。
solrconfig.xml 配置
如果您使用提供的 示例配置集 之一启动了 Solr,则默认情况下可能已配置了 ExtractingRequestHandler
。
首先,您必须启用模块。如果solrconfig.xml
尚未配置,您需要修改它以找到ExtractingRequestHandler
及其依赖项。
<lib dir="${solr.install.dir:../../..}/modules/extraction/lib" regex=".*\.jar" />
然后,您可以在solrconfig.xml
中配置ExtractingRequestHandler
。以下是 Solr 的sample_techproducts_configs
配置集中找到的默认配置,您可以根据需要进行修改。
<requestHandler name="/update/extract"
startup="lazy"
class="solr.extraction.ExtractingRequestHandler" >
<lst name="defaults">
<str name="lowernames">true</str>
<str name="fmap.content">_text_</str>
</lst>
</requestHandler>
在此设置中,所有字段名称都转换为小写(使用lowernames
参数),Tika 的content
字段映射到 Solr 的text
字段。
您可能需要配置更新请求处理器 (URP),这些处理器解析数字和日期,并对 Solr Cell 生成的元数据字段进行其他操作。 在 Solr 的 如果您改为显式定义架构的字段,则可以选择性地指定所需的 URP。一个简单的方法是将参数
以上建议的列表取自作为无模式模式一部分运行并提供其大部分功能的 URP 列表。但是,无模式功能的一个主要部分在建议的列表中缺失,即 |
特定于解析器的属性
Tika 使用的解析器可能具有特定的属性来控制数据的提取方式。这些属性可以通过 Solr 传递,用于特殊解析情况。
例如,当从 Java 程序中使用 Tika 库时,PDFParserConfig
类有一个方法setSortByPosition(boolean)
,它可以提取垂直方向的文本。要通过使用ExtractingRequestHandler
的配置访问该方法,可以将parseContext.config
属性添加到solrconfig.xml
,然后在 Tika 的PDFParserConfig
中设置属性,如下面的示例所示。
<entries>
<entry class="org.apache.tika.parser.pdf.PDFParserConfig" impl="org.apache.tika.parser.pdf.PDFParserConfig">
<property name="extractInlineImages" value="true"/>
<property name="sortByPosition" value="true"/>
</entry>
<entry>...</entry>
</entries>
请参阅 Tika Java API 文档,了解可以为任何需要这种控制级别的特定解析器设置的配置参数。
Solr Cell 内部
Tika 创建的元数据
如前所述,Tika 会生成有关文档的元数据。元数据描述了文档的不同方面,例如作者姓名、页数、文件大小等。生成的元数据取决于提交的文档类型。例如,PDF 的元数据与 Word 文档不同。
Solr Cell 示例
使用捕获和映射字段
以下命令分别捕获<div>
标签(capture=div
),然后将该字段的所有实例映射到名为foo_t
的动态字段(fmap.div=foo_t
)。
bin/solr post -c gettingstarted example/exampledocs/sample.html -params "literal.id=doc2&captureAttr=true&defaultField=_text_&fmap.div=foo_t&capture=div"
使用文字定义自定义元数据
要添加您自己的元数据,请将文字参数与文件一起传递
bin/solr post -c gettingstarted -params "literal.id=doc4&captureAttr=true&defaultField=text&capture=div&fmap.div=foo_t&literal.blah_s=Bah" example/exampledocs/sample.html
参数literal.blah_s=Bah
将在每个文档中插入一个字段blah_s
。文本的每个实例都将是“Bah”。
定义 XPath 表达式
以下示例传递一个 XPath 表达式,以限制 Tika 返回的 XHTML
bin/solr post -c gettingstarted -params "literal.id=doc5&captureAttr=true&defaultField=text&capture=div&fmap.div=foo_t&xpath=/xhtml:html/xhtml:body/xhtml:div//node()" example/exampledocs/sample.html
在不索引的情况下提取数据
Solr 允许您在不索引的情况下提取数据。如果您仅将 Solr 用作提取服务器,或者您有兴趣测试 Solr 提取,则可能需要这样做。
以下示例设置extractOnly=true
参数,以在不索引的情况下提取数据。
curl "http://localhost:8983/solr/gettingstarted/update/extract?&extractOnly=true" --data-binary @example/exampledocs/sample.html -H 'Content-type:text/html'
输出包括 Tika 生成的 XML(并由 Solr 的 XML 进一步转义),使用不同的输出格式使其更易读(-out yes
指示工具将 Solr 的输出回显到控制台)
bin/solr post -c gettingstarted -params "extractOnly=true&wt=ruby&indent=true" -out yes example/exampledocs/sample.html
使用 Solr Cell 与 SolrJ
SolrJ 是一个 Java 客户端,您可以使用它将文档添加到索引中、更新索引或查询索引。您可以在SolrJ中找到有关 SolrJ 的更多信息。
以下是如何使用 Solr Cell 和 SolrJ 将文档添加到 Solr 索引中的示例。
首先,让我们使用 SolrJ 创建一个新的 SolrClient,然后我们将构建一个包含 ContentStream(本质上是文件的包装器)的请求,并将其发送到 Solr
public class SolrCellRequestDemo {
public static void main (String[] args) throws IOException, SolrServerException {
SolrClient client = new HttpSolrClient.Builder("http://localhost:8983/solr/my_collection").build();
ContentStreamUpdateRequest req = new ContentStreamUpdateRequest("/update/extract");
req.addFile(new File("my-file.pdf"));
req.setParam(ExtractingParams.EXTRACT_ONLY, "true");
NamedList<Object> result = client.request(req);
System.out.println("Result: " + result);
}
此操作将文件my-file.pdf
流式传输到my_collection
的 Solr 索引中。
上面的示例代码调用了 extract 命令,但您可以轻松地替换 Solr Cell 支持的其他命令。要使用的关键类是ContentStreamUpdateRequest
,它确保 ContentStream 设置正确。SolrJ 会处理其余工作。
请注意,ContentStreamUpdateRequest
不仅仅特定于 Solr Cell。您可以将 CSV 发送到 CSV 更新处理程序,以及发送到任何其他使用 ContentStream 进行更新的请求处理程序。