Solr入门,
Solr入门
通过Solr实现全文搜索,如果每次通过访问数据库搜索全文,比如使用LIKE %,将会给服务器造成巨大的压力。
简介
Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。
Solr是一个全文检索服务器,只需要进行配置就可以实现全文检索服务。
安装及配置
首先版本Java 1.8/Tomcat8.5/solr 7.2.1
步骤:
1. solr下载http://mirrors.hust.edu.cn/apache/lucene/solr/7.2.1/,解压为solr-7.2.1文件夹。
2. 将. \solr-7.2.1\server\solr-webapp文件夹复制到. \apache-tomcat-8.5.29\webapps下,并重命名为solr。
3. 将.\solr-7.2.1\server\lib\ext下的所有jar包,以及.\solr-7.2.1\server\lib下以metrics开头的jar、gmetric4j-1.0.7.jar复制到. \apache-tomcat-8.5.29\webapps\solr\WEB-INF\lib下。
4. 在. \apache-tomcat-8.5.29\webapps\solr\WEB-INF中,新建classes文件夹,将.\solr-7.2.1\server\resources下的log4j.properties文件拷贝到里面。
5. 创建一个D:\solr_home 的目录,并将.\solr-7.2.1\server\solr目录复制D:\solr_home目录下
6. 打开Tomcat/webapps/solr/WEB-INF下的web.xml,增加如下配置内容(初始状态下该内容是被注释掉的):
<env-entry>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>D:/solr_home</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
将<env-entry-value>中的内容改成你的solr_home路径,这里是D:/solr_home。这项配置,要是建立tomcat与solr之间的关系的,的作用是让tomcat找到你所配置的solr目录。
以上操作配置完成后,启动Tomcat,如果出现报错,查看一下tomcat/logs下的日志文件,查看报错信息,我当时出现java.lang.NoClassDefFoundError:Failed to initialize Apache Solr: Could not find necessary SLF4j logging jars.这个错误,后来添加依赖slf4j-simple-1.7.25.jar后成功。
使用
添加新Core
1、在solr_home目录下创建新core的文件夹,如new_core,然后进入new_core再创建conf文件夹。
2、将.\solr-7.2.1\server\solr\configsets\_default\conf下的所有文件复制到刚刚创建的conf文件夹中,也就是将默认的配置文件复制过去。
3、打开solr,在core admin中点击Add Core,name和instanceDir都改为刚刚创建的新core的名字。
创建成功。
配置中文分词
1. 百度搜索下载ikanalyzer-solr6.5.zip,解压。
2. 将ik-analyzer-solr5-5.x.jar复制到.\ apache-tomcat-8.5.29\webapps\solr\WEB-INF\lib下。
3. 将KAnalyzer.cfg.xml、ext.dic(扩展词典)、stopword.dic(停用词典)复制到.\ apache-tomcat-8.5.29\ebapps\solr\WEB-INF\classes中。
4. 进入.\solr_home\(core_name)\conf下,就是core文件夹中修改managed-schema内容加上
<fieldType name="text_ik"class="solr.TextField">
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
保存后重启服务器,按照如下方法查看是否配置成功。
配置业务(搜索)字段
业务字段判断标准:
1、在搜索时是否需要在此字段上进行搜索。例如:新闻标题、新闻内容、新闻来源等。
2、后续的业务是否需要用到此字段。例如:新闻id。
进入.\solr_home\(core_name)\conf下,就是core文件夹中修改managed-schema,在刚刚添加的fieldType标签后面添加下面的内容
<fieldType name="text_ik"class="solr.TextField">
<analyzerclass="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
<field name="news_title" type="text_ik" indexed="true"stored="true"/>
<field name="content" type="text_ik" indexed="true"stored="false"/>
<field name="ctype" type="string" indexed="true"stored="true"/>
<field name="news_keywords" type="text_ik"indexed="true" stored="false"multiValued="true"/>
<copyField source="news_title" dest="news_keywords"/>
<copyField source="content" dest="news_keywords"/>
<copyField source="ctype" dest="news_keywords"/>
注意indexed="true"表示开启索引(当字段不需要被检索时,最好不要开启索引) stored="true"表示存储原来数据(当字段不被检索,而只是需要通过其他字段检索而获得时,要设为true) multiValued="true" 表示返回多值,如一个返回多个content,此时要在java代码中把 content设置 集合或数组类型如private String[] content;//多值,对应 multiValued="true"
重启服务器,可以看到刚刚配置的字段都在:
SolrJ基本使用
SolrJ是操作Solr的JAVA客户端,它提供了增加、修改、删除、查询Solr索引的JAVA接口。SolrJ针对 Solr提供了Rest 的HTTP接口进行了封装, SolrJ底层是通过使用httpClient中的方法来完成Solr的操作。
Maven依赖
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>7.1.0</version>
</dependency>
SolrClient是一个抽象类,下边有很多被实现的子类,HttpSolrClient是通用客户端,可以与一个Solr节点直接通信。
SolrJ客户端创建
使用Tomcat启动本地地址,默认端口8080,URL指向我们配好的Core(newcore)。
String solrUrl = "http://127.0.0.1:8080/solr/newcore";
//创建solrClient同时指定超时时间,不指定走默认配置
HttpSolrClientclient = new HttpSolrClient.Builder(solrUrl)
.withConnectionTimeout(10000)
.withSocketTimeout(60000)
.build();
//不同solr版本solrj 的创建方式有所不同
//solr4创建方式
//SolrServersolrServer = new HttpSolrServer("http://127.0.0.1:8080/solr");
//solr5创建方式,在url中指定core名称:newcore
//HttpSolrClient solrServer=newHttpSolrClient("http://127.0.0.1:8080/solr/newcore");
//solr7创建方式,在url中指定core名称:newcore
HttpSolrClient solrServer = new HttpSolrClient.Builder(
"http://127.0.0.1:8080/solr/newcore").build();
创建索引
创建文档,添加Field(k-v),使用SolrClient的add()方法,然后commit()。可以批量添加然后commit。
solr也是nosql的一种。更新索引则直接在原有基础上添加进行覆盖。
/**
* 用solrJ创建索引
* 添加索引使用SolrClient的add()方法
*/
@Test
public void solrAdd() throws Exception {
//创建文档doc
SolrInputDocument doc = new SolrInputDocument();
//添加内容
doc.addField("id", "13002");
doc.addField("news_title", "新闻标题2");
doc.addField("content", "新闻内容2");
doc.addField("ctype", "时政");
//添加到client
UpdateResponse updateResponse = client.add(doc);
System.out.println(updateResponse.getElapsedTime());
//索引文档必须commit
client.commit();
}
查询
/**
* 查询
* SolrClient有很多quary() 查询方法用于从solr中获取结果,这些方法都需要一个SolrParams 类型的参数,
* 该对象可以封装任意的查询参数。和每个方法输出 QueryResponse 一个包装器,可以用来访问结果文档和其他相关的元数据。
*/
@Test
public void querySolr() throws Exception {
//封装查询参数
Map<String, String> queryParamMap = new HashMap<>();
queryParamMap.put("q", "*:*");
//添加到SolrParams对象
MapSolrParams queryParams = new MapSolrParams(queryParamMap);
//执行查询返回QueryResponse
QueryResponse response = client.query(queryParams);
//获取doc文档
SolrDocumentList documents =response.getResults();
//内容遍历
for (SolrDocument doc : documents) {
System.out.println("id:" + doc.get("id")
+ "\tnews_title:" + doc.get("news_title")
+ "\tcontent:" + doc.get("content")
+ "\tctype:" + doc.get("ctype"));
}
client.close();
}
SolrParams 有一个 SolrQuery 子类,它提供了一些方法极大地简化了查询操作。
/**
* 使用 SolrParams 的子类 SolrQuery,它提供了一些方便的方法,极大地简化了查询操作。
*/
@Test
public void querySolr2() throws Exception {
//封装查询参数
SolrQuery query = new SolrQuery("*:*");
//添加需要回显得内容
query.addField("id");
query.addField("news_title");
query.setRows(20);//设置每页显示多少条
//执行查询返回QueryResponse
QueryResponse response = client.query(query);
//获取doc文档
SolrDocumentList documents =response.getResults();
//内容遍历
for (SolrDocument doc : documents) {
System.out.println("id:" + doc.get("id")
+ "\tnews_title:" + doc.get("news_title")
+ "\tcontent:" + doc.get("content")
+ "\tctype:" + doc.get("ctype"));//因为没添加回显内容查询为null
}
client.close();
}
删除索引
/** * 单个id的删除索引 */ @Test public void solrDelete() throws Exception { //通过id删除 client.deleteById("13001"); //提交 client.commit(); //关闭资源 client.close(); } /** * 多个id的list集合 删除索引 */ @Test public void solrDeleteList() throws Exception { ArrayList<String> ids = new ArrayList<>(); ids.add("13001"); ids.add("13002"); client.deleteById(ids); //提交 client.commit(); //关闭资源 client.close(); } /** * 根据查询删除 */ @Test public void solrDeleteQuery() throws Exception { client.deleteByQuery("id:100");//删除满足查询条件的信息 //提交 client.commit(); //关闭资源 client.close(); }
SolrJ绑定Java对象
SolrJ支持通过@Field注解隐式转换文档与任何类。每个实例变量在Java对象可以映射到一个相应的Solr字段中。
定义一个Article的pojo。其中articleTitle和articleContent字段使用@Field注解,而author没有使用则不能映射到集合的索引中。
public class Article { @Field("articleTitle") private String articleTitle; @Field("articleContent") private String articleContent; private String author; public Article() { System.out.println("无参构造器"); } //getter、setter略 ... }
创建索引
/** * Java对象绑定,通过对象创建索引 */ @Test public void addBean() throws Exception{ //创建对象 Article article = new Article("北京的雪","北京的雪好美!","小明"); Article article2 = new Article("南京的雪","南京不下雪!","小红"); //添加对象 UpdateResponse response = client.addBean(article); client.addBean(article2); //提交操作 client.commit(); //关闭资源 client.close(); }
结果如下:
通过对象查询索引
这里会报一个错org.apache.solr.client.solrj.beans.BindingException:Could not instantiate object of class com.seu.fn.bean.Article。首先,映射的pojo的类需要有无参构造函数(在该构造器中设置输出,会看到调用的结果)。然后在managed-schema文件中,配置对应字段的类型。由于之前插入,会自动生成text_general的类型,需要修改成text_ik类型,即我们之前配置的支持中文分词的类型,同时还可以设置indexed="true"stored="true"。然后再运行,输出正常。
/** * Java对象绑定,通过对象查询索引 */ @Test public void queryBean() throws Exception{ //创建SolrQuery对象 SolrQuery query = new SolrQuery("*:*"); //添加回显的内容 query.addField("articleTitle"); query.addField("articleContent"); query.setRows(200);//设置每页显示多少条 //执行查询返回QueryResponse QueryResponse response = client.query(query); //获取doc文档 List<Article> articles = response.getBeans(Article.class); //遍历 for (Article article : articles) { System.out.println("title:"+article.getArticleTitle() +"\tauthor:"+article.getAuthor() +"\tcontent:"+article.getArticleContent()); } //关闭资源 client.close(); }
整合Spring
编写applicationContext-solr.xml
添加:
<bean id="httpSolrClient"class="org.apache.solr.client.solrj.impl.HttpSolrClient">
<constructor-arg name="builder" value="${SOLR.SERVER.URL}"></constructor-arg>
</bean>
solr.properties文件中添加:
SOLR.SERVER.URL=http://127.0.0.1:8080/solr/corename
然后就可以使用@Autowired进行使用HttpSolrClient对象了。
从数据库导入数据Dataimport
上面提到的都是一些基本用法,但是面对从数据库中导入大量的数据时,如果还傻傻的一个一个addField和commit(我尝试过),效率就太慢了。。。
随即百度一波,学习记录下。
步骤:
1、修改.\solr_home\copyrightCore\conf\solrconfig.xml文件,添加
<requestHandler name="/dataimport"class="org.apache.solr.handler.dataimport.DataImportHandler">
<lstname="defaults">
<strname="config">db-data-config.xml</str>
</lst>
</requestHandler>
2、在conf目录下创建上面标红的文件db-data-config.xml,并添加:
<?xml version="1.0"encoding="UTF-8" ?>
<dataConfig>
<dataSourcedriver="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/copyright_test"user="root" password="root" />
<document>
<entity name="news_info" query="selectnews_id,title,author,editor,source,ctype,keywords from news_info">
<fieldcolumn="news_id" name="newsId"/>
<fieldcolumn="title" name="title"/>
<fieldcolumn="author" name="author"/>
<fieldcolumn="editor" name="editor"/>
<fieldcolumn="source" name="source"/>
<field column="ctype"name="ctype"/>
<fieldcolumn="keywords" name="keywords"/>
</entity>
</document>
</dataConfig>
其中entity name=“对应的数据库表名”,后面的query就是sql语句,field column一定要对应数据库的列名,不然导入不进去,后面的name为managed-shchema中配置的字段名,即在solr中显示的字段。
3、两个jar包是必须的,当然其他jar包在上面配置中已经配置过。
mysql-connector-java-5.1.7-bin.jar(MySQL的jar包肯定是必须的)
solr-dataimporthandler-7.2.1.jar(看名字大概知道是dataimport相关的,在.\solr-7.2.1\dist目录下)
4、配置managed-shchema文件中的字段名,名字要和数据库一一对应,当然不想显示的字段可以不配置。配置如下(部分略):
<field name="title"type="text_ik" indexed="true" stored="true"/>
<field name="news_id"type="string" indexed="true" stored="true"multiValued="true"/>
。。。等。注意,如果出现报错说xml格式的问题,最好复制文件中默认配置的格式然后进行修改。
5、启动solr,进入控制界面。
进入Dataimport页面,然后选择Auto-Refresh Status,点击Execute。
导入成功。