企业级搜索引擎Solr 第三章 索引数据(Indexing Data)[2]--DIH

时间:2021-06-08 15:58:17

转载:http://quweiprotoss.wap.blog.163.com/w2/

DIH需要在solrconfig.xml中注册,如下:

<requestHandler name="/dih_artists_jdbc"

class="org.apache.solr.handler.dataimport.DataImportHandler">

<lst name="defaults">

<str name="config">mb-dih-artists-jdbc.xml</str>

</lst>

</requestHandler>

被引用的md-dih-artist-jdbc.xml位于<solr-home>/conf,它指定了数据导入过程的细节。我们会稍涉及一下这个文件。

The development console

在描述DIH配置文件之前,我们将看一下DIH开发控制台。访问下面的URL:

http://localhost:8983/solr/mbartists/admin/dataimport.jsp

如果注册了多个请求handler,你会看到页面上列出了它们的链接,链接指向这个Handler的开发的控制台。开发控制台如下图所示:

页面分为两部分:在左边的DIH控制界面,右边是命令以XML输出的结果。

控制界面还分为了开发区和调试区,包含了一个对配置文件的临时编辑,页面底部是重要的DIH命令列表。

本章后面会介绍提交一个命令到DIH的细节。

Writing a DIH configuration file

DIH配置文件的关键部分包括一个数据源(data source),一个实体(entity),一些转换器(transformers)和一些域列表。配置中有很多这些的东西,但有时它们可以被忽略。首先我会列表一些DIH组件,并写一些对组件简单的描述。然后我会给你一个示例配置,来展示这些组件是如何配置起来工作的。

Data Sources

如你所想,<dataSource/>是由一个实体引用的数据源。这是配置中最简单的一段。Type属性指定了类型,默认为JdbcDataSource。根据类型的不同,可以指定其它的属性(这里没有列出)。也可以有多个数据源,但这种做法不常见。并且除了JdbcDataSource之外,每种类型都会处理二进制或是文本数据,下面是可用的数据源类型。它们的名字都以DataSource结尾。

l  JdbcDataSource:通过JDBC访问数据库,通常是关系数据库。

l  FieldStreamDataSource,FieldReaderDataSource:从JdbcDataSource的列中抽取二进制或是字符数据。

l  BinFileDataSource,FileDataSource:指定一个二进制或是文本文件的路径。

l  URLDataSource:指定一个文本数据源的URL。

l  BinContentStreamDataStream,ContentStreamDataSource:接收Post到DIH的二进制或是文本数据,而不是由DIH从其它地方拉数据。

如果你在找MailDataSource,其实是没有的。MailEntityProcessor里包含了从数据源抓取邮件的功能。

Entity processors

在<document/>元素之后,有一个或多个<entity/>元素,这些元素会被实体处理器(Entity Processor)中的processor属性引用。默认的是SqlEntityProcessor。一个实体处理器执行时会产生文档集合。产生文档的数据通常是从所指定的数据源中取得。在<document>元素的第一个儿子默认是根实体(root entity),它表示它的文档会被Solr索引。如果rootEntity属性被显式地设置为false,那么DIH会反向查找,直到找到一个实体配置没有设置为false的。可以有子实体(sub-entities),它通常为每个父文档执行一次,并通常由父文档引用来缩小查找范围。子实体的文档会合并到根实体的文档中,如果多个子实体对相同的域产生了多个文档,那么根实体会产生一个多值域。

实体处理器(s)有一些属性配置是通用的,有一些是特有的。

实体处理器名字都以EntityProcessor结尾。下面是处理器列表:

l  SqlEntityProcessor:引用一个JdbcDataSource并执行一个特定的SQL查询。结果集中的列将映射到相同域名的域中。这个处理器是唯一支持增量导入的。

l  CachedSqlEntityProcessor:与SqlEntityProcessor相似,但它不是每次都执行Sql查询,而是将每条记录Cache到内存以便以后查找。它是根实体的子实体唯一的选择。

l  XPathEntityProcessor:从文本数据源中处理XML。它根据XPath表达式将XML分成文档。通过XPath表达式来引用XML中的一部分内容作为域。

l  PlainTextEntityProcessor:从文本数据源中读取数据,然后将数据作为一个域值。

l  LineEntityProcessor:将文档数据源的每一行产生一个文件。

l  FileListEntityProcessor:查找所有满足条件的文件,为找到每个文件产生一个文档,文档中包含文件的路径。一个如Tika子实体就可以从文件中抽取文本。

l  TikaEntityProcessor:用Tika从二进制数据源中抽取文本。Tika支持多种文件类型,如HTML,PDF和Microsoft Office文档。

l  MailEntityProcessor:从IMAP邮件服务器取得e-mail,包括用Tika处理附件。它不用数据源。你可以指定一个起始时间,但不幸的是,它不支持DIH增量更新。

Fields and transformers

在一个<entity>中有一些<field/>元素,它们定义了Sql查询中的列如果映射到Solr。Field元素必须有一个column属性,它对应于SQL查询中的字段名。Name属性是Solr Schema中的域名,即字段名要映射的域名。如果它没有指定,那么它就默认为字段名。当结果集中的列不需要再做进一步处理,其实没有必要去写域的定义,因为定义已经被隐式定义了。

实体定义中我们还没有提及的是transformer,它定义了一个创建,修改,删除域的transformer列表,列表以逗号分隔。

很关键的一点是,Transformer是按顺序进行的。通常Transformer作用于指定使用的域上,它会对域值进行一些处理,可能是将域值切分成多个值,或是对域值进行格式化,或是其它。下面是Transformer的一个列表:

?    TemplateTransformer:基于一个字符串模板的对域值进行重写或是修改。模板中可以包含对其它域的引用并能使用DIH变量。

?    RegexTransformer:可以进行字符串替换,将域值切分成多值,或是将域值映射到不同的域中。这个Transformer非常有用!

?    DateFormatTransformer:根据指定的模式解析日期-时间格式。输出格式是Solr的日期格式。

?    NumberFormatTransformer:根据本地设置和风格(即数字,百分比,整形,货币格式)解析一个数字。输出是一个适用于Solr的某一数值域类型的普通数值。

?    HTMLStripTransformer:将HTML标签根据HTMLStripCharFilter(在前面章节有描述)移除。在这一步进行移除,而不是在文本分析组件中移除,可以让Stored的内容是清洁的,而不仅仅是索引的数据。

?    ClobTransformer:将数据库中的CLOB中的值转换成一个普通字符串。

?    LogTransformer:使用字符串模板如TemplateTransformer,可以为分析Transformer处理过程记录日志。不同于多数Transformer,它在实体中配置,因为它是进行分析每个实体输出的结果,而不是每个域。

?    ScriptTransformer:调用用户定义在<script/>元素中的代码。这个Transformer很特殊地在transformer属性中配置,用…,script:myFunctionName, …配置,其中myFunctionName是所提供代码中的函数名。代码默认是JavaScript,但是其它可以在JVM上运行的语言大多也都是支持的。

Example DIH configurations

DIH会因为数据是否是数据库,内容是否是XML,或是文本是否要从文档中抽取出来,而使用配置看起来非常不同。

Importing from databases

下面是mb-dih-aritsts-jdbc.xml配置文件,其中有一个比较长的SQL查询:

<dataConfig>

<dataSource name="jdbc" driver="org.postgresql.Driver"

url="jdbc:postgresql://localhost/musicbrainz_db"

user="musicbrainz" readOnly="true" autoCommit="false" />

<document>

<entity name="artist" dataSource="jdbc" pk="id" query="

select

a.id as id,

a.name as a_name, a.sortname as a_name_sort,

a.begindate as a_begin_date, a.enddate as a_end_date,

a.type as a_type,

array_to_string(

array(select aa.name from artistalias aa

where aa.ref = a.id ),

'|') as a_alias,

array_to_string(

array(select am.name from v_artist_members am

where am.band = a.id order by am.id),

'|') as a_member_name,

array_to_string(

array(select am.id from v_artist_members am

where am.band = a.id order by am.id),

'|') as a_member_id,

(select re.releasedate from release re inner join

album r on re.album = r.id where r.artist = a.id

order by releasedate desc limit 1)

as a_release_date_latest

from artist a

"

transformer="RegexTransformer,DateFormatTransformer,

TemplateTransformer">

<field column = "id" template="Artist:${artist.id}" />

<field column = "type" template="Artist" />

<field column = "a_begin_date"

dateTimeFormat="yyyy-MM-dd" />

<field column = "a_end_date"

dateTimeFormat="yyyy-MM-dd" />

<field column = "a_alias" splitBy="\|" />

<field column = "a_member_name" splitBy="\|"/>

<field column = "a_member_id" splitBy="\|" />

</entity>

</document>

</dataConfig>

如果dataSource中的type属性没有指定,它默认为JdbcDataSource。熟悉JDBC的读者应该发现这个例子中的属性很熟悉,另外还有其它的属性可以设置。你可以在wiki中找到全部的属性。

<entity/>中最重要的部分是在query属性中配置的,它是一个SQL查询。这个查询里有一些子查询。子查询的结果为数组,数组用空格连接转换为字符串。不同的数据库会用不同的语法来实现这些功能。

Importing XML from a file with XSLT

在本例中,我们将从磁盘中导入一个XML文件,并用XSLT完成大部分工作,而不使用DIH Transformer。

<dataConfig>

<dataSource name="artists" type="FileDataSource" encoding="UTF-8"/>

<document name="artists">

<entity name="artist" dataSource="artists"

url="downloads/artists_veryshort.xml"

processor="XPathEntityProcessor"

xsl="cores/mbtype/conf/xslt/artists.xsl"

useSolrAddSchema="true">

</entity>

</document>

</dataConfig>

FileDataSource类型的dataSource是对文本文件配置。实体的URL是相对数据源中baseUrl的路径。在配置中它没有设置,所以它默认是服务器的当前工作目录。XSLT文件是相对当前工作目录,而不是conf目录——它是一个已知的bug:SOLR-1226。要查看被引用的XSLT文件,请下载本书的补充资料。

这个例子有意思的地方是它不仅使用XSLT并且还使用了useSolrAddSchema属性,它会使结果XML结构为<add><doc><field name=…结构。我们的输入是HTML,XSLT文件转换了它。这两个选项最好结合使用。

Importing multiple rich document files (crawling)

在这个例子中,我们的配置会爬取所有一个目录中的PDF文件,然后抽取其中的文本和元数据。

<dataConfig>

<dataSource type="BinFileDataSource" />

<document>

<entity name="f" dataSource="null" rootEntity="false"

processor="FileListEntityProcessor"

baseDir="/my/file/path" fileName=".*pdf"

recursive="true">

<entity name="tika-test" processor="TikaEntityProcessor"

url="${f.fileAbsolutePath}" format="text">

<field column="Author" name="author" meta="true"/>

<field column="title" name="title" meta="true"/>

<field column="text" name="text"/>

</entity>

</entity>

</document>

</dataConfig>

FileListEntityProcessor是进行文件爬取的部分,它并不需要一个数据源,但配置中必须指定。因为指定了rootEntity=”false”,所以它不是一个根实体,在里面的子实体才是一个根,实体,它对应Solr文档。实体名为f,子实体名为tika-test,子实体在它的url中用变量f.fileAbsolutePath引用了f中指定的路径。这个例子使用了变量替换语法${…}。

提到这,其实DIH中有大量的变量用于替换,包括在solr.xml和solrconfig.xml中的定义的变量。同样,你可以看DIH wiki查看更多细节。

TikaEntityProcessor部分很直观。Tika产生大量的元数据,本例中只使用两个。

Importing commands

导入全部数据称为全量导入(full import),相对的是增量导入(delta import)。命令是在DIH请求处理器的commmand属性中指定。我们可以用下面的URL告诉DIH进行一次全量的导入:

http://localhost:8983/solr/mbartists/dataimport?command=full-import。

在命令行中我们可以用:

curl http://localhost:8983/mbartists/solr/dataimport -F command=full-import

它使用HTTP POST,它比前面介绍的GET更合理一些。

不同于其它的导入机制,DIH立即返回HTTP响应,并继续异步处理。要得到DIH当前的状态。访问URL:http://localhost:8983/solr/mbartists/dataimport,你会得到如下的输出:

<response>

<lst name="responseHeader">

<int name="status">0</int>

<int name="QTime">15</int>

</lst>

<lst name="initArgs">

<lst name="defaults">

<str name="config">mb-dih-artists-jdbc.xml</str>

</lst>

</lst>

<str name="status">idle</str>

<str name="importResponse"/>

<lst name="statusMessages"/>

<str name="WARNING">This response format is experimental. It islikely to change in the future.</str>

</response>

命令的属性默认设置为status,它的返回就是上面的输出。当导入正在进行时,它会显示一些进度的统计信息,和状态为busy。

其它布尔参数clean,commit和optimize可能也会带在命令中,并且其它默认设置为true。Clean是DIH中的参数,它是指在导入文档之前,先将所有文档删除。要自定义文档如何被删除,你可以在根实体的preImportDeleteQuery属性指定。你甚至可以在postImportDeleteQuery属性中指定在一次导入后删除文档。查询的语法在第四章介绍。

另两个有用的命令是reload-config和abort。第一个是导入DIH配置文件,它可以在不重启Solr的情况下改变配置。第二个命令是取消任何正在进行的导入。

Delta imports

DIH支持它称为增量导入(delta import)的功能,它只获取自上次获取后改变的数据。只有SqlEntityProcessor支持增量导入,并且它假设你的数据是打时间戳的。 官方wiki中有详细的介绍。它在实体中使用deltaImportQuery和deltaQuery属性对,和一个delta-import命令。这个方法很麻烦,很难维护,并且它比起新的方法速度慢,文档在:http://wiki.apache.org/solr/DataImportHandlerDeltaQueryViaFullImport。

你要在SQL的WHERE子句中引入时间戳以进行增量更新。并要检查clean参数,clean是指示一个增量更新或是全量更新是否应该进行。下面是一个简洁的<entity/>定义:

<entity name="item" pk="ID"

query="SELECT * FROM item

WHERE '${dataimporter.request.clean}' != 'false'

OR last_modified > '${dataimporter.last_index_time}'">

注意${…}参数替换语法,要进行全量导入,要使用clean=true的全量导入命令:/dataimport?command=full-fullimport&clean=true。对于一个增量导入,我们仍用full-import命令,但我们设置clean为false:/dataimport?command=full-import&clean=false&optimize=false。我同样禁止索引优化因为它在增量导入中不适合。