1.掌握SolrJ的使用。
2.掌握索引API
3.掌握结构化数据导入DIHhtml
SolrJ是什么?java
Solr提供的用于JAVA应用中访问solr服务API的客户端jar。在咱们的应用中引入solrj:mysql
<dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>7.3.0</version> </dependency>
SolrJ的核心APIweb
SolrClient、SolrRequest、SolrResponsespring
SolrClient 的子类sql
HttpSolrClient – 与指定的一个solr节点通讯的客户端
LBHttpSolrClient –负载均衡地访问一组节点的客户端
CloudSolrClient – 访问solrCloud的客户端
ConcurrentUpdateSolrClient –并发更新索引用的客户端数据库
建立客户端时通用的配置选项apache
Base URL
Timeoutsjson
建立客户端时通用的配置选项服务器
Base URL 基础URL
http://hostname:8983/solr/core1 http://hostname:8983/solr
Timeouts
final String solrUrl = "http://localhost:8983/solr"; return new HttpSolrClient.Builder(solrUrl) .withConnectionTimeout(10000) .withSocketTimeout(60000) .build();
用SolrJ索引文档
final SolrClient client = getSolrClient(); final SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", UUID.randomUUID().toString()); doc.addField("name", "Amazon Kindle Paperwhite"); final UpdateResponse updateResponse = client.add("techproducts", doc); // Indexed documents must be committed client.commit("techproducts");
用SolrJ查询
final SolrClient client = getSolrClient(); final Map<String, String> queryParamMap = new HashMap<String, String>(); queryParamMap.put("q", "*:*"); queryParamMap.put("fl", "id, name"); queryParamMap.put("sort", "id asc"); MapSolrParams queryParams = new MapSolrParams(queryParamMap); final QueryResponse response = client.query("techproducts", queryParams); final SolrDocumentList documents = response.getResults(); print("Found " + documents.getNumFound() + " documents"); for(SolrDocument document : documents) { final String id = (String) document.getFirstValue("id"); final String name = (String) document.getFirstValue("name"); print("id: " + id + "; name: " + name); }
Java 对象绑定
public static class TechProduct { @Field public String id; @Field public String name; public TechProduct(String id, String name) { this.id = id; this.name = name; } public TechProduct() {} }
索引
final SolrClient client = getSolrClient(); final TechProduct kindle = new TechProduct("kindle-id-4", "Amazon Kindle Paperwhite"); final UpdateResponse response = client.addBean("techproducts", kindle); client.commit("techproducts");
查询
final SolrClient client = getSolrClient(); final SolrQuery query = new SolrQuery("*:*"); query.addField("id"); query.addField("name"); query.setSort("id", ORDER.asc); final QueryResponse response = client.query("techproducts", query); final List<TechProduct> products = response.getBeans(TechProduct.class);
SolrClient的API
SolrRequest 的API
method path queryParams 响应解析器 是否使用V2版API 认证信息
SolrRequest 的子类
SolrResponse 的API
SolrResponse 的子类
public class SolrJClientDemo { // baseSolrUrl 示例 private static String baseSolrUrl = "http://localhost:8983/solr/"; private static String baseSolrUrlWithCollection = "http://localhost:8983/solr/techproducts"; /** * HttpSolrClient:与一个solr Server 经过http进行通讯 */ public static SolrClient getHttpSolrClient(String baseSolrUrl) { return new HttpSolrClient.Builder(baseSolrUrl) .withConnectionTimeout(1000).withSocketTimeout(6000).build(); } public static SolrClient getHttpSolrClient() { return new HttpSolrClient.Builder(baseSolrUrl) .withConnectionTimeout(1000).withSocketTimeout(6000).build(); } /** * LBHttpSolrClient: 负载均衡的httpSolrClient <br> * 负载均衡方式: 轮询给定的多个solr server url。 * 当某个url不通时,url地址会从活跃列表移到死亡列表中,用下一个地址再次发送请求。<br> * 对于死亡列表中的url地址,会按期(默认每隔1分钟,可设置)去检测是否变活了,再加入到活跃列表中。 <br> * 注意: <br> * 一、不可用于主从结构master/slave 的索引场景,由于主从结构必须经过主节点来更新。 <br> * 二、对于SolrCloud(leader/replica),使用CloudSolrClient更好。 * 在solrCloud中可用它来进行索引更新,solrCloud中的节点会将请求转发到对应的leader。 */ public static SolrClient getLBHttpSolrClient(String... solrUrls) { return new LBHttpSolrClient.Builder().withBaseSolrUrls(solrUrls) .build(); } private static String baseSolrUrl2 = "http://localhost:7001/solr/"; public static SolrClient getLBHttpSolrClient() { return new LBHttpSolrClient.Builder() .withBaseSolrUrls(baseSolrUrl, baseSolrUrl2).build(); } /** * 访问SolrCloud集群用CloudSolrClient<br> * CloudSolrClient 实例经过访问zookeeper获得集群中集合的节点列表,<br> * 而后经过LBHttpSolrClient来负载均衡地发送请求。<br> * 注意:这个类默认文档的惟一键字段为“id”,若是不是的,经过 setIdField(String)方法指定。 */ public static SolrClient getCloudSolrClient(List<String> zkHosts, Optional<String> zkChroot) { return new CloudSolrClient.Builder(zkHosts, zkChroot).build(); } private static String zkServerUrl = "localhost:9983"; public static SolrClient getCloudSolrClient() { List<String> zkHosts = new ArrayList<String>(); zkHosts.add(zkServerUrl); Optional<String> zkChroot = Optional.empty(); return new CloudSolrClient.Builder(zkHosts, zkChroot).build(); } public static void main(String[] args) throws Exception { // HttpSolrClient 示例: SolrClient client = SolrJClientDemo.getHttpSolrClient(); SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", UUID.randomUUID().toString()); doc.addField("name", "HttpSolrClient"); UpdateResponse updateResponse = client.add("techproducts", doc); // 记得要提交 client.commit("techproducts"); System.out.println("------------ HttpSolrClient ------------"); System.out.println("add doc:" + doc); System.out.println("response: " + updateResponse.getResponse()); client.close(); // LBHttpSolrClient 示例 client = SolrJClientDemo.getLBHttpSolrClient(); doc.clear(); doc.addField("id", UUID.randomUUID().toString()); doc.addField("name", "LBHttpSolrClient"); updateResponse = client.add("techproducts", doc); // 记得要提交 client.commit("techproducts"); System.out.println("------------ LBHttpSolrClient ------------"); System.out.println("add doc:" + doc); System.out.println("response: " + updateResponse.getResponse()); client.close(); // CloudSolrClient 示例 client = SolrJClientDemo.getCloudSolrClient(); doc.clear(); doc.addField("id", UUID.randomUUID().toString()); doc.addField("name", "CloudSolrClient"); updateResponse = client.add("techproducts", doc); // 记得要提交 client.commit("techproducts"); System.out.println("------------ CloudSolrClient ------------"); System.out.println("add doc:" + doc); System.out.println("response: " + updateResponse.getResponse()); client.close(); } }
Solr提供的数据提交方式简介
Solr中数据提交进行索引都是经过http请求,针对不一样的数据源solr提供了几种方式来方便提交数据。
1.基于Apache Tika 的 solr cell(Solr Content Extraction Library ),来提取上传文件内容进行索引。
2.应用中经过Index handler(即 index API)来提交数据。
3.经过Data Import Handler 来提交结构化数据源的数据
Post工具:提交服务器上的xml、json、csv数据文件及pdf、excel等富文本文件。
Index handler 是什么?
Index handler 索引处理器,是一种Request handler 请求处理器。
solr对外提供http服务,每类服务在solr中都有对应的request handler来接收处理,solr中提供了默认的处理器实现,若有须要咱们也可提供咱们的扩展实现,并在conf/solrconfig.xml中进行配置。
在 conf/solrconfig.xml中,requestHandler的配置就像咱们在web.xml中配置servlet-mapping(或spring mvc 中配置controller 的requestMap)同样:配置该集合/内核下某个请求地址的处理类。
Solrconfig中经过updateHandler元素配置了一个统一的更新请求处理器支持XML、CSV、JSON和javabin更新请求(映射地址为/update),它根据请求提交内容流的内容类型Content-Type将其委托给适当的ContentStreamLoader来解析内容,再进行索引更新。
配置一个requestHandler示例
<requestHandler name=“/update" class="solr.UpdateRequestHandler" />
Xml 格式数据索引更新
请求头中设置 Content-type: application/xml or Content-type: text/xml
添加、替换文档
<add> 操做,支持两个可选属性: commitWithin:限定在多少毫秒内完成 overwrite:指定当惟一键已存在时是否覆盖,默认true。
<add> <doc> <field name="authors">Patrick Eagar</field> <field name="subject">Sports</field> <field name="dd">796.35</field> <field name="numpages">128</field> <field name="desc"></field> <field name="price">12.40</field> <field name="title">Summer of the all-rounder</field> <field name="isbn">0002166313</field> <field name="yearpub">1982</field> <field name="publisher">Collins</field> </doc> <doc> ... </doc> </add>
删除文档
<delete> <id>0002166313</id> <id>0031745983</id> <query>subject:sport</query> <query>publisher:penguin</query> </delete>
<delete> 操做,支持两种删除方式:
一、根据惟一键
二、根据查询
组合操做
<update> <add> <doc><!-- doc 1 content --></doc> </add> <add> <doc><!-- doc 2 content --></doc> </add> <delete> <id>0002166313</id> </delete> </update>
<response> <lst name="responseHeader"> <int name="status">0</int> <int name="QTime">127</int> </lst> </response>
响应的结果: Status=0表示成功 Qtime是耗时
提交、优化、回滚操做
<commit waitSearcher="false"/> <commit waitSearcher="false" expungeDeletes="true"/> <optimize waitSearcher="false"/> <rollback/> commit、optimize 属性说明: waitSearcher:默认true,阻塞等待打开一个新的IndexSearcher并注册为主查询searcher,来让提交的改变可见。 expungeDeletes: (commit only) 默认false,合并删除文档量占比超过10%的段,合并过程当中删除这些已删除的文档。 maxSegments: (optimize only) 默认1,优化时,将段合并为最多多少个段
JSON 格式数据索引更新
请求头中设置 Content-Type: application/json or Content-Type: text/json
添加、替换一个文档
{ "id": "1", "title": "Doc 1" }
添加、替换多个文档
[ { "id": "1", "title": "Doc 1" }, { "id": "2", "title": "Doc 2" } ]
在json中指定操做
{ "add": { "doc": { "id": "DOC1", "my_field": 2.3, "my_multivalued_field": [ "aaa", "bbb" ] } }, "add": { "commitWithin": 5000, "overwrite": false, "doc": { "f1": "v1", "f1": "v2" } }, "commit": {}, "optimize": { "waitSearcher":false }, "delete": { "id":"ID" }, "delete": { "query":"QUERY" } }
根据惟一键删除的简写方式:
{ "delete":"myid" } { "delete":["id1","id2"] }
针对 JSON 格式数据提供的两个专用path
不须要在请求头中设置 Content-Type: application/json or Content-Type:text/json
Solr结构化数据导入简介
Solr支持从关系数据库、基于http的数据源(如RSS和ATOM提要)、电子邮件存储库和结构化XML 中索引内容。
从外而内,咱们来分析该如何实现数据导入。
一、咱们如何触发solr进行数据导入?
须要在solrconfig.xml配置一个requestHandler,经过发出http请求来触发,这个requestHander称为Data import Handler (DIH)
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">/path/to/my/DIHconfigfile.xml</str> </lst> </requestHandler>
一、DataImportHandler这个类所在jar并无包含在类目录中,咱们须要在solrconfig.xml中引入这个jar;
二、在solrconfig.xml中引入DataImportHandler的jar
在solrconfig.xml中找到<lib>的部分,加入下面的
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-dataimporthandler-.*\.jar" />
二、solr接到请求后,它如何知道该从何处取什么数据进行索引?
这就须要一个配置文件来定义这些了:
<requestHandler name="/dataimport" class="solr.DataImportHandler"> <lst name="defaults"> <str name="config">dih-data-config.xml</str> </lst> </requestHandler>
配置文件能够是绝对路径、或相对集合conf/的相对路径。
思考:你以为在DIH的配置文件中须要配置些什么?以从数据库导入为例
从哪取数据?
取什么数据?
如何取?
取出的数据是否须要经中间处理?
取出的数据如何与模式中的字段对应?
DIH核心概念
Datasource 数据源
Entity 实体:数据库中的表、视图
Processor 处理器:执行从实体中加载数据
Transformer 转换器:对处理器加载的数据进行转换处理
field 定义实体中的列与模式字段的对应。
字段对应规则说明:
一、自动进行名字相同配对;
二、对于名字不一样的经过显式配置 field的column、name属性指定
来看一些配置示例
在solr安装目录中的example/example-DIH/solr/ 下能够看到好几个导入示例。
一、请查看各示例的solrconfig.xml中经过<lib>导入了哪些数据导入相关的jar。
二、请查看各示例的DIH配置文件的定义。
三、请重点看看从关系数据库导入的示例。
从关系数据库导入实践
数据库:mysql 表结构以下
create table t_product( prod_id varchar(64) PRIMARY key, name varchar(200) not null, simple_intro LONGTEXT, price bigint, uptime datetime, brand_id varchar(64), last_modify_time datetime ); create table t_brand( id varchar(64) PRIMARY key, name varchar(200) not null, last_modify_time datetime ); create table t_cat( id varchar(64) PRIMARY key, name varchar(200) not null, last_modify_time datetime ); create table t_prod_cat( prod_id varchar(64), cat_id varchar(64) , last_modify_time datetime );
从关系数据库导入实践-步骤
1.拷贝mysql的驱动jar包到solr中。问,能够放到哪些目录下?
2.在myproducts集合的solrconfig.xml中配置DIH及<lib>
3.在myproducts集合的conf/目录下建立dih配置文件dih-data-config.xml
4.配置数据源
dataSource 数据源说明
单数据源
<dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/dbname" user="db_username" password="db_password"/>
多数据源
<dataSource type="JdbcDataSource" name="ds-1" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://db1-host/dbname" user="db_username" password="db_password"/> <dataSource type="JdbcDataSource" name="ds-2" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://db2-host/dbname" user="db_username" password="db_password"/>
说明:
1.name、type 是通用属性,type默认是jdbcDataSource
2.其余属性是非固定的,不一样type可有不一样的属性,可随意扩展。
配置咱们的数据源
<dataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT" user="root" password="root" />
说明: serverTimezone=GMT是为解决引入最新版mysql驱动jar报时区错误而加入的链接请求参数。
从关系数据库导入实践-步骤
5.配置document对应的实体(咱们要导入的数据)
<document> 下可包含一个或多个 <entity>数据实体
entity 数据实体通用属性说明
name (required) : 标识实体的惟一名
processor : 当数据源是非RDBMS 时,必须指定处理器。(默认是SqlEntityProcessor)
transformer : 要应用在该实体上的转换器。
dataSource : 当配置了多个数据源时,指定使用的数据源的名字。
pk : 实体的主键列名。只有在增量导入时才须要指定主键列名。和模式中的惟一键是两个不一样的东西。
rootEntity : 默认document元素的子entity是rootEntity,若是把rootEntity属性设为false值,则它的子会被做为rootEntity(依次类推)。rootEntity返回的每一行会建立一个document。
onError : (abort|skip|continue) . 当处理entity的行为document的过程当中发生异常该如何处理:默认是 abort,放弃导入。skip:跳过这个文档,continue:继续索引该文档。
preImportDeleteQuery : 在全量导入前,如须要进行索引清理cleanup,能够经过此属性指定一个清理的索引删除查询,不然用的是‘*:*’(删除全部)。只有<document>的直接子Entity设置此属性有效。
postImportDeleteQuery : 指定全量导入后须要进行索引清理的delete查询。只有<document>的直接子Entity设置此属性有效.
SqlEntityProcessor 的 entity 属性说明
query (required) : 从数据库中加载实体数据用的SQL语句。
deltaQuery : 仅用于增量导入,指定增量数据pk的查询SQL。
parentDeltaQuery : 指定增量关联父实体的pk的查询SQL。
deletedPkQuery :仅用于增量导入,被删除实体的pk查询SQL。
deltaImportQuery : (仅用于增量导入) .指定增量导入实体数据的查询SQL。若是没有指定该查询语句,solr将使用query属性指定的语句,经修改后来查询加载增量数据(这很容易出错)。在该语句中每每须要引用deltaQuery查询结果的列值,经过 ${dih.delta.<column-name>} 来引用,如:select * from tbl where id=${dih.delta.id}
从关系数据库导入实践-步骤
6.配置查询数据的SQL
7.配置获取关联表数据
entity 关系表示
实体关系(一对1、一对多、多对1、多对多),用子实体来表示,在子实体的SQL查询语句中可用${parentEntity.columnName}来引用父实体数据的列值。
一个实体可包含多个子实体。
导入API请求参数command(操做)的选项值说明
full-import : 请求启动全量导入:
http://<host>:<port>/solr/dataimport?command=full-import 返回导入正在进行中的状态信息,导入会在一个新线程中开启(可能会须要必定时间完成导入)。导入完成后,导入的开始时间将存入到conf/dataimport.properties 文件中,用于后面的增量导入。增量导入完成后也会存入增量开始的时间到这个文件,用于下一次增量导入。全量导入期间并不会阻塞solr查询。
可附加的参数有:
entity : 指定导入<document> 下的哪些实体(必须是直接子). 若是没有给定该参数,则是其下全部子entity。
clean : (default 'true') 指定是否在导入前清理索引
commit : (default ‘true’) 指定导入后是否提交
optimize : (default ‘true’ up to Solr 3.6, ‘false’ afterwards). 是否进行优化。
debug : (default ‘false’). 是否以调试模式运行,开发下使用。
调试模式下不会提交,除非明确指定commit=true.
delta-import : 请求启动增量导入http://<host>:<port>/solr/dataimport?command=delta-import . 支持的附加参数: clean, commit, optimize and debug 同 full-import。
status : 导入是异步进行的,经过下面的请求得到导入的进度、结果状态信息:http://<host>:<port>/solr/dataimport.
reload-config : 请求从新加载dih配置文件:http://<host>:<port>/solr/dataimport?command=reload-config .
abort : 请求停止当前的操做:http://<host>:<port>/solr/dataimport?command=abort .
8.配置增量导入的SQL
增量导入中的特殊变量${dataimporter.last_index_time} 说明
这个变量是上次导入的开始时间。默认存储在conf/请求处理器名.properties文件中。咱们能够在<dataConfig>下配置一个propertyWriter元素来设置它。
<propertyWriter dateFormat="yyyy-MM-dd HH:mm:ss" type="SimplePropertiesWriter" directory="data" filename="my_dih.properties" locale="en_US" />
说明:type在非cloud模式下默认是SimplePropertiesWriter,在cloud模式下默认是ZKPropertiesWriter
DIH配置文件中使用请求参数
若是你的DIH配置文件中须要使用请求时传人的参数,可用${dataimporter.request.paramname}表示引用请求参数。
配置示例:
<dataSource driver="org.hsqldb.jdbcDriver" url="${dataimporter.request.jdbcurl}" user="${dataimporter.request.jdbcuser}" password="${dataimporter.request.jdbcpassword}" />
请求传参示例:
http://localhost:8983/solr/dih/dataimport?command=full-import&jdbcurl=jdbc:hsqldb:./example-DIH/hsqldb/ex&jdbcuser=sa&jdbcpassword=secret
小结
1.Solr支持从不少种数据源导入数据
2.实现了不少的处理器及转换器
实际工做过程当中如碰到了别的数据源的导入,必定要想到solr中应该有对应的解决方案。参考地址:
http://lucene.apache.org/solr/guide/7_3/uploading-structured-data-store-data-with-the-data-import-handler.html
https://wiki.apache.org/solr/DataImportHandle
<dataConfig> <dataSource driver="org.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/example?useUnicode=true" user="sa" password="123456"/> <document> <entity name="product" query="select * from t_product" pk="prod_id" <!-- deltaQuery :增量操做时进行的查询 --> deltaQuery ="select prod_id from t_product where last_modify_time > '${dataimporter.last_index_time}'" <!-- deltaImportQuery:deltaQuery 以后进行的查询 prod_id 是entity的pk --> deltaImportQuery ="select * from t_product where prod_id ='${dih.delta.prod_id}'" > <field column="prod_id" name="prodId" /> <field column="simple_intro" name="simpleIntro" /> <!-- 对应品牌 --> <entity name="brand" pk='id' query="select name from t_brand where id='${product.brand_id}'" <!-- deltaQuery :增量操做时进行的查询 --> deltaQuery ="select id from t_brand where last_modify_time > '${dataimporter.last_index_time}'" <!-- parentDeltaQuery: 子操做更新完 父级折行的操做 --> parentDeltaQuery ="select prod_id from t_product where brand_id='#{brand.id}' " > <field name="name" column="brand" /> </entity> <!-- 商品类别 --> <entity name="productCat" query="select prod_id,cat_id from t_product_cat where prod_id='${product.prod_id}'"> <entity name="cat" query="select name from t_cat where id = '${productCat.cat_id}'"> <field column="name" name="cat" /> </entity> </entity> </entity> </document> </dataConfig>