以下资料整理自网络,觉的有必要合并在一起,这样方便查看。主要分为两部分,第一部分是对《db-data-config.xml》的配置内容的讲解(属于高级内容),第二部分是DataImportHandler(属于基础),第三部分是对db-data-config.xml的进阶(这个国内可能还没有人写过啊,我在google、baidu上都没有搜索到,最后可是拔代码,看solr的英文文档找的)
第一部分是对《db-data-config.xml》
query是获取全部数据的SQL
deltaImportQuery是获取增量数据时使用的SQL
deltaQuery是获取pk的SQL
parentDeltaQuery是获取父Entity的pk的SQL
Full Import工作原理:
执行本Entity的Query,获取所有数据;
针对每个行数据Row,获取pk,组装子Entity的Query;
执行子Entity的Query,获取子Entity的数据。
Delta Import工作原理:
查找子Entity,直到没有为止;
执行Entity的deltaQuery,获取变化数据的pk;
合并子Entity parentDeltaQuery得到的pk;
针对每一个pk Row,组装父Entity的parentDeltaQuery;
执行parentDeltaQuery,获取父Entity的pk;
执行deltaImportQuery,获取自身的数据;
如果没有deltaImportQuery,就组装Query
限制:
子Entity的query必须引用父Entity的pk
子Entity的parentDeltaQuery必须引用自己的pk
子Entity的parentDeltaQuery必须返回父Entity的pk
deltaImportQuery引用的必须是自己的pk
第二部分是DataImportHandler
关于DataImportHandler的具体使用方法,详见下文,如果你英文超级好,那看这个链接吧:http://wiki.apache.org/solr/DataImportHandler
大多数的应用程序将数据存储在关系数据库、xml文件中。对这样的数据进行搜索是很常见的应用。所谓的DataImportHandler提供一种可配置的方式向solr导入数据,可以一次全部导入,也可以增量导入。
概览
目标
设计思路
这个Handler首先要在solrconfig.xml文件中配置下,如下所示。
<requestHandlername="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<strname="config">/home/username/data-config.xml</str>
</lst>
</requestHandler>
从它的名字上,我们或许也可以猜到,
DataImportHandler正是requestHandler的实现。我们一共需要在两个地方配置文件中进行一些配置。
跟关系数据库一起使用
下面几个步骤是必要的.
配置数据源
将dataSource标签直接添加到dataConfig下面,即成为dataConfig的子元素.
<dataSourcetype="JdbcDataSource" driver="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost/dbname" user="db_username"password="db_password"/>
多数据源
一个配置文件可以配置多个数据源。增加一个dataSource元素就可以增加一个数据源了。name属性可以区分不同的数据源。如果配置了多于一个的数据源,那么要注意将name配置成唯一的。
例如:
<dataSourcetype="JdbcDataSource" name="ds-1" driver="com.mysql.jdbc.Driver"url="jdbc:mysql://db1-host/dbname" user="db_username"password="db_password"/>
<dataSourcetype="JdbcDataSource" name="ds-2"driver="com.mysql.jdbc.Driver"url="jdbc:mysql://db2-host/dbname" user="db_username"password="db_password"/>
然后这样使用
..
<entityname="one" dataSource="ds-1" ...>
..
</entity>
<entityname="two" dataSource="ds-2" ...>
..
</entity>
..
配置JdbcDataSource
JdbcDataSource中的属性有
任何其他的在JdbcDataSource中配置的属性,都会被直接传给jdbc driver
配置data-config.xml
solr document是schema,它的域上的值可能来自于多个表.
data-config.xml的根元素是document。一个document元素代表了一种文档。一个document元素中包含了一个或者多个root实体。一个root实体包含着一些子实体,这些子实体能够包含其他的实体。实体就是,关系数据库上的表或者视图。每个实体都能够包含多个域,每个域对应着数据库返回结果中的一列。域的名字跟列的名字默认是一样的。如果一个列的名字跟solr field的名字不一样,那么属性name就应该要给出。其他的需要的属性在solrschema.xml文件中配置。
为了能够从数据库中取得想要的数据,我们的设计支持标准sql规范。这使得用户能够使用他任何想要的sql语句。root实体是一个中心表,使用它的列可以把表连接在一起。
dataconfig的结构
dataconfig的结构不是一成不变的,entity和field元素中的属性是随意的,这主要取决于processor和transformer。
以下是entity的默认属性
一下是SqlEntityProcessor的属性
· query (required) :sql语句
· deltaQuery :只在“增量导入”中使用
· parentDeltaQuery :只在“增量导入”中使用
· deletedPkQuery :只在“增量导入”中使用
· deltaImportQuery : (只在“增量导入”中使用) . 如果这个存在,那么它将会在“增量导入”中导入phase时代替query产生作用。这里有一个命名空间的用法${dataimporter.delta.}详情请看solr1.4.
Commands
The handler 通过httprequest向外界提供它的API . 以下是一些或许你会用到的操作
Full Import例子
让我们来看下面的例子.假设我们数据库中的表结构如下:
This is arelational model of the same schema that Solr currently ships with.我们使用这个例子来为我们的DataImportHandler建data-config.xml。我们已经使用这个结构在HSQLDB上建立了一个数据库.好,现在开始了, 跟着下面的步骤走:
上面给出的solr目录是一个多核的solr home。它有两个核,一个是DB example,一个是RSSexample(新属性)。
这个例子的data-config.xml如下:
<dataConfig>
<dataSourcedriver="org.hsqldb.jdbcDriver"url="jdbc:hsqldb:/temp/example/ex" user="sa" />
<document name="products">
<entity name="item"query="select * from item">
<field column="ID"name="id" />
<field column="NAME"name="name" />
<field column="MANU"name="manu" />
<field column="WEIGHT"name="weight" />
<field column="PRICE"name="price" />
<fieldcolumn="POPULARITY" name="popularity" />
<fieldcolumn="INSTOCK" name="inStock" />
<fieldcolumn="INCLUDES" name="includes" />
<entity name="feature"query="select description from feature whereitem_id='${item.ID}'">
<fieldname="features" column="description" />
</entity>
<entityname="item_category" query="select CATEGORY_ID fromitem_category where item_id='${item.ID}'">
<entityname="category" query="select description from category where id= '${item_category.CATEGORY_ID}'">
<fieldcolumn="description" name="cat" />
</entity>
</entity>
</entity>
</document>
</dataConfig>
这里,根实体是一个名叫“item”的表,它的主键是id。我们使用语句 "select *from item"读取数据.每一项都拥有多个特性。看下面feature实体的查询语句
<entity name="feature"query="select description from feature whereitem_id='${item.id}'">
<field name="feature"column="description" />
</entity>
feature表中的外键item_id跟item中的主键连在一起从数据库中取得该row的数据。相同地,我们将item和category连表(它们是多对多的关系)。注意,我们是怎样使用中间表和标准sql连表的
<entityname="item_category" query="select category_id fromitem_category where item_id='${item.id}'">
<entity name="category" query="select description fromcategory where id = '${item_category.category_id}'">
<field column="description" name="cat" />
</entity>
</entity>
短一点的 data-config
在上面的例子中,这里有好几个从域到solr域之间的映射。如果域的名字和solr中域的名字是一样的话,完全避免使用在实体中配置域也是可以的。当然,如果你需要使用转换器的话,你还是需要加上域实体的。
下面是一个更短的版本
<dataConfig>
<dataSourcedriver="org.hsqldb.jdbcDriver"url="jdbc:hsqldb:/temp/example/ex" user="sa" />
<document>
<entity name="item"query="select * from item">
<entity name="feature"query="select description as features from feature whereitem_id='${item.ID}'"/>
<entityname="item_category" query="select CATEGORY_ID fromitem_category where item_id='${item.ID}'">
<entityname="category" query="select description as cat from categorywhere id = '${item_category.CATEGORY_ID}'"/>
</entity>
</entity>
</document>
</dataConfig>
使用“增量导入”命令
你可以通过访问URL http://localhost:8983/solr/dataimport?command=delta-import 来使用增量导入。操作将会新起一个线程,response中的属性statue也将显示busy now。操作执行的时间取决于你的数据集的大小。在任何时候,你都可以通过访问 http://localhost:8983/solr/dataimport 来查看状态。
当增量导入被执行的时候,它读取存储在conf/dataimport.properties中的“start time”。它使用这个时间戳来执行增量查询,完成之后,会更新这个放在conf/dataimport.properties中的时间戳。
Delta-Import例子
我们将使用跟“完全导入”中相同的数据库。注意,数据库已经被更新了,每个表都包含有一个额外timestamp类型的列 叫做last_modified。或许你需要重新下载数据库,因为它最近被更新了。我们使用这个时间戳的域来区别出那一行是上次索引以来有更新的。
看看下面的这个 data-config.xml
<dataConfig>
<dataSourcedriver="org.hsqldb.jdbcDriver"url="jdbc:hsqldb:/temp/example/ex" user="sa" />
<document name="products">
<entity name="item"pk="ID" query="select * from item"
deltaQuery="select id fromitem where last_modified > '${dataimporter.last_index_time}'">
<entity name="feature"pk="ITEM_ID"
query="selectdescription as features from feature where item_id='${item.ID}'">
</entity>
<entityname="item_category" pk="ITEM_ID, CATEGORY_ID"
query="select CATEGORY_IDfrom item_category where ITEM_ID='${item.ID}'">
<entityname="category" pk="ID"
query="selectdescription as cat from category where id ='${item_category.CATEGORY_ID}'">
</entity>
</entity>
</entity>
</document>
</dataConfig>
注意到item实体的属性deltaquery了吗,它包含了一个能够查出最近更新的sql语句。注意,变量{dataimporter.last_index_time}是DataImporthandler传过来的变量,我们叫它时间戳,它指出“完全导入”或者“部分导入”的最后运行时间。你可以在data-config.xml文件中的sql的任何地方使用这个变量,它将在processing这个过程中被赋值。
注意
deltaQuery="select id from itemwhere id in
(select item_id as id from featurewhere last_modified > '${dataimporter.last_index_time}')
or id in
(select item_idas id from item_category where item_id in
(select id as item_id fromcategory where last_modified > '${dataimporter.last_index_time}')
orlast_modified > '${dataimporter.last_index_time}')
orlast_modified > '${dataimporter.last_index_time}'"
<dataConfig>
<dataSourcedriver="org.hsqldb.jdbcDriver"url="jdbc:hsqldb:/temp/example/ex" user="sa" />
<document>
<entity name="item"pk="ID" query="select * from item"
deltaQuery="select id fromitem where last_modified > '${dataimporter.last_index_time}'">
<entityname="feature" pk="ITEM_ID"
query="select DESCRIPTIONas features from FEATURE where ITEM_ID='${item.ID}'"
deltaQuery="selectITEM_ID from FEATURE where last_modified >'${dataimporter.last_index_time}'"
parentDeltaQuery="select ID from item where ID=${feature.ITEM_ID}"/>
<entityname="item_category" pk="ITEM_ID, CATEGORY_ID"
query="selectCATEGORY_ID from item_category where ITEM_ID='${item.ID}'"
deltaQuery="selectITEM_ID, CATEGORY_ID from item_category where last_modified >'${dataimporter.last_index_time}'"
parentDeltaQuery="select ID from item whereID=${item_category.ITEM_ID}">
<entityname="category" pk="ID"
query="selectDESCRIPTION as cat from category where ID ='${item_category.CATEGORY_ID}'"
deltaQuery="selectID from category where last_modified >'${dataimporter.last_index_time}'"
parentDeltaQuery="select ITEM_ID,CATEGORY_ID from item_category where CATEGORY_ID=${category.ID}"/>
</entity>
</entity>
</document>
</dataConfig>
除了根实体(有两个)以外,这里一共有三个查询,每个实体个一个。
查询语句,为我们取得需要建立索引的数据。
下面是一些值得注意的地方:
XML/HTTP Datasource使用指南
DataImportHandler能够帮我们为基于HTTP的数据源建立索引.目前支持REST/XML APIs和RSS/ATOM Feeds.
配置HttpDataSource
HttpDataSource在dataconfig.xml中的配置看起来应该像这样:
<dataSourcetype="HttpDataSource" baseUrl="http://host:port/"encoding="UTF-8" connectionTimeout="5000"readTimeout="10000"/>
属性:
在 data-config.xml中的配置
一个 xml/http data source中的实体有下面一些属性,也可以有上面提到的默认属性。
域能够有以下这些属性 (此外还有那些默认值):
如果一个API支持分块数据(当一个数据集太大时),可能需要多次调用才能完成这个处理过程。XPathEntityprocessor通过转换器支持这个特性。如果转换器返回的的行带有属性“hasMore”,并且这个属性的值等于true,那么Processor将会使用同样的url模板发出令一次请求(实际的url是需要重新计算的)。一个转换器也可以传递一个完整的url路径,这个url被包含在属性“nextUrl”中,nextUrl的值必需是一个完整的url。
XPathEntityProcessor通过实现streaming parser来支持取得xpath子集的操作。完整的xpath是不被支持的,但是常见的应用都是受支持的。
HttpDataSource例子
下载在DB部分中的“完全导入”例子,试着去体验一下。我们将在这里例子中为slashotRSS建立索引。
这个例子的data-config配置看起来像这样。
<dataConfig>
<dataSourcetype="HttpDataSource" />
<document>
<entityname="slashdot"
pk="link"
url="http://rss.slashdot.org/Slashdot/slashdot"
processor="XPathEntityProcessor"
forEach="/RDF/channel | /RDF/item"
transformer="DateFormatTransformer">
<fieldcolumn="source" xpath="/RDF/channel/title"commonField="true" />
<fieldcolumn="source-link" xpath="/RDF/channel/link"commonField="true" />
<fieldcolumn="subject" xpath="/RDF/channel/subject"commonField="true" />
<fieldcolumn="title" xpath="/RDF/item/title" />
<field column="link"xpath="/RDF/item/link" />
<fieldcolumn="description" xpath="/RDF/item/description" />
<fieldcolumn="creator" xpath="/RDF/item/creator" />
<field column="item-subject"xpath="/RDF/item/subject" />
<fieldcolumn="date" xpath="/RDF/item/date"dateTimeFormat="yyyy-MM-dd'T'hh:mm:ss" />
<fieldcolumn="slash-department" xpath="/RDF/item/department"/>
<fieldcolumn="slash-section" xpath="/RDF/item/section" />
<fieldcolumn="slash-comments" xpath="/RDF/item/comments" />
</entity>
</document>
</dataConfig>
这个data-config有很多值得借鉴的地方。 我建议你看下SlashdotRSS的结构图,它有一些头部元素,例如title、link、subject。这些元素将分别通过xpath语法映射到source、source-link、subject这些solr域。这个种子有多个item元素,这些元素包含了真正的新闻信息。所以,我们希望做得是,为每一个item元素建立一个文档。
XPathEntityprocessor 是一行一行的读取xml文件的(这里的行指的是一个xml元素)。它使用属性“forEach”去识别每一行 。在这个例子一种“forEach”的值是'/RDF/channel | /RDF/item'。也就是说这个xml有两种类型的行(这里使用一个OR的xpath语法,用以支持多个类型) 。当遇到一个行的时候,它会在行的域声明中读取尽量多的域。在这个例子中,当它读到行“/RDF/channel”时,它将会得到3个域。它处理完这个行的时候,它就会意识到,这个行并没有pk这个域的值,于是它并不会试图去建立一个solr文档(即使它去做,它也会失败)。但是这个三个域都有一个属性commonField,并且它的值是true,所以它将会保留这个域的值,以便后面的行可以使用
它继续前进,然后遇到/RDF/item ,接着一个个处理这些行。它将会取得除了那个三个域之外的所有域。但是因为他们是common field。处理器会把公共域也加到这个记录中,然后写入索引。
transformer=DateFormatTransformer又是什么呢?你可以看一下DateFormatTransformer有关部分。
你可以使用这些特性来从REST API,例如 rss、atom、xml、其他solr服务器、甚至是格式良好的xhtml文档,建立索引。我们的xpath语法有它自己的限制(不支持通配符,只可以是全路径),但是一般的应用是绝对没有问题的,而且它是基于streaming parser的,它非常快,并且在读取非常大的xml文件的时候,它的内存消耗始终保持如一。它不支持命名空间,它却可以处理带有命名空间的xml文件。当你处理带有命名空间的xpath的时候,你需要做的是,丢弃命名空间部分,只留下其他的部分(例如,这个标签,相对应的xpath部分是subject)。很容易,是吧?而且你不需要写一行代码,好好享受吧。
注意 : 不像数据库,如果你使用XPathEntityProcessor,想忽略域声明是不可能。域通过你声明的xpaths来从xml中解析相应的数据。
例子:索引 wikipedia
利用下面的data-config.xml文件可以对wikipedia的数据建立索引。从wikipedia下载下来的pages-articles.xml.bz2文件解压之后大概有18g。
<dataConfig>
<dataSourcetype="FileDataSource" encoding="UTF-8" />
<document>
<entity name="page"processor="XPathEntityProcessor" stream="true"forEach="/mediawiki/page/"url="/data/enwiki-20080724-pages-articles.xml">
<field column="id"xpath="/mediawiki/page/id" />
<fieldcolumn="title" xpath="/mediawiki/page/title" />
<fieldcolumn="revision" xpath="/mediawiki/page/revision/id" />
<fieldcolumn="user"xpath="/mediawiki/page/revision/contributor/username" />
<fieldcolumn="userId" xpath="/mediawiki/page/revision/contributor/id"/>
<fieldcolumn="text" xpath="/mediawiki/page/revision/text" />
<fieldcolumn="timestamp"xpath="/mediawiki/page/revision/timestamp" />
</entity>
</document>
</dataConfig>
schema.xml中有关的部分如下所示:
<fieldname="id" type="integer" indexed="true"stored="true" required="true"/>
<fieldname="title" type="string" indexed="true"stored="false"/>
<fieldname="revision" type="sint" indexed="true"stored="true"/>
<fieldname="user" type="string" indexed="true"stored="true"/>
<fieldname="userId" type="integer" indexed="true"stored="true"/>
<fieldname="text" type="text" indexed="true"stored="false"/>
<fieldname="timestamp" type="date" indexed="true"stored="true"/>
<field name="titleText"type="text" indexed="true" stored="true"/>
...
<uniqueKey>id</uniqueKey>
<copyFieldsource="title" dest="titleText"/>
为7278241个文章建立索引大概花了2个小时40分,内存使用量的峰值在4G左右。
使用“增量导入”命令
只有SqlEntitiProcessor支持增量数据!XPathEntityProcessor还没有实现它。所以,不幸运的是,现在还不能为“增量导入”提供支持。如果你想要在XPathEntityProcessor中实现这些方法,你可以在EntityProcessor.java中看看这些方法的解释。
Extending the tool with APIs
我们所展现的例子确实没有多大价值,单靠配置xml文件就满足所有的需求是不可能的。所以我们提供了一些抽象类,可以通过这些方法来提高功能。
Transformer
每一条从数据库中取得的数据能够被直接处理掉,或者通过它创建一个全新的域,它设置能够返回多行数据。配置文件必须像下面那样设置。
<entityname="foo" transformer="com.foo.Foo" ... />
注意-- trasformer的值必须是一个可以使用的classname。如果class包是'org.apache.solr.handler.dataimport' ,包名可以被忽略。solr.也是可以使用的,如果这个class在solr的一个包下的话。这个规则适应所有的可插入的类,像DataSource、EntityProcessor、Evaluator。
类Foo必须继承抽象类org.apache.solr.hander.dataimport.Transformer.这个类只有一个抽象方法。
transformer这个属性可以有多个transformers()(比如 transformer="foo.X,foo.Y")之间用逗号隔开。 transformers会形成一条处理链。它们将会按照它们的排列顺序起作用。
public abstractclass Transformer {
/**
* The input is a row of data and the outputhas to be a new row.
*
* @param context The current context
* @param row A row of data
* @return The changed data. It must be a Mapif it returns
* only one row or if there are multiple rows to be returned it must
* be a List>
*/
public abstract Object transformRow(Map row,Context context);
}
Context 是一个抽象的类,它提供上下文关系,这可能在处理数据的时候要用到。
另外,类Foo,可以选择不不实现这个抽象类,而只需要下面这个方法
public ObjecttransformRow(Map row)
So there is nocompile-time dependency on the DataImportHandler API
它的配置是灵活的。它允许用户向标签entity和field提供任意的属性。tool将会读取数据,并将它传给实现类。如果Transformer需要额外的的信息,它可以从context中取得。
正则表达式转换器
tool它提供了一个内嵌的转换器,叫做正则表达式转换器。它可以使用正则表达式从原数据中解析出我们想要的值。org.apache.solr.handler.dataimport.RegexTransformer 是它的名字.因为它属于默认的包,所以它的包名是可以被忽略的。
例子:
<entityname="foo" transformer="RegexTransformer"
query="selectfull_name , emailids from foo"/>
... />
<field column="full_name"/>
<field column="firstName"regex="Mr(/w*)/b.*" sourceColName="full_name"/>
<field column="lastName"regex="Mr.*?/b(/w*)" sourceColName="full_name"/>
<field column="mailId"splitBy="," sourceColName="emailids"/>
</entity>
属性
RegexTransfromer只对属性中有regex或者splitBy的域起作用。所有的属性我们列在下面。
这里,属性‘regex’和‘sourceColName’是转换器自定义的属性。它从resultSet中读取域‘full_name’的值,然后转换它,并将结果分别传给‘firstName’和‘lastName’。所以,尽管查询结果只返回一列“full_name”,但solr document依然可以获得额外的两个域“firstName”和‘lastName’。
域'emailids' 是一个用逗号分隔着的值。所以,我们最终可以从emailids得到一个以上的emial id。mailid在solr中应该被定义为多值的。
脚本转换器
你可以使用javascript或者其他的脚本语言来写转换器,只要java支持这种脚本。在这里我们应该使用java 6.
<dataConfig>
<script><![CDATA[
function f1(row) {
row.put('message', 'HelloWorld!');
return row;
}
]]></script>
<document>
<entity name="e"pk="id" transformer="script:f1" query="select * fromX">
....
</entity>
</document>
</dataConfig>
日期格式转换器
这里有一个内嵌的转换器,叫做DateFormatTransformer(日期格式转换器),这个在将字符型时间转换成java.util.Date的类型的时候是很有用的。
<fieldcolumn="date" xpath="/RDF/item/date"dateTimeFormat="yyyy-MM-dd'T'hh:mm:ss" />
属性
日期格式转换器只对带有属性“dateTimeFormat”的域才起作用。其他属性如下所示。
上面的域的定义在RSS例子中有使用,以转换RSS种子项中的时间格式。
数字格式转换器
能将一个字符串转换成一个数字,使用的是java中类NumberFormat。例子:
<fieldcolumn="price" formatStyle="number" />
默认情况下,类Numberformat使用系统的本地格式去转换一个字符串,如果你需要指定一个不同的本地类型的话,你可以像下面这样指定。例子:
<fieldcolumn="price" formatStyle="number"locale="de-DE" />
属性
数字格式转换器只对那些带有属性“formatStyle”的域有用。
模板转换器
使用DataImportHandler中强大的模板引擎来创建或者设定一个域的值。例如:
<entityname="e" transformer="TemplateTransformer" ..>
<fieldcolumn="namedesc"template="hello${e.name},${eparent.surname}" />
...
</entity>
这里模板的规则跟‘query’、‘url’的规则是一样的。它主要能帮我们将多个值连到一起,或者忘域值注入其他的字符。这个转换器只对拥有属性‘template’的域起作用。
属性
自定义模板转换器
如果你需要在将数据送给solr之前,对数据进行一些处理,你可以写一个你自己的转换器。让我们来看一个例子。在我们的schema中我们有一个单值的域叫做‘artistName’,类型是String。这个域的值包含了多个单词,例如‘Celine Dion’,这里有一个问题,这个值包含一些开头空格和结尾空格,这些空格不是我们想要的。solr的WhitespaceAnalyze在这里用不上,因为,我们并不想把这个字符串切词了。一个可以选择的解决方案就是自己写一个TrimTransformer。
一个简单的TrimTransformer
package foo;
public classTrimTransformer {
public Object transformRow(Maprow) {
String artist =row.get("artist");
if (artist != null)
row.put("ar",artist.trim());
return row;
}
}
不需要去继承任何类。这个类只需要有transformRow方法,就像上面的那样。DataImportHandler会自动辨别它,并使用反射机制来调用它。你可以在你的data-config.xml文件中这样来设置:
<entityname="artist" query="..."transformer="foo.TrimTransformer">
<field column="artistName"/>
</entity>
一个通用的TrimTransformer
假设,你想写一个通用的TrimTransformer,这样你就不用将要处理的列写在的代码里面。这里,我们需要在data-config.xml中设一个标记来表示这个域是否要应用这个转换器。
<entityname="artist" query="..."transformer="foo.TrimTransformer">
<field column="artistName"trim="true" />
</entity>
现在,你需要去继承 Transformer 这个抽象类,并使用Context中的API来获得实体中的域,并获得域中的属性,检查标记有没有被设值。
package foo;
public classTrimTransformer extends Transformer {
public Map transformRow(Map row,Context context) {
List> fields =context.getAllEntityFields();
for (Map field : fields) {
// Check if this fieldhas trim="true" specified in the data-config.xml
String trim =field.get("trim");
if("true".equals(trim)) {
// Apply trimon this fied
StringcolumnName = field.get("column");
// Get this field's value from the currentrow
String value =row.get(columnName);
// Trim and putthe updated value back in the current row
if (value != null)
row.put(columnName, value.trim());
}
}
return row;
}
}
如果域是多值的,那么返回值将会是一个list而不是单单一个对象,而且需要被恰当的处理。你可以将DataImprotHandler打包成一个jar包,然后再扩展Transformer和Context。
EntityProcessor(实体处理器)
默认的情况下,每个实体都会被sqlEntityProcessor处理。在系统使用RDBMS作为数据源的时候,它很适用。对于其他的数据源,例如 REST 或者不是sql的数据源,你可以选择继承org.apache.solr.handler.dataimport.Entityprocessor.这个抽象类。它被设计成从实体中一行一行的读取数据。最简单的实现自己的实体处理器的方式是继承EntityProcessorBase,然后重写方法 public Map nextRow()method。 'EntityProcessor'依赖于数据源来获取数据。数据源的返回类型对实体处理器来说是很重要的。下面是一些内嵌的实体处理器。
SqlEntityProcessor
它是默认的,数据源必须是DataSource类型的,在这里默认的情况下使用的是jdbcDataSource。
XPathEntityProcessor
处理XML类型的数据源。数据源的类型必须是DataSource类型的,这种类型的数据源有HttpDataSource和FileDatasource类型。
FileListEntityProcessor
简单的处理器,它能够从文件系统中得到文件的集合。这个系统基于一些标准,它不使用数据源,下面是实体的属性:
例子:
<dataConfig>
<dataSourcetype="FileDataSource" />
<document>
<entity name="f"processor="FileListEntityProcessor" fileName=".*xml"newerThan="'NOW-3DAYS'" recursive="true"rootEntity="false" dataSource="null">
<entity name="x"processor="XPathEntityProcessor"forEach="/the/record/xpath" url="${f.fileAbsolutePath}">
<fieldcolumn="full_name" xpath="/field/xpath"/>
</entity>
</entity>
<document>
<dataConfig>
千万要注意rootEntiry这个属性,由这个处理器所产生的域有fileAbsolutePath,fileSize,fileLastModified,fileName.
CachedSqlEntityProcessor
应该说,这是SqlEntityProcessor的一个扩展,这个处理器通过缓存一些行,来减少数据库查询。它几乎对根实体没有用,因为这个实体中只有一个sql语句被执行了。
Example 1.
<entityname="x" query="select * from x">
<entity name="y"query="select * from y where xid=${x.id}"processor="CachedSqlEntityProcessor">
</entity>
<entity>
这个例子的用法跟下面的是一样的,一个查询被执行完,它的结果被存储起来,下次这个查询再被执行的的时候,它将会从缓存中取出结果并返回。
Example 2:
<entityname="x" query="select * from x">
<entity name="y"query="select * from y"processor="CachedSqlEntityProcessor" where="xid=x.id">
</entity>
<entity>
这个例子跟前一个的区别在于属性‘where’。这个例子中,查询语句将从表中取回所有的数据,并把他们都放在缓存中。其中的关键就在域属性‘where’。缓存使用y中的xid作为键值,实体被查询的时候x.id的值就会被计算出来,我们首先会在缓存中找匹配的数据,接着返回。
在属性where中,=号之前的值是y中的列,=号之后的值是计算出来的要在缓存中查找的值。
DataSource(数据源)
org.apache.solr.handler.dataimport.DataSource 能被继承。
public abstractclass DataSource {
/**
* Initializes the DataSource with theContext
and
* initialization properties.
*
* This is invoked by the DataImporter
after creating an
* instance of this class.
*
* @param context
* @param initProps
*/
public abstract void init(Context context,Properties initProps);
/**
* Get records for the given query.The returntype depends on the
* implementation .
*
* @param query The query string. It can be aSQL for JdbcDataSource or a URL
* for HttpDataSource or a filelocation for FileDataSource or a custom
* format for your own customDataSource.
* @return Depends on the implementation. Forinstance JdbcDataSource returns
* an Iterator>
*/
public abstract T getData(String query);
/**
* Cleans up resources of this DataSourceafter use.
*/
public abstract void close();
}
它必须在数据源的定义部分被配置。
<dataSourcetype="com.foo.FooDataSource" prop1="hello"/>
JdbcdataSource
这个是默认的,它的声明如下:
public classJdbcDataSource extends DataSource>>
它可以一条一条的遍历数据库,每一行数据被当作一个Map。
HttpDataSource
XPathEntityProcessor使用这个数据源 .它的声明如下:
public classHttpDataSource extends DataSource
FileDataSource
这个很像HttpDataSource .它的声明如下:
public classFileDataSource extends DataSource
The attributesare:
Boosting , Skipping documents(提高文档的得分,或者跳过文档)
我们还可以在运行的时候提高一个文档的得分,或者跳过某一个特定的文档。
可以通过自定义转化器,增加一个属性,并将它设为true,这样就可以跳过这个文档了。可以通过,增加一个属性docBoost ,属性是文档的评分的这种方式给文档打分。Write a custom Transformer to add avalue $skipDoc with a value'true' to skip that document. To boost a document with a given value add $docBoost with the boostvalue
在 solrconfig.xml中增加数据源
我们也可以在solrconfig.xml中配置数据源,属性是一样的,只是方式稍微有点不同。
<requestHandler name="/dataimport"class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">/home/username/data-config.xml</str>
<lst name="datasource">
<strname="driver">com.mysql.jdbc.Driver</str>
<strname="url">jdbc:mysql://localhost/dbname</str>
<strname="user">db_username</str>
<str name="password">db_password</str>
</lst>
</lst>
</requestHandler>
结构图
下面的这个图显示了一般的配置文件的逻辑流程。
上面的这个图表达了这样的一个意思:一共有三个数据源,两个关系数据库的数据源,和一个http/xml的数据源。
jdbc1 和jdbc2是JdbcDataSource,它配置在solrconfig.xml文件中。
域声明
域的声明,能够帮助我们通过提供一些额外的信息得到那些不能自动获取到的值。它依赖于结果集中的列。在dataConfig里面配置的域,一般情况下应该跟schema配置的一样。它应该自动继承schema.xml中的所有的域。但是,你不能增加一些额外的域。 那么,什么时候增加域声明呢?
关于行(row)和多值域
行在DataimportHandler中的表现形式是一个Map。在这个map里面,key是域的名字,value可以任何一个合法的solr类型。value也能够是合法的solr类型的聚集(这将会映射到一个多值域)。如果数据源是RDBMS的话,一般是不会产生多值域的。当然我们可以通过加一个子实体的方式来产生多值域。这里子实体返回的多个域,相当于父实体的一个多值域。如果数据源是xml的话,产生多值域是一件相当简单的事情。
变量
变量是指最终代替那些占位符的值。这是一个多级的map,每一个命名空间都是一个map,命名空间使用.分隔。例如占位符 ${item.ID},'item'是一个命名空间(也是一个map),ID是这个命名空间下的一个值。我们很容易推导出占位符 ${item.x.ID} 这里x是另外一个map。变量的值能够从Context中获得,也可以在RDMS的query属性中或者http数据源的url属性中使用类似${}的占位符获得。
使用函数来自定义query和url的格式
命名空间这个概念在这里也是相当的有用的。用户可能想要传一个经过计算的值给 query或者url,比如这里有一个Data类型的数据,但是你的数据源只支持另外一种格式的数据源。我们提供了一些函数,或许它们能够帮你完成一些事情。
访问请求参数
我们可以使用'request'命名空间来访问传递给http请求的参数。例如'${dataimporter.request.command}' 将会返回被执行的命令。任何参数都可以通过这种方式得到。
交互式的开发模式Interactive Development Mode
这是一个很酷的,并且功能强大的工具。它能够帮助你通过图形界面来建立一个dataconfig.xml文档。你可以通过http://host:port/solr/admin/dataimport.jsp 来访问它。以下是它的特性:
屏幕快照
哪里可以找到它?
DataimportHandler是solr的新加的特性。
在Solr JIRA.的 SOLR-469 你可以查看到有关DataImporthandler的一些开发讨论。
第三部分:SOLR的db-data-config.xml高级进阶(处理CLOB和BLOB)
我们在使用solr处理数据库时,很多时候需要处理一下数据库中的CLOB、BLOB字段,对于这两种字段如何处理,下面以代码的形式给大家演示,(不用写Java代码啊)
1)定义数据源
<dataSource name="ora" driver="oracle.jdbc.OracleDriver"url="...." />
<datasourcename="ds-BlobField" type="FieldStreamDataSource" />
2.)写一个blob字段处理
<entity dataSource="ora" name="meta"query="select id, filename,content, bytes from documents"transformer="ClobTransformer">
<field column="ID"name="id" />
<field column="FILENAME"name="filename" />
<field column="CONTENT"name="CONTENT" clob="true" />
<entity dataSource="ds-BlobField"processor="TikaEntityProcessor" url="FILE_CONTENT"
dataField="ATTACH.FILE_CONTENT">
<field column="text" name="FJ_FILE_CONTENT"/><!--全局搜索 -->
<field column="Author" name="FJ_FILE_AUTHOR"meta="true" />
</entity>
</entity>
这里简单介绍一下,上述蓝色字体是处理clob必须的,红色字体是处理blob必须的。
还是比较简单的吧。如果你还没看明白,,我也没辙了。哦,这里需要说明一下,使用上述代码需要依赖几个jar包:
tika-app-0.9.jar(巨大20M,不过非常好用,对于PDF、Excel、Word、PPT、RTF、TAR、ZIP 等等吧,好多自己查吧。)
http://apache.etoak.com//pdfbox/1.6.0/pdfbox-app-1.6.0.jar
activation-1.1.jar
mail-1.4.1.jar
缺少了就跑不起来了,;)
文章转自:http://blog.csdn.net/xzknet/article/details/6710753