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

2019-03-27 01:05|来源: 网路

Index Data

Author: David Smiley Eric Pugh

译者:Koala++ / 屈伟

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

l  与Solr交互。

l  以Solr的Update-XML格式发送数据。

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

l  以CSV 格式发送数据。

l  通过Solr的DataImportHandler直接读数据库和XML。

l  通过Solr的ExtractingRequestHandler从富文档中抽取数据。

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

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

Communicating With Solr

       Solr提供了很多导入数据的方式。在本节中,我们将先介绍一些方法,给出一些交互的例子。一些特定格式,比如Solr的Update-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发送给Solr。Solr支持一种它称为remote streaming的方式,这种方式需要提供给它一个URL,它可以是一个HTTP URL,但一般它是一个基于文件系统的URL,基于文件系统的URL,可以在数据已经在Solr所在的本机或是在网络驱动中时可以使用。这种方式减少了HTTP方式的代价。另一种方式是让Solr通过DataImportHandler去拉取数据,这种方式可以从数据库和其它来源拉取数据。DIH提供了一个可扩展的框架,它可以扩展以适应自定义的数据源。

Data formats

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

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

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

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

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

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

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

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

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

HTTP POSTing options to Solr

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

       发送HTTP POST的方法之一是使用UNIX命令行工具curl,我们将用它来介绍例子。另一个跨平台的工具是Solr中post.jar,它在Solr的example/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.xml的multipartUpdateLimitInKB中,默认是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

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

<?xml version="1.0" encoding="UTF-8"?>

<response>

<lst name="responseHeader">

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

</lst>

</response>

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

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

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

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

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

Remote streaming

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

       像前面一样,如果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 POST给Solr增加(或替换)两个文档:

<add overwrite="true">

<doc boost="2.0">

<field name="id">5432a</field>

<field name="type" ...</field>

<field name="a_name" boost="0.5"></field>

<!-- the date/time syntax MUST look just like this -->

<field name="begin_date">2007-12-31T09:40:00Z</field>

</doc>

<doc>

<field name="id">myid</field>

<field name="type" ...

<field name="begin_date">2007-12-31T09:40:00Z</field>

</doc>

<!-- more doc elements here as needed -->

</add>

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

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

Deleting documents

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

<delete><id>Artist:11604</id><id>Artist:11603</id></delete>

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

<delete><query>timestamp:[* TO NOW-12HOUR]</query></delete>

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

       查询语法会在第四章讨论。我简单解释上面的例子,我们假设我们的文档中有一个时间戳域,它是被索引的,并且你会每天进行一次数据全量重建。在一次全量数据更新后,就要删除以前的老数据。上面的查询会删除所有不在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:

<commit />

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

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

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

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

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

<commit commitWithin="30000"/>

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

<optimize />

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

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

<optimize waitFlush="true" waitSearcher="true"/>

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

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

<rollback />

Sending CSV formatted data to Solr

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

       要得到MusicBrainz的Track数据,可以从一个本地的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,则在最后一步移除域值开始和结尾的空格,即使是那些被引号引起来的空格。默认为false。Solr已经进行了初步的去空白字符了,但引号引起的空格不会被去除。

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映射为Album,2映射为Single。


转自:http://www.cnblogs.com/rainbowzc/p/3680000

相关问答

更多
  • 表浪费时间咧,小白~
  • 你在你链接指向的页面,点击屏幕左侧稍靠下的need to Download Code,跳转后点击分页码101-150,下方有一条叫做Begin Spring或Begin Spring Framework,点进去下载,看看是不是你需要的源码。
  • 正如您正确地发现这些是Solr 5.x特定功能和DSE 4.8随Solr 4.10.x一起提供。 当DSE更新时,您将能够使用这些新的Solr 5.x功能。 As you rightly found out these are Solr 5.x specific features and DSE 4.8 comes with Solr 4.10.x. You will be able to use these new Solr 5.x features when DSE gets updated.
  • 两者都是合法的方法,但是存在权衡。 首先,你的数据集有多大? 如果它足够大,您可能想要跨多台服务器进行分区,则可能有不同的索引。 其次,性能有多重要 - 将它们一起编入索引可能会导致性能下降,但程度取决于数据量以及查询的复杂程度。 第三,你是否需要在同一个搜索中查询多种数据类型? 如果是这样,将所有内容编入索引可以是一种方便的方法。 从技术上讲,这可以通过单独的索引来实现,但为查询获得最相关的结果可能是一个挑战(不是它已经不是) 第四,具有单一模式和配置的单个索引可以简化将部署和维护系统的人员的生命。 另一 ...
  • 我亲爱的朋友,你只是误输了其中一个字段。 尝试这个链接,你想要同时笑和哭。 HTTP://本地主机:8983 / solr的/维基/选择Q = *%3A * FL = ID + titleText +用户+修订&重量= JSON&缩进=真 您在架构中提到的标题是“titleText”,您的限制单独提到“标题”和“文本”。 所以上帝速度,你可以通过环聊与我保持联系:porous999@gmail.com The provided setting will work fine with classic sche ...
  • 假设all_data是一个单元格数组,并且每个单元格包含一个矩阵(至少有三列): result = cellfun(@(x) bsxfun(@rdivide, x, x(:,3)), all_data, 'uniformoutput', 0); Assuming that all_data is a cell array and that each cell contains a matrix (with at least three columns): result = cellfun(@(x) bsx ...
  • 谷歌现在解析JavaScript 。 只要您的JavaScript代码可供Googlebot查找和解析,Google就应该能够正确地为这些网页编制索引。 Google now parses JavaScript. As long as your JavaScript code is available for Googlebot to find and parse Google should be able to index those pages properly.
  • 在这种情况下,您需要存储父文档(框)和子文档(项)之间的关系。 幸运的是,BlockJoinQuery用于此类目的。 本文将详细介绍Lucene 3.x,或者您可以使用Lucene 4.x提供的ToParentBlockJoinQuery。 In this case you need to store relation between parent document (box) and child documents (items). Fortunately there is BlockJoinQuery ...