转载:http://quweiprotoss.wap.blog.163.com/
Solr Cell是一个针对Tika的简单适配器,它由一个SAX ContentHandler组成,ContentHandler处理SAX事件,并通过指定要抽取的域产生文档。
在索引二制进文件的时候,有些事要注意:
l 你可以提供任何Tika支持的文档类型给Tika,Tika会尝试确定文档正确的MIME类型,然后再调用相应的解析器。如果你已经知道了正确的MIME,你可以在stream.type参数中指定。
l Solr Cell中默认的SolrContentHandler用起来很简单。你可能发现你不止需要Solr Cell提供的功能,你还需要对数据进行一些处理来减少垃圾数据被索引。其中一种实现的方法是自定义的Solr UpdateRequestProcessor,在本章后面会讲。另一种方式是实现子类ExtractingRequestHandler的createFactory()来提供自定义的SolrContentHandler。
l 请记住如果你可能会发送很大的二进制文件,然后由Solr解析,这样速度可能会很慢。如果你只想索引元数据,那你应该用Tika来编写自己的解析器来抽取元数据,再Post给服务器,见第九章。
你可以从Tika官网上学习到更多内容:http://tika.apache.org/。
Configuring Solr
在/example/cores/karaoke/conf/solrconfig.xml文件中,有对二进制文档解析的请求处理器:
<requestHandler name="/update/extract"
class="org.apache.solr.handler.extraction.ExtractingRequestHandler">
<lst name="defaults">
<str name="map.Last-Modified">last_modified</str>
<str name="uprefix">metadata_</str>
</lst>
</requestHandler>
这里我们可以看到Tika的元数据属性Last-Modified被映射到了Solr的域last_modified,参数uprefix是指定没有匹配对应Solr域名的域的名字的前缀。
Solr Cell作为contrib模块发布,它包含大约25以上的JAR文件,它们每种能解析不同的文件格式。要使用Solr Cell,我们将Solr Cell Jar和其它的JAR放到karaoke核心中,/examples/cores/karaoke/lib,它默认不被引入solr.war,也不需要其它的核心的JAR。这些JAR文件放到lib,这时只有karaoke核心可以使用它。要将这些核心在多个核心中共享,你可以将它们加入到examples/cores/lib中,并在solr.xml中指定目录:
<solr persistent="false" sharedLib="lib">
在这个例子中,我们用标准Java包javax.audio.midi将MIDI格式的karaoke文件解析。如果你知道你所解析的文件格式是什么,你可以只加入你所需要的JAR文件,这可以让你的发布包更小,但是为了完备性,我们将所有依赖的JAR全部加入,比如pdfbox,poi和icu4j在./cores/karaoke/lib。
Solr Cell parameters
在进入示例之前,我们看一下Solr Cell的配置参数,所有参数都是可选的。
首先,Solr Cell决定文档的格式。它通常会猜的比较准,但它也可以与下面的参数配合:
l Resource.name:这个可选参数是指定文件的名字。它能帮助Tika来确定正确的MIME类型。
l Stream.type:这个可选参数允许你明确地指定文档MIME类型,它会优先于Tika的猜测结果。
Tika将所有的输入文档软换为基础的XHTML文档,在头部分中包含元数据。元数据会成为域,内容的文档会映射到content域。下面的参数更详细:
l Capture:X会被拷贝它自己的域中的HTML元素名称(比如:“p”),它可以被设置多次。
l captureAttr:设置为true来获取XHTML属性到域中。一个常见的例子是用Tika抽取从<a/>抽取href属性来索引另一个域的索引。
l xpath:允许你指定一个XPath查询,它不会将元素的文本放入content域。如果你只要得到元数据,丢弃XTHML中的正文,你可以用xpath=/xhtml:html/xhtml:head/descendant:node()。注意为每个元素使用xhtml:命名空间前缀。注意它支持一部分XPath命令。见http://tika.apache.org/0.8/api/org/apache/tika/sax/xpath/XPathParser.html。API中没有提到到它支持/descendant:node()。
l literal. [fieldname]:允许你为这个域一个特定的域值,比如,为ID域。
现在产生的域名都应该与Schema中的域名对应。下面是控制产生域名的参数:
l lowernames:将域名都小写,并将非字母数字的字符都转换成下划线。比如Last-Updated变为last_updated。
l fmap.[tikaFieldName]:将源域名转换为目标域名。比如,fmap.last_modified=timestamp映射将由Tika产生元数据域的last_modified映射到Solr Schema中timestamp域。
l uprefix:这个前缀用于域名,如果没有加前缀的名字不匹配Schema中的域名。使用这个前缀可以与动态域中进行匹配:
uprefix=meta_
<dynamicField name="meta_*" type="text_general" indexed="true"
stored="true" multiValued="true"/>
l defaultField:如果没有指定uprefix,并没有匹配Schema中的域名,那么域的值将写入这个参数指定的域名中。这样可以将所有元数据都入一个多值的域。
defaultField=meta
<field name="meta" type="text_general" indexed="true"
stored="true" multiValued="true"/>
其它的一些参数:
l boost. [fieldname]:由参数值对指定域进行Boost,来影响打分。
l extractOnly:设置为true,仅返回Tika解析的XHTML的文档结档,而不进行建索引。通常还会带上参数wt=json&indent=true来使XHTML更容易读。这个选项用来帮助调试。
l extractFormat:(当extractOnly=true时)默认为xml,它产生XHMTL结构,可以设置为text产生从文档中抽取的原始文本。
Extracting karaoke lyrics
现在我们可以将MIDI Post给Solr /update/extract请求处理器来抽取卡拉OK的歌词。一些经典的ABBA音乐在./examples/3/karaoke/目录,是从FreeKaraoke中得到http://www.freekaraoke.com,在此感谢。
要对Angel Eyes这首歌建索引,可以如下使用命令行:
>> curl 'http://localhost:8983/solr/karaoke/update/extract?fmap.content=text' -F "file=@angeleyes.kar"
不要提交你的改变:
>> curl http://localhost:8983/solr/karaoke/update?commit=true
你可以在发送一个文档的请求中加上commit=true请求参数进行提交,但是,在你建立大量文档时,这样做是低效的。
我们只有一个fmap.conent=text参数来指定源中正文的文本都到text域。在这个例子中angeleyes.kar的歌词都保存到Solr的text域中。现在我们看一下索引结果:http:// localhost:8983/solr/karaoke/select/?q=*:*。你会看到下面结果:
<result name="response" numFound="1" start="0">
<doc>
<arr name="text">
<str>
Angel Eyes by Abba sequenced by Michael Boyce
tinker@worldnet.att.netSoft karaoke@KMIDI KARAOKE
FILEWords@LENGL@TAngel Eyes@TABBALast night I was taking
a walk along the river/And I saw him together with a young
girl/And the look that he gave made me shiver/'Cause
he always used ...
</str>
</arr>
</doc>
</result>
你现在已经对MIDI文件中的歌词在text域中建立索引了。但是,元数据呢?Tika解析MIDI出的数据没有利用。嗯,这是动态域一展身手的地方了。每种二进制文件格式都有着很大的不同。幸运的是,Solr Cell通过使用uprefix属性让你来将元数据映射到动态域。我们在schema.xml中使用动态域:
<dynamicField name="metadata_*" type="string" indexed="true"
stored="true" multiValued="true"/>
因为有时我们想标准化地处理元数据属性,我们可以在solrconfig.xml中加入下面的配置:
<str name="uprefix">metadata_</str>
如果你提前知道一些元数据域的名字,并想进行域名映射,你可以配置:
<str name="fmap.content_type">content_type</str>
当你搜索所有文档时,你应该可以看到Angel Eyes中所有的元数据,除了content_type外,其它都以metadata_为前缀。
<str name="content_type">audio/midi</str>
<arr name="metadata_patches"><str>0</str></arr>
<arr name="metadata_stream_content_type"><str>application/octetstream
</str></arr>
<arr name="metadata_stream_size"><str>55677</str></arr>
<arr name="metadata_stream_source_info"><str>file</str></arr>
<arr name="metadata_tracks"><str>16</str></arr>
显然在大多情况下,你每次索引同一文件,你不想再产生一个新的Solr文档。如果在你的Schema中的uniqueKey域指定了如id的域,那你可以用literal.id=34参数提供一个特定的ID。每次你用相同的ID索引文件,它就会删除再插入这个文件。但是这就要求你要有管理ID的能力,可能需要第三方的支持,比如数据库。如果你想用元数据来做为ID,比如Tika提供的stream_name,那你可以用fmap.stream_name=id来映射域。要在这个例子中使用,更新./examples/cores/karaoke/schema.xml指定<uniqueKey>id</uniqueKey>。
>> curl 'http://localhost:8983/solr/karaoke/update/extract?fmap.
content=text&fmap.stream_name=id' -F "file=@angeleyes.kar"
这要求你所定义的id是字符串型,而不是数值型。
Indexing richer documents
索引对MIDI文件中的卡拉OK歌词建索引是个微不足道的例子了。我们只简单的忽略了所有的内容,然后把它们存到Solr的text域中,不对文档的结构进行任何的处理。
但是其它的文档结构,比如PDF,会更复杂,只是取得文本可能不会得到很好的搜索结果。我们看一下Take a Chance on Me,它是一个解释Monte Carlo模拟是什么的PDF文件。
打开./examples/3/karaoke/mccm.pdf,你会看到一个复杂的PDF有很多字符,背景图片,复杂的数学公式,希腊字母,和图表。尽管这个PDF很复杂,索引它和索引卡拉OK文件一样简单:
>> curl 'http://localhost:8983/solr/karaoke/update/extract?map.
content=text&commit=true' -F "file=@mccm.pdf"
如果你将文件名作为stream_name的参数进行搜索:http://localhost:8983/solr/karaoke/select/?q=stream_name:mccm.pdf,你会看到由Tika抽取出的Last_modified元数据被映射到solrconfig.xml中的last_modified域。
其中设置的lowercase=true会将Last-Modified映射到last_modified,使它符合域名的规范。
<doc>
<arr name="id">
<str>mccm.pdf</str>
</arr>
<arr name="last_modified">
<str>Sun Mar 03 15:55:09 EST 2002</str>
</arr>
<arr name="text">
<str>
Take A Chance On Me
那么对于富文档,我们将如何处理元数据和内容呢?在URL加入extractOnly=true,它会输出Solr Cell解析出的XML文档,包括元数据域,而不对文档建索引。
追加wt=json会使它更容易解析出内嵌的XML内容:
>> curl 'http://localhost:8983/solr/karaoke/update/extract?extractOnly=tr
ue&wt=json&indent=true' -F "file=@mccm.pdf"
复制粘贴JSON输出中内嵌的XML内容,用你最喜欢的HTML浏览工具查看内容。我用的是TextMate的HTML插件,另一个不错的选择在http://xmlindent.com/。
<html>
<head>
<meta name="stream_source_info" content="file">
<meta name="subject" content="Monte carlo condensed">
<meta name="Last-Modified" content="2002-03-03T20:55:09Z">
<meta name="Author" content="Andrew" n.=">
<meta name="xmpTPg:NPages" content="11">
<meta name="Creation-Date" content="2002-03-03T20:53:14Z">
<meta name="Keywords" content="ABBA">
<title>
Take A Chance On Me
</title>
</head>
<body>
<p>
"
</p>
<div class="page">
<p>
Take A Chance On MeMonte Carlo Condensed MatterA very brief
guide to Monte Carlo simulation.An explanation of what I do.A chance
for far too many ABBA puns.
</p>
</div>
<p>
</p>
<div class="page">
<p>
What's The Name Of The Game?Simulation:'I have a dream,
a fantasy,to help me through reality'Given some model many-body
system.Simulate the evolution of the system.We can then measure
various observables.Attempt to predict properties of real systems.
Eg Equilibrium Properties All about knowing free-energies, phase
behavior, compressibility, specific heat,knowing M(E), knowing m.
</p>
</div>
返回的XHTML文档包含从文档在<head/>中的元数据,还有以XHTML格式表示的正文内容。
Update request processors
无论你如何选择导入数据,都要在Solr中有一个最后一步的配置点,允许它在索引导入数据之前处理数据。Solr的请求处理器是将文档放到更新请求处理器链(update request processor chain)上进行数据更新。如果你在solrconfig.xml中查找updateRequestProcessorChain你会看到示例。
它可以通过配置update.chain,选择哪个更新链来处理更新请求。它有一定用,但你可能只会用到一个链。如果没有链被指定,你会有一个默认的链,由LogUpdateProcessorFactory和RunUpdateProcessorFactory组成。下面是一些可以选择的更新请求处理器。它们名字都以UpdateProcessorFactory结尾。
l SignatureUpdateProcessorFactory:它基于你指定的域产生一个Hash ID。如果你想对你的数据去重,那么这个会很适合你。更多的信息见:http://wiki.apache.org/solr/Deduplication。
l UIMAUpdateProcessorFactory:它将文档传给Unstructured Information Management Architecture(UIMA),一个对文档进行自然语言处理的模块。更多信息见:http://wiki.apache.org/solr/SolrUIMA。
l LogUpdateProcessorFactory:它会在更新发生时记录Log信息。
l RunUpdateProcessorFactory:它是真正建索引的部分,不要忘了它,否则文档更新不会有效果!更具体地说,它将文档传给Lucene,它会根据Schema中的配置对每个域进行处理。
未来会有很多的处理器来进行其它有趣的任务,包括一个类似DIH的ScriptTransformer的处理器,见SOLR-1725。你当然也可以写你自己的处理器。Solr对这点是支持扩展的,所以你不用改动Solr本身