1. Solr的安装
略。(注意安装jdk)
2. Solr客户端界面介绍
solr-7.7.3目录结构介绍
- bin:该目录下存放了Solr的工具命令。
- contrib:该目录下存放了Solr所依赖的第三方JAR包。
- dist:该目录下存放了Solr本身的JAR包。
- docs:该目录下存放了Solr的文档。
- example:该目录下存放了Solr的各种实例。
- server:该目录下的solr目录存放了一系列的索引库。
启动Solr
[root@hadoop1 solr-7.7.3]# cd bin
[root@hadoop1 bin]# ./solr start -force
打开Solr客户端
- Dashboard:显示了该Solr实例开始启动运行的时间,版本,系统资源,jvm等信息。
- Logging: 日志,显示Solr运行出现的异常或错误。
- Core Admin: 在这里可以添加Solr Core的实例,也就是创建一个索引库,类似数据库,用来存放数据。所以如果要使用Solr必须创建一个索引库才能使用。主要有Add Core(添加核心),Unload(卸载核心),Rename(重命名核心),Reload(重新加载核心),Optimize(优化索引库)。
- Java Properties: 可查看到Java相关的一些属性信息。
- Thread Dump:显示Solr Server中当前活跃线程信息,同时也可以跟踪线程运行栈信息。
创建索引库
一般来说,在真实项目环境下,数据库是要跟Solr中的索引库结合在一起的,数据库负责增删改,而索引库负责查询,所以,我们要利用Solr客户端来创建我们的索引库,那怎么创建呢?如下:
Add Core,创建核心,可以理解为创建表,点击,如下:
- name为自定义名字,也就是到时候如果添加成功,Solr客户端左下角的Core Selector下拉框就会出现name指定的名字,到时可以选择。建议和下面的instanceDir保持一致。
- instanceDir为磁盘上文件夹的名称。
- dataDir为默认的数据存储目录。
- config为配置文件,里面的solrconfig.xml的位置在db_cht/conf/solrconfig.xml。
- schema为db_cht下的conf下的schema文件(schema.xml)。
不过以上这样直接点击Add Core会报错的,但是,虽然报错,在/usr/local/solr-7.7.3/server/solr目录下就会生成一个叫db_cht的空文件夹(为什么文件夹叫db_cht,因为instanceDir已经指定了),进去里面啥也没有,在这里需要复制一点东西来,执行下面命令,如下:
cp -r ../configsets/sample_techproducts_configs/* ./
这样就能创建成功了,那么在db_cht目录下除了我们刚刚复制过来的conf文件夹,还有core.properties文件和data文件夹。我们可以打开core.properties看看,如下:
name=db_cht
config=solrconfig.xml
schema=schema.xml
dataDir=data
或者我们采用命令来创建,首先,进入solr-7.7.3的bin目录,如下:
[root@hadoop1 bin]# pwd
/usr/local/solr-7.7.3/bin
[root@hadoop1 bin]# ./solr create_core -c db1_core -force
WARNING: Using _default configset with data driven schema functionality. NOT RECOMMENDED for production use.
To turn off: bin/solr config -c db1_core -p 8983 -action set-user-property -property update.autoCreateFields -value false
Created new core 'db1_core'
这次再打开管理界面,就会出现下面的两个索引库,如下:
Core选择器
- Overview:主要显示当前库的一个状况。
- Analysis:查询分析器。如下:
如果左边高亮,意味着用户输入的虽然是my number这个英文单词,但是是会把phone number这条记录搜索出来的,即使用户没有搜索phone这个英文单词。
再说下分词器,分词器很好懂吧,它默认是英文分词器,但也有中文分词器,中文的后面再说,所谓的分词器就是对一句话分成各个词语,比如苹果手机会被分成三个词语,分别是苹果,手机,苹果手机。当用户在搜索框里输入苹果手机的时候,搜索结果可不单单只出现苹果手机哦,可能水果类的苹果也出来了,这就是分词的作用。在如上图,也就是那个下拉框是选择分词策略的,如果是中文的,那就选中文分词器,只不过默认是英文的,暂时没有中文分词器,没关系,后面再配,反正下拉框里列出来的内容就是各个分词器策略,而这些分词器策略都在一个文件里可以看出来,该文件就在/usr/local/solr-7.7.3/server/solr/索引库名称/conf/managed-schema文件里,里面的fieldType标签就是了。
- Schema: 既然索引库都创建出来了,或者你也可以理解就是创建了一张表,那么表是不是得有字段呀,字段英文名是不是就叫Field呀,所以,如下:
以商品信息为例,是不是有这几种字段,商品标题,商品描述,商品价格等,分别对应commodityTitle,message,price。话不多说,我们创建一下吧,如下:
添加完之后,在下面的下拉框是可以看到我们刚刚添加的字段的,那我要说下,下拉框里的所有数据来源是哪里?没错,就来自managed-schema文件里,打开该文件,就有如下标签,如下:
<field name="commodityTitle" type="text_general" uninvertible="true" docValues="false" indexed="true" stored="true"/>
剩下的字段一样的操作,不过像id,price这两字段,Solr已经默认帮我们提供了(说白了就是managed-chema文件里已经存在了name等于id和name等于price的field标签),那我们就关注剩下的message咯,如下:
那Add Dynamic Field,添加动态字段,就是managed-schema文件里的dynamicField标签,看它的name就知道了。
下一个是Add Copy Field,添加复制字段(或叫合成字段),也就是说,我们可以把商品标题和商品描述这两个字段合成一个新的字段,叫xxxKeyWorld,随便。那为什么要合成一个新的字段?就是说,如果我们在查询某件商品信息的时候,肯定是根据某一个字段来查询的,而这字段不单单是commodityTitle字段,或者是message字段,应该是两字段合成,说白了,就是我们查询商品信息,是根据commodityTitle字段+message字段查出来的。换句话说,我们在做匹配的时候,不单单可以根据commodityTitle匹配,也可以根据message做匹配。而这合成后的字段xxxKeyWorld是一个数组。
- Documents: 添加数据,更新数据,删除数据的。如下:
向索引库添加数据(Json版):
向索引库添加数据(xml版):
删除id为1的记录:
删除所有记录,就是*:*。下一个是更新,那么就像添加一样,只不过重点在id上,如果你要添加的这条记录,id在索引库刚好存在,那么就会把原有记录覆盖掉,这就是更新。
- Query: 模拟查询条件。
说明一点,如上commodityTitle字段只所以可以根据它作为查询条件,那是因为commodityTitle的index为true,如果为false,那是不能作为查询条件的。
如果是多条件,那么可以这样写,字段1:值1 AND/OR 字段2:值2。
如上fq代表过滤查询。看,一共有两个条件,代表我要查询commodityTitle有华为的以及message带有色彩字眼的记录。
fq是在q查询符合结果中同时是fq查询符合的,例如,请求fq是一个数组:
如果fq里写的是字段:[1 TO 10],代表过滤查询1到10的记录,该字段可以是价格之类的,反正就是数字型的。当然,如果要表示10以上的就是[10 TO *]。
fq的另一个语法,表示并且的关系,比如commodityTitle:华为,苹果,表示要把commodityTitle中有华为的和有苹果的都查出来。同样也支持字段1:值1 AND/OR 字段2:值2。
sort为排序。
下面的start,rows为分页。
fl为指定返回哪些字段内容,比如你写的是commodityTitle,price。那么就意味着你在查询的时候只会把commodityTitle和price查询出来,其它的如message不会查询出来。
df表示默认字段,譬如你写的是commodityTitle,那么我们在q那里就不用写的那么完整了,比如commodityTitle:华为,就可以直接写华为这两个字了,因为你如果不写以哪个字段作为条件,默认就是以你df指定的那个。
如上图是做高亮设置的,hl.fl那里表示你要对谁做高亮,下面就是高亮后是什么颜色的。
IK分词器的使用
接下来是配置中文分词器。往下看吧!!!
下载地址:http://files.cnblogs.com/files/zhangweizhong/ikanalyzer-solr5.zip
解压文件及说明:
-
ext.dic:自定义词语,如沙雕,沙雕在汉语里面不是一个词,它只是一个网络用语,我们可以配置到这里面,让它成为一个词。
-
stopword.dic:停止分词,或者说对哪些不做分词处理。
-
IKAnalyzer.cfg.xml:配置IK的配置文件,不用改。
1. 修改managed-sahma,加上如下配置:
<!-- China -->
<fieldType name="text_cn" class="solr.TextField" positionIncrementGap="10">
<analyzer type="index">
<tokenizer class="org.apache.lucene.analysis.ik.IKTokenizerFactory" useSmart="true"/>
</analyzer>
<analyzer type="query">
<tokenizer class="org.apache.lucene.analysis.ik.IKTokenizerFactory" useSmart="false"/>
</analyzer>
</fieldType>
说明一下,看analyzer标签,其中的type等于index或者query是什么意思?其实是对应如下图:
然后再看,useSmart又是什么意思,如下:
跟分词的粒度相关:
- False:分词的粒度大,一句话里面分的词语少。
- True:分词的粒度细,一句话里面分的词语多。
2. 把IK的配置放到Solr:
放入jar包:准备好ik-analyzer-solr5-5.x.jar,这个我们已经下载下来了,但还要下载一个jar包,可以去maven仓库下,该jar包就是solr-analyzer-ik-5.1.0.jar,也就是总共有两个jar,有了这两个jar,就可以把这两个jar放到/usr/local/solr-7.7.3/server/solr-webapp/webapp/WEB-INF/lib目录下。
3. 放配置:
退回到上一级,也就是/usr/local/solr-7.7.3/server/solr-webapp/webapp/WEB-INF,在该目录下新建文件夹,名字叫classes,然后再把ext.dic,IKAnalyzer.cfg.xml,stopword.dic这三个文件放进去。
4. 重启Solr
[root@hadoop1 solr-7.7.3]# cd bin
[root@hadoop1 bin]# ps -ef|grep solr
root 3632 1 0 06:24 pts/0 ......
[root@hadoop1 bin]# kill -9 3632
[root@hadoop1 bin]# ./solr start -force
重新进入solr的管理界面,进入如下页面:
补充:ext.dic的说明
打开ext.dic文件,直接写上沙雕这两个字即可,只有这样,在做分词的时候,遇到沙雕才不会把沙和雕分开来,因为如果这样的话,网友在搜索沙雕时,不就搜索不到有关沙雕的视频吗?那么为了能够搜索到,我们就得把沙雕写到ext.dic文件上,毕竟它是一种网络用语,要把它当成一个词语来用。那么以后,我们再搜索沙雕的时候,就会搜索到有关沙雕的视频了。如下:
以第一个视频为例,如下:
我要说明的是,如果你不在ext.dic写上沙雕这两个字,那不好意思,ik分词器不认为沙雕是一个词语,只会把沙和雕两个字分开。
数据库导入数据到Solr
我们打开数据库Navicat,然后新建个数据库,数据库名随便你叫什么,都行,右键数据库,选择运行sql文件,因为此处,我为方便,准备导入sql文件,就不自己新建表了。我呢这有两个sql文件,也就意味着有两张表,这两张表的名字叫bless和products,该两张表数据很多,我就只说它有哪些字段好了:
bless | id,bless_content,bless_time |
products | pid,pname,catalog,catalog_name,price,number,description,picture,release_time |
DataImport导入数据:
该功能是将数据库中的数据通过sql语句方式导入到Solr索引库中。
第一步:添加jar包:
进入/usr/local/solr-7.7.3/dist下,复制solr-dataimporthandler-7.7.3.jar和solr-dataimporthandler-extras-7.7.3.jar。同时还要复制mysql的驱动包mysql-connector-java-5.1.42.jar,复制到哪?复制到/usr/local/solr-7.7.3/server/solr-webapp/webapp/WEB-INF/lib下即可。
第二步:修改solrconfig.xml
进入/usr/local/solr-7.7.3/server/solr/索引库名/conf目录下,我这里的索引库名是db1_core,也就是配置了中文分词器的那个。
进入conf下,打开solrconfig.xml,首先查询是否存在dataimport的requestHandler,如果不存在,因此需要手动添加,为了以后便于维护此文件,我们就在requestHandler起始位置,约在720行处,添加如下内容:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str>
</lst>
</requestHandler>
第三步:创建data-config.xml配置文件
注意,在当前目录下创建,也就是跟solrconfig.xml同级。
data-config.xml的作用:数据库连接相关信息,SQL以及查询结果映射对应域(字段)中。
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource type="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://192.168.1.101:3306/db1"
user="root"
password="root"/>
<document>
<entity name="products" query="select pid,pname,catalog_name,price,description,picture from products">
<field column="pid" name="id"/>
<field column="pname" name="prod_pname"/>
<field column="catalog_name" name="prod_catalog_name"/>
<field column="price" name="prod_price"/>
<field column="description" name="prod_description"/>
<field column="picture" name="prod_picture"/>
</entity>
</document>
</dataConfig>
如上field标签里,column为数据库里的字段名称,name为solr索引库里的字段名称,或者叫域名称。
第四步:分析定义域
修改同目录下的managed-schema文件,增加下面内容:
<!--prod_pname:支持分词技术查询-->
<field name="prod_pname" type="text_cn" indexed="true" stored="true" required="true"/>
<!--catalog_name: 直接相等的方式查询,不要做分词,直接精确查询-->
<field name="prod_catalog_name" type="string" indexed="true" stored="true" required="true"/>
<field name="prod_price" type="pdouble" indexed="true" stored="true" required="true"/>
<field name="prod_description" type="text_cn" indexed="true" stored="true" required="true"/>
<!--prod_picture: 不分词,也不做搜索条件-->
<field name="prod_picture" type="string" indexed="false" stored="true" required="true"/>
第五步,重启solr。
第六步,查看solr管理界面,进入核心选择器,选中DataImport选项,也就是我们在界面上添加数据(Documents)的上方。如下:
只是我这出现了问题,导入失败,因为我的solr是在linux安装的,而mysql在windows中,可能就是该原因,造成导入失败。为了不浪费时间,我这也就暂时先放着,大不了我直接向索引库添加数据也是可以的嘛,虽然麻烦点。大家可以看这个视频,我就是参考该视频的,包括我说的那两张表:<https://www.bilibili.com/video/BV1ob411T7NQ?p=7>。
这里我就自行的往索引库添加了5条数据,意思意思一下,最重要的还是后面进行增删改查的部分。如下:
3. Solrj的操作
Solrj添加数据/更新数据
到这,就要真正的用java代码来操作索引库了,而以上用solr管理控制台操作的索引库我们当做了解学习即可,下面才是王道。
solrj是操作Solr的JAVA客户端,它提供了增加,修改,删除,查询Solr索引的JAVA接口。Solrj针对Solr提供了Rest的HTTP接口进行了封装,SolrJ底层是通过使用HttpClient中的方法来完成Solr的操作的。
1. 创建项目普通的maven项目。
2. 引入maven坐标:
<properties>
<solrj.version>7.7.2</solrj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>${solrj.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
3. 创建测试类来测试连接,如下:
package com.cht.test;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
public class Test01Connection {
//声明一个连接solr的地址
public static final String SOLR_URL = "http://192.168.6.133:8983/solr/db1_core";
//声明一个连接solr的对象
private static HttpSolrClient httpSolrClient;
static {
httpSolrClient = new HttpSolrClient.Builder(SOLR_URL).build();
}
public static void main(String[] args) {
System.out.println(httpSolrClient);
}
}
如果连接成功,没报错,就说明可以来操作solr了。
4. 使用solrj向索引库添加数据:
public static void main(String[] args) throws IOException, SolrServerException {
//一个一个添加
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id",6); //不指定id值,默认是UUID
doc.addField("prod_pname","zakka杂货 情侣小鹿树脂摆件家居装饰品一对");
doc.addField("prod_catalog_name","幽默杂货");
doc.addField("prod_price",15);
doc.addField("prod_description","<TABLE id=table2 cellSpacing=5 .........");
doc.addField("prod_picture","2014031517190225.jpg");
httpSolrClient.add(doc);//或者这样,指定某个库,如果这样那么上面的地址SOLR_URL就不用具体指定是哪个索引库了。httpSolrClient.add("db1_core",doc);
httpSolrClient.commit();
httpSolrClient.close();
}
测试一下是否添加成功,如果成功,打开solr管理后台,看看添加进去没有。
下面是添加多条数据,如下:
public static void main(String[] args) throws IOException, SolrServerException {
List<SolrInputDocument> docs = new ArrayList<>();
for (int i=0;i<=5;i++){
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id",i);
doc.addField("prod_pname","魔幻星座音乐水晶球内雕音乐盒七彩渐变音乐球");
doc.addField("prod_catalog_name","幽默杂货:"+i);
doc.addField("prod_price",70);
doc.addField("prod_description","description:"+i);
doc.addField("prod_picture","2014030610151185.jpg");
docs.add(doc);
}
httpSolrClient.add(docs);
httpSolrClient.commit();
httpSolrClient.close();
}
我们还可以添加一个对象,那么我们就要创建一个实体类,如下:
package com.cht.domain;
import lombok.Data;
import org.apache.solr.client.solrj.beans.Field;
@Data
public class Products {
@Field("id")
private String pid;
@Field("prod_pname")
private String pname;
private String catalog;
@Field("prod_catalog_name")
private String catalogName;
@Field("prod_price")
private double price;
private Integer number;
@Field("prod_description")
private String description;
@Field("prod_picture")
private String picture;
}
那么添加如下:
Products products = new Products();
products.setPid("8");
products.setPname("家天下嘻哈动物魔术贴挂钩绕带无痕挂钩2个装RB205");
products.setCatalogName("幽默杂货");
products.setPrice(5.5);
products.setDescription("<TABLE id=table2 cellSpacing=5 cellPadding=5 width=700 border=0>\n...");
products.setPicture("2013112909444459_S.jpg");
UpdateResponse response = httpSolrClient.addBean(products);
httpSolrClient.commit();
httpSolrClient.close();
注意,以上添加的时候是不是设置了id,那么如果你设置的id在索引库已存在,那就是更新,所以这点要注意!!!因为你一不小心就会把原有记录覆盖掉。
Solrj删除数据
根据id删除:
httpSolrClient.deleteById("1");
httpSolrClient.commit();
httpSolrClient.close();
根据ids删除:
httpSolrClient.deleteById(Arrays.asList("1","2","3"));
httpSolrClient.commit();
httpSolrClient.close();
全部删除:
httpSolrClient.deleteByQuery("*:*");//全部删除。表示以查询作为删除条件。
httpSolrClient.commit();
httpSolrClient.close();
solrj查询数据
简单查询:
String q = "*:*";
//SolrParams是抽象类
SolrParams solrQuery = new SolrQuery(q);
QueryResponse query = httpSolrClient.query(solrQuery);
List<Products> product = query.getBeans(Products.class);
System.out.println(product.size()); //默认只查询10条记录,这点要注意
for (Products p:product){
System.out.println(p);
}
httpSolrClient.commit();
httpSolrClient.close();
SolrQuery solrQuery = new SolrQuery();
String keyWorld = "情侣"; //模拟用户在搜索框输入情侣
//判断当前用户是否对keyWorld进行赋值,如果为空,查询所有,不为空,就专门查询用户输入的值
if(StringUtils.isEmpty(keyWorld)){
solrQuery.set("q","*:*");
}else {
solrQuery.set("q","prod_pname:"+keyWorld);
}
QueryResponse query = httpSolrClient.query(solrQuery);
List<Products> product = query.getBeans(Products.class);
System.out.println(product.size());
for (Products p:product){
System.out.println(p.getPname());
}
httpSolrClient.commit();
httpSolrClient.close();
复杂查询:
public static void main(String[] args) throws IOException, SolrServerException {
SolrQuery solrQuery = new SolrQuery();
String keyWorld = "情侣"; //模拟用户在搜索框输入情侣
//判断当前用户是否对keyWorld进行赋值,如果为空,查询所有,不为空,就专门查询用户输入的值
if(StringUtils.isEmpty(keyWorld)){
solrQuery.set("q","*:*");
}else {
solrQuery.set("q","prod_pname:"+keyWorld);
}
//设置fq
String catalogName = "";
if(!StringUtils.isEmpty(catalogName)){
solrQuery.addFilterQuery("prod_catalog_name:"+catalogName);
}
//prod_price:[1 TO 5]
String price_str="1-5";//如果是1- 那么对应的就是prod_price:[1 TO *]
if(!StringUtils.isEmpty(price_str)){
String[] arrs = price_str.split("-");
if(arrs.length == 1){ //针对price_str是这种情况: 数字-
solrQuery.addFilterQuery("prod_price:["+arrs[0]+" TO *]");
}else{
String perfix = arrs[0];
if(StringUtils.isEmpty(arrs[0])){//针对price_str是这种情况: -数字
perfix = "*";
}
solrQuery.addFilterQuery("prod_price:["+perfix+" TO "+arrs[1]+"]");
}
}
//设置价格排序
/*psort=1为升序,psort=2为降序*/
int psort=2;
if(psort==1){
solrQuery.addSort("prod_price", SolrQuery.ORDER.asc);
}else if(psort==2){
solrQuery.addSort("prod_price", SolrQuery.ORDER.desc);
}
//设置分页
//start=0,rows=10 公式:start=rows*(page-1)
solrQuery.setStart(0);
solrQuery.setRows(6);
//设置回显(可以保护隐私数据)
solrQuery.setFields("id","prod_pname","prod_catalog_name");//注意这里没对prod_price做回显,所以查询结果是0.0
//设置默认域(df)
//solrQuery.set("df","prod_pname");
//hi 设置高亮
solrQuery.setHighlight(true);
solrQuery.addHighlightField("prod_pname");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");
QueryResponse query = httpSolrClient.query(solrQuery);
//得到高亮数据
Map<String, Map<String, List<String>>> map = query.getHighlighting();
List<Products> product = query.getBeans(Products.class);
System.out.println(product.size());
//下面的numFound表示查询出来的个数,跟上面的product.size()是一样的
long numFound = query.getResults().getNumFound();
System.out.println(numFound);
for (Products p:product){
//获取id号
String pid = p.getPid();//注意要把索引库的id回显,否则获取不到,为null
Map<String, List<String>> map1 = map.get(pid);
List<String> map2 = map1.get("prod_pname");
if(map2!=null){
// System.out.println(p.getPrice()+":::"+p.getPname());
System.out.println(map2.get(0)+":::"+p.getPname());
}else {
System.out.println(p.getPrice()+":::"+p.getPname());
}
}
httpSolrClient.commit();
httpSolrClient.close();
}
SpringBoot整合solr
maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
编写配置文件(application.yml):
spring:
data:
solr:
host: http://192.168.6.133:8983/solr/db1_core
测试是否可以获取到solrClient,如下:
@SpringBootTest
class SolrSpringbootApplicationTests {
@Autowired
SolrClient solrClient;
@Test
void contextLoads() {
System.out.println(solrClient);
}
}