略。(注意安装jdk)
[root@hadoop1 solr-7.7.3]# cd bin [root@hadoop1 bin]# ./solr start -force
一般来说,在真实项目环境下,数据库是要跟Solr中的索引库结合在一起的,数据库负责增删改,而索引库负责查询,所以,我们要利用Solr客户端来创建我们的索引库,那怎么创建呢?如下:
Add Core,创建核心,可以理解为创建表,点击,如下:
不过以上这样直接点击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'
这次再打开管理界面,就会出现下面的两个索引库,如下:
如果左边高亮,意味着用户输入的虽然是my number这个英文单词,但是是会把phone number这条记录搜索出来的,即使用户没有搜索phone这个英文单词。
再说下分词器,分词器很好懂吧,它默认是英文分词器,但也有中文分词器,中文的后面再说,所谓的分词器就是对一句话分成各个词语,比如苹果手机会被分成三个词语,分别是苹果,手机,苹果手机。当用户在搜索框里输入苹果手机的时候,搜索结果可不单单只出现苹果手机哦,可能水果类的苹果也出来了,这就是分词的作用。在如上图,也就是那个下拉框是选择分词策略的,如果是中文的,那就选中文分词器,只不过默认是英文的,暂时没有中文分词器,没关系,后面再配,反正下拉框里列出来的内容就是各个分词器策略,而这些分词器策略都在一个文件里可以看出来,该文件就在/usr/local/solr-7.7.3/server/solr/索引库名称/conf/managed-schema文件里,里面的fieldType标签就是了。
以商品信息为例,是不是有这几种字段,商品标题,商品描述,商品价格等,分别对应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是一个数组。
向索引库添加数据(Json版):
向索引库添加数据(xml版):
删除id为1的记录:
删除所有记录,就是*:*。下一个是更新,那么就像添加一样,只不过重点在id上,如果你要添加的这条记录,id在索引库刚好存在,那么就会把原有记录覆盖掉,这就是更新。
说明一点,如上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那里表示你要对谁做高亮,下面就是高亮后是什么颜色的。
接下来是配置中文分词器。往下看吧!!!
下载地址: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又是什么意思,如下:
跟分词的粒度相关:
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分词器不认为沙雕是一个词语,只会把沙和雕两个字分开。
我们打开数据库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条数据,意思意思一下,最重要的还是后面进行增删改查的部分。如下:
到这,就要真正的用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在索引库已存在,那就是更新,所以这点要注意!!!因为你一不小心就会把原有记录覆盖掉。
根据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();
简单查询:
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(); }
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); } }