【转】企业级搜索引擎Solr 第三章 索引数据

转载自:http://quweiprotoss.blog.163.com/blog/static/4088288320128583524434/



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

Index Data

Author: David Smiley Eric Pugh

译者:Koala++ / 屈伟

       在这一章中我们将了解如何将数据传入Solr。这个传入的过程称之为索引,尽管中间还包含了导入的过程。本章的结构如下:

l  Solr交互。

l  SolrUpdate-XML格式发送数据。

l  提交,优化,回滚和删除。

l  CSV 格式发送数据。

l  通过SolrDataImportHandler直接读数据库和XML

l  通过SolrExtractingRequestHandler从富文档中抽取数据。

l  UpdateRequestProcessors进行文档后处理(post-processing)。

你会在第九章看到一些相关的内容,第九章中有语言绑定,框架集成,包括爬虫。大都用SolrUpdate-XML格式。

Communicating With Solr

       Solr提供了很多导入数据的方式。在本节中,我们将先介绍一些方法,给出一些交互的例子。一些特定格式,比如SolrUpdate-XML的细节随后介绍。

Direct HTTP or a convenient client API

       应用与Solr通过HTTP方式交互,你可以选择直接用你喜欢的HTTP客户端API,也可以使用与Solr集成的API,比如SolrJ或是Sunspot,它们将处理与HTTP交互的细节。这些API将在第九章介绍。HTTP Solr交互并不意味着需要索引的数据一定要通过HTTP传输,你马上会学习到如何告诉Solr去取数据。

Push data to Solr or have Solr pull it

       尽管一个应用通过HTTP方式与Solr通信,并不意味着它需要将文档通过HTTP发送给SolrSolr支持一种它称为remote streaming的方式,这种方式需要提供给它一个URL,它可以是一个HTTP URL,但一般它是一个基于文件系统的URL,基于文件系统的URL,可以在数据已经在Solr所在的本机或是在网络驱动中时可以使用。这种方式减少了HTTP方式的代价。另一种方式是让Solr通过DataImportHandler去拉取数据,这种方式可以从数据库和其它来源拉取数据。DIH提供了一个可扩展的框架,它可以扩展以适应自定义的数据源。

Data formats

       下面是多种在Solr用来建索引的格式:

l  SolrUpdate-XMLSolr接受一种通过XML格式表达的Solr特定的格式。它也有删除,优化和提交的命令。

?  其它XML:任意的XML带上一个XSLT文件给SolrSolr会将XML转化成Update-XML格式以进行后面的处理。

?  SolrUpdate-JSONSolrUpdate-XML的一个JavaScript Object Notation变形。更多细节见http://wiki.apache.org/solr/UpdateJSON

?  Java-BinSolrUpdate-XML的一个高效的二进制变形。正式地只有SolrJ客户端API支持,但也有第三方的Ruby支持。

?  CSV:逗号(或其它符号)分隔符的格式。

?  富文档:大多数常见的文件格式,比如PDFXLSDOCPPT。文本和元数据都可以从这些格式中抽取出来,并放入Solr的域中。这可以通过Solr Cell Contrib模式完成。

我们将通过把MusicBrainz的数据以XMLCSV和数据库的方式导入Solr来展示Solr的能力。其它的例子将展示通过DIH将爬取的文件导入,和通过Solr Cell导入。但是通常来说一个应用只会用一种格式来导入。

在我们介绍这些方法之前,我们先介绍一下cURLremote streaming,这两个是基本知识。

HTTP POSTing options to Solr

       Solr通过HTTP POST接收命令,还可以接收文档数据。

       发送HTTP POST的方法之一是使用UNIX命令行工具curl,我们将用它来介绍例子。另一个跨平台的工具是Solrpost.jar,它在Solrexample/exampledocs目录下。要得到一些使用信息,用下面的命令运行:

>> java jar example/exampledocs/post.jar -help

       有几种让Solr索引数据的方式,并所有的方式都是通过HTTP POST

l  通过POST方式发送数据。curl--data-binary参数可以做到这点,并会带一个与格式相符的content-type头。

l  发送一些类似一个HTML格式的键值对。Curl使用-F来进行。如果你不是在数据库中得到数据,你可以用下面的方式来进行:

?  将数据放在stream.body参数中。如果它比较小,也许小于1M,这种方式没有问题。大小的限制是在solrconfig.xmlmultipartUpdateLimitInKB中,默认是2GB。如果你想提高限制,你应该再考虑一下你的方式。

?  stream.file参数引用Solr服务器上的一个本地文件,或是通过stream.url参数通过一个URL去取数据。这些方式Solr称之为remote streaming

下面是第一种选择的例子。我们假设有一个artists.xml在当前目录。我们可以用下面的命令Post这个文件。

>> curl http://localhost:8983/solr/mbartists/update -H 'Contenttype:text/xml; charset=utf-8' --data-binary @artists.xml

       如果它成功了,你会得到下面的输出:

0128

       要用stream.body来完成上例,你可以写:

curl http://localhost:8983/solr/mbartists/update -F [email protected]

       在两个例子中,@符号指示curl从文件中取得数据。如果XML比较短,你可以直接在命令行中写:

curl http://localhost:8983/solr/mbartists/update -F stream.body=' '

       注意在值中有一个空格,这是有意为之的。在本例中,curl对待@<有特殊含义。在本例中应该用form-string而不是-F。但是我懒得打字了。

Remote streaming

       在前面的例子中,我们通过HTTP方式将数据发给Solr建索引。另外,我们可以通过POSTSolr一个数据的位置让它去取数据,数据的位置可以是文件路径也可以是HTTPURL

       像前面一样,如果Solr没有处理完请求,那么是不会返回响应的。如果文件大小合适或是它已经在某一已知的URL中了,那么你会发现remote streaming更快并且/或者更方便。

       下面是一个Solr访问一个本地文件的例子:

curl http://localhost:8983/solr/mbartists/update -F stream.file=/tmp/artists.xml

       如果要使用URL,那么参数就改为stream.url,并且将值指定为一个URL。我们现在传递的键值对参数,而不是真正的数据。

Solr's Update-XML format

       你可以通过使用一个XML格式化的方式,来提供建索引的文档,告诉Solr提交改变,来优化索引,删除文档。下面是一个示例XML文件,你可以通过HTTP POSTSolr增加(或替换)两个文档:

5432a

2007-12-31T09:40:00Z

myid

2007-12-31T09:40:00Z

       其中overwirte属性默认为true保证你在schema中指定为unique的域的值唯一,如果你添加的另一个文档在unique的域中有相同的值,那么这个文档会替换前一个文档。你不会得到一个错误。

       其中boost值会影响匹配文档时的得分。在文档或是域级别可选提供一个boost值。默认值是1.0,即无boost。技术上讲,不应该对文档进行boost,只应该对域进行boost。域最终的boost值是文档的boost值乘以域的boost值。

Deleting documents

       你可以通过unique域删除一个文档。下面的例子是我们删除两个文档:

Artist:11604Artist:11603

       为更灵活地删除文档,你可以用Lucene/Solr查询删除文档:

timestamp:[* TO NOW-12HOUR]

       内容中的delete标签可以有多个你想删除的idquery标签,这样一次可以批量删除多个文档。

       查询语法会在第四章讨论。我简单解释上面的例子,我们假设我们的文档中有一个时间戳域,它是被索引的,并且你会每天进行一次数据全量重建。在一次全量数据更新后,就要删除以前的老数据。上面的查询会删除所有不在12小时以前建立索引的文档。12小时是随意选择一个值,但它需要小于24个小时并且大于加载所有数据的耗时。

Commit, optimize, and rollback

       发送给Solr的数据不能立即搜索到,删除的文档也不会立即失效。像数据库一样,改动需要先提交(commit)。最简单的方式是在Solr的更新URL后加上commit=true请求参数。这个请求可以是包含更新数据的请求也可以是一个空的请求。比如,你可以通过访问URL产生一个提交到我们的mbreleases索引:http://localhost:8983/solr/mbreleases/update?commit=true。你也可以通过下面的XML语法提交,你只需要将它发送给Solr

       你需要知道关于Solr提交的三个重要的点:

l  提交是缓慢的。速度依赖于索引的大小,Solrauto-warming配置,和SolrCache状态的提交,一次提交会花费一些时间。通常,它需要几秒钟,但在极端情况下,它会花费几分钟。要了解如何减少提交时间,可以参考第十章。

l  没有事务隔离:这意味着如果多个Solr客户端提交修改,并且提交的时间重叠,那么就可能一个客户端的在发出提交命令之前,一部分修改已经提交了。这种情况也适用于回滚(rollback)。如果你的应用中存在这个问题,你应该考虑只使用一个客户端处理Solr的更新。

l  同时提交是可以避免的,特别是多个客户端的情况。这个问题其实属于同时query warmingquery warming是影响提交时间的主要因素。如果有太多同时进行的warming Solr会使用大量的资源,甚至会产生一个错误,但是提交最后还是会正常提交。

如果你批量载入数据,在最后进行一次提交,这次提交你倒不用担心。但如果Solr由多个独立的客户端异步更新数据,提交可能很频繁也可能重复。为了解决这个问题,Solr有两个相似的特性,autoCommitcommitWithinautoCommitsolrconfig.xml中一小段注释掉的配置,配置后Solr会在达到文档数阈值或是时间阈值(最老未提交文档的时间)后自动提交。这样,你的应用不用再发送提交,Solr会自己来处理提交。commitWithin是一个类似的时间阈值选项。这个选项可以由客户端提交的更新信息设置,信息是放到XML更新数据的元素或是元素中,也可以通过设置请求的参数来设置。它会保证每隔多少毫秒进行一次提交。下面是30秒进行一次提交的例子:

       Lucene的索引内部是由一个或是多个Segments组成的。当索引文档的缓冲区写入磁盘时,它会创建一个新的Segment。删除信息是在另一个文件中,但它们也要写入文件。有时,当一个新Segment写入时,Lucene会将多个Segment合并。当Lucene只有一个Segment时,它处在已优化(optimized)状态。Segment个数越多,则查询的效率就越低下。当然,优化一个索引是需要代价的,你的索引越大,那么优化花费的时间就越长。最后优化命令的语法与提交是相同的。如果你想在URL中使用,你可以用http://localhost:8983/solr/mbreleases/update?optimize=true。对于XML格式,可以发送:

       建议在比如批量载入数据时,并且/或是如果有零星的更新时,可以在一天内比较空闲的时间显式地进行索引优化。后面章节会介绍如果优化时间过长的情况下,对多个索引进行优化。

       提交和优化都有两个布尔选项,它们默认设置为true

       如果你把它们设置为false,那么提交和优化命令会立即返回,即使操作并没有真正完成。所以如果你写一个脚本进行提交,并将上面两个选项设置为false,再进行查询。你会发现查询结果并没有反应出改变。通过等待数据入写磁盘(waitFlush)和等待新的索引可以反应数据改变(waitSearcher),则可以避免上述情况。

       最后还有一个索引命令回滚(rollback)。它可以将未提交的改变回滚。Solr的回滚命令可以通过URL参数:http://localhost:8983/solr/mbreleases/update?rollback=true或是XML

Sending CSV formatted data to Solr

       如果你已经有一个CSV格式的数据或是对你来说得到CSV文件比XML或是JSON格式要容易,那么你可以选择CSV方式导入数据。SolrCSV支持比较灵活。但你不能指定一个索引时的boost,但是它也不常用。

       要得到MusicBrainzTrack数据,可以从一个本地的PostgreSQL数据中用下面命令导出数据:

psql -U postgres -d musicbrainz_db -c "COPY (\

select 'Track:' || t.id as id, 'Track' as type, t.name as t_name,

t.length/1000 as t_duration, a.id as t_a_id, a.name as t_a_name,

albumjoin.sequence as t_num, r.id as t_r_id, r.name as t_r_name, array_

to_string(r.attributes,' ') as t_r_attributes, albummeta.tracks as t_r_

tracks \

from (track t inner join albumjoin on t.id = albumjoin.track \

inner join album r on albumjoin.album = r.id left join albummeta on

albumjoin.album = albummeta.id) inner join artist a on t.artist = a.id \

) to '/tmp/mb_tracks.csv' CSV HEADER"

       它大约会产生7百万行数据像下面一样的数据(前三行):

id,type,t_name,t_duration,t_a_id,t_a_name,t_num,t_r_id,t_r_name,t_r_

attributes,t_r_tracks

Track:183326,Track,In the Arms of Sleep,254,11650,The Smashing

Pumpkins,4,22471,Mellon Collie and the Infinite Sadness (disc 2: Twilight

to Starlight),0 1 100,14

Track:183328,Track,Tales of a Scorched Earth,228,11650,The Smashing

Pumpkins,6,22471,Mellon Collie and the Infinite Sadness (disc 2: Twilight

to Starlight),0 1 100,14

       代码和CSV文件都在本书提供的补充资料中。要将CSV文件导入Solr,运行下面的命令:

curl http://localhost:8983/solr/update/csv -F f.t_r_attributes.split=true

-F f.t_r_attributes.separator=' ' -F overwrite=false -F commit=true -F

stream.file=/tmp/mb_tracks.csv

       CSV选项通过-F来指定。

Configuration options

       下面是对每个配置选项参数的解释。对于前面的MusicBrainz CSV文件例子,命令中只设置了多值域的分隔符t_r_attributes,并为了效率而禁用了唯一键(unique key)处理,其它的都采用默认值。

l  separator:用于分隔域的分隔符。默认为逗号。

l  header:如果设置为true,则文件的第一行是域名。

l  fieldnames:如果第一行没有包含域名,那么你需要使用它来指定域名。用逗号分隔它们。如果某一列没有指定域名,这一列的值会被忽略。

l  skip:指定不用导入的域。

l  skipLines,指定要忽略输入文件中多少行。默认为0.

l  trim:如果为true,则在最后一步移除域值开始和结尾的空格,即使是那些被引号引起来的空格。默认为falseSolr已经进行了初步的去空白字符了,但引号引起的空格不会被去除。

l  encapsulator:这个符号是用于将一个域的值引起来,因为一个域中的值可能包括域分隔符,引起来后解析就不会错误地将域值解析成两个域值。它默认是双引号,除非它被转义了,比如:

11604, foo, "The ""second"" word is quoted.", bar

l  escapse:如果输入文本中有这个字符,那么下一个字符就会被转义字符本身,即它不会被转义的字符不会被认为是特殊字符,比如:

11604, foo, The second\, word is followed by a comma., bar

l  keepEmpty:指定是否空(0长度)域值是否应该被索引或是忽略。默认为false

l  overwirte:它是指是否有相同ID的文档是否应该覆盖另一个文档,ID是由Schema中指定的唯一键。它默认为true。如果你对确定你没有重复的ID,可以设置为false可以提高效率。

l  split:它用于有多值的域的切分。指定多值间的分隔符。

l  map:它可以将域值替换为另一个值,也可以移除某些域值。替换前和替换后的值用冒号分隔,你可以在MusicBrainz Track数据上用这一特性,你可将数值替换为一些更有意义的值。下面是一个例子:

-F keepEmpty=false -F f.t_r_attributes.map=0:

-F f.t_r_attributes.map=1:Album -F f.t_r_attributes.map=2:Single

       这会使0被移除,因为它是无用的数据,几乎所有的Track都有这个值。我们将1映射为Album2映射为Single

你可能感兴趣的:(经验积累)