Solr及Spring-Data-Solr入门学习

Solr安装与SSM框架整合

Solr的安装与配置

多数搜索引擎应用都必须具有某种搜索功能,而搜索功能往往大量的消耗资源导致应用程序运行缓慢。为此,出现了各种用于构建搜索的应用程序,我们要学习的solr正是其中的一款开源搜索平台。

Apache Solr 是建立在Lucene(全文搜索引擎)之上,帮助我从大量的数据中寻找所需信息。不仅限于搜索,Solr也可用于储存目的。像其他NoSQL数据库一样,它是一种非关系数据储存和处理技术。

下面我们开始喜闻乐见的手摸手教学,教你优雅的整合SSM框架和Solr搜索框架。

项目中使用了SSM + Shiro + Redis + Solr + Vue.JS + ElementUI技术,优雅的整合了SSM框架阶段几个常见的企业框架;并用Vue.js + ElementUI写了超漂亮的前端页面;如果觉得可以,就点亮右上角star吧(#.#)。

如果你对Shiro+用户-角色-权限整合不是很懂,你或许可以看下我的这个项目: 手摸手教你SSM整合Shiro框架后的开发

日常学习记录,如果想支持我,希望能在Github上看到你点亮的星星(#.#)。



安装

Apache Solr官网 下载最新版的Solr,在 Apache Tomcat官网 下载Tomcat。

解压solr,发现其中有如下目录结构

image

新版本的Solr和老版本的不同,老版本中直接提供的是war文件,新版本则是提供了直接可运行的项目;其次需要导入项目中的的文件也有所不同的。下面我们介绍一下完整的配置和Solr项目的启动:

  • 1、将solr-7.4.0/server/solr-webapp/下的webapp文件夹拷贝到Tomcat下的webapps目录下(并重命名为solr)。
  • 2、将solr-7.4.0/server/lib/ext/下的所有Jar文件全部拷贝到Tomcat下webapps/solr/WEB-INF/lib目录下。
  • 3、将solr-7.4.0/server/lib下所有metrics开头的jar文件全部拷贝到webapps/solr/WEB-INF/lib目录下。
  • 4、将solr/7.4.0/server/solr文件夹复制到任意一个位置并重命名为solrhome
  • 5、修改tomcat/webapps/solr/WEB-INF/web.xml文件的第41行,指定solrhome所在的目录。

image

  • 6、在webapps/solr/WEB-INF/web.xml下的第125行出添加注释,也就是注释<security-constraint></secirity-constraint>节点下的配置。

image

  • 7、启动Tomcat,在浏览器中访问http://localhost:8080/solr/index.html即可访问到solr Admin

image

如上所示,你会发现其左下角显示的是No cores,这和老版本的可能又有所差别,所以我们点击No cores,创建一个新的cores,那么会在我们指定的solrhome文件夹内产生一个new_core的空文件夹,并且页面会报错:Can't find resource 'solrconfig.xml' in xxx,表示的就是在这个新core下的conf文件夹下没有找到solrconfig.xml文件:

image

image

我们需要将复制来的configsets/_default/conf这个文件夹(或者solr-7.4.0/server/solr/configsets/_default/conf文件夹)复制到solrhome/new_core/文件夹下。

  • 8、重启Tomcat服务器,发现还是没有出现新的core,点击No cores那里点击add core,此时就会完整的创建一个新的core,在solrhome/new_core文件夹下会生成新的文件:

image

  • 至此Solr已经安装完成。

中文分词器

上面我们成功的安装了solr,下面就要进行相关的配置。因为solr是一个开源的搜索平台,主要功能就是把用户输入的搜索信息分类汇总并进行数据库的查询,而中文众所周知语义比较复杂,而且中文所占的字节和英文也有所不同,所以就出现了中文分词器,实现模拟中文语义对数据进行分词衍化。IK Analyzer正是其中的一种分词器。

IK Analyzer在solr工程中的配置如下:

  • 1、下载ikanalyzer相关配置文件,因为Maven仓库中的ikanalyzer版本太老,solr5以上的版本都不支持,所以这里提供一个新版本: 传送门
  • 2、将下载的jar文件copy到webapps/solr/WEB-INF/lib文件夹下
  • 3、在webapps/solr/WEB-INF/下创建classes文件夹,将上面下载的文件夹中的ext.dicIKAnalyzer.cfg.xmlstopword.dic三个文件复制到webapps/solr/WEB-INF/classes/文件夹下。
  • 4、在solrhome/conf/目录下我们发现并没有schema.xml文件,这和老版本又有所不同,老版本直接生成好了schema.xml文件,在新版本中我们可以发现/conf文件夹中存在一个managed-schema文件,这个其实就是我们要的schema.xml文件。但是我们又不能直接用,具体原因参考 博文
  • 5、直接将managed-schema文件重命名为schema.xml。并在schema.xml的最后添加<fieldType>节点:
<fieldType name="text_ik" class="solr.TextField">
     <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
  • 6、重启Tomcat服务器,重新访问项目,点击new_core下的Schema功能,在select下拉框中输入text_ik如果出现刚创建的text_ik,说明IK中文分词器安装成功。

image

测试

未使用分词器效果:

image

使用了分词器的效果显而易见:

image


配置域

solr和其他NoSQL数据库一样可以实现数据存储,所以我们可以以数据库的思想想象一下solr,之前我们新创建的core就类似一个数据库,那么下面要配置的就相当于数据库的表字段,因此要手动的去定义系统中需要的字段Field(域)。

通常我们创建的一种Field分别对应这一类数据,用户对同一种数据进行相同的操作。域常用的属性有:

  • name: 指定域的名称
  • type: 指定域的类型
  • indexed: 是否索引
  • stored: 是否储存
  • required: 是否必须
  • multiValued: 是否多值

域的介绍

如上面的介绍,类似数据库中的表字段,而我们做项目时数据库的字段都是根据项目需求创建的,所以也是如此,它是根据搜索平台需要搜索的信息对应的数据库表字段来创建的。

比如,在淘宝商城购买商品,我们可能会搜索:1、品牌(对应数据库中brand字段);2、价格(对应数据库中price字段);3、商品介绍名称(对应数据库中title字段)等等…

每一中域(字段)都用<field>字段设定,比如如上的搜索数据,我们可以设置为:

<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_price" type="pdouble" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category" type="string" indexed="true" stored="true" />
<field name="item_seller" type="text_ik" indexed="true" stored="true" />
<field name="item_brand" type="string" indexed="true" stored="true" />

在新版本的solr中,type属性不能单单设置为基本的数据类型名称了,具体用法要参考schema.xml文件中之前已存在的配置,例如:long要写成plong,double要写为pdouble,不然就会报错。

注意:

  • 你会奇怪域的设定不就是根据用户搜索的数据分类来设定的吗,那为何还要指定indexed=“true”,原因:可能有些数据是不是用户输入的查询的,但是还是需要在用户搜索的同时检索出来。

  • 你会奇怪为何要设定stored,原因:大多数域都是要进行存储的,但是也有不需要存储的,比如复制域。

复制域

复制域的作用在于将某一个Field中的数据复制到另一个域中。
由于用户输入的数据可能是查询的价格,也可能是商品的title,又或者是商品的品牌等… 我们无法预测用户要查询的是什么,由此出现了搜索引擎平台,帮助我们对查询数据进行分类。
所以,solr的目标是实现两种不同的域可以在同一个域中查询(发送一次请求),而复制域的出现正可解决这一问题。

如此,我们为上面要查询的字段设定复制域:

<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="item_title" dest="item_keywords"/>
<copyField source="item_category" dest="item_keywords"/>
<copyField source="item_seller" dest="item_keywords"/>

其中的source属性值要和<field>中的name保持一致。

动态域

在项目中,商品的数据可能会动态的添加或减少,比如原来没有的数据,但是后来又完善添加上去了,那么就需要动态的配置从而实现用户能及时查询到。

<dynamicField name="item_conf_*" type="string" indexed="true" stored="true">

Spring Data Solr入门

经过上面的安装和配置大家应该已经知道如何配置Solr,那么就会思考一个问题了:这个solr项目和我们实际的项目查询有什么关系呢?是怎么结合的呢?

solr官方提供了solrj API,就是一个jar文件,我们可以通过solr官方提供的接口来实现本地项目和solr项目的交互;而这里我们要介绍的是Spring Data Solr,它是Spring Data家族对solrj进行封装后的框架。

  • 注意
    上面我们在Tomcat中部署的solr项目是不需要再进行位置上的变动的,也就是他必须是已经在Tomcat中部署好的,而我们自己的项目启动时不能再使用8080端口(因为solr本身就占用了Tomcat的端口,而我们的项目是可以改变运行端口的,总之两者无论是不是在同一个Tomcat服务器中部署都不能使用同一端口)。我们通过配置文件就能访问到这个指定端口的solr项目(Tomcat必须是启动着的),通过Spring Data Solr提供的接口就能实现交互:
    所谓交互 --> 等价于查询solr中以存在的数据,然后将结果返回:
    • 用户查询,请求接口将查询条件交给solr(通过Spring Data Solr提供的接口访问Solr服务),solr对自身已存在的数据进行查找

准备

导入jar文件

<dependency>
	    <groupId>org.springframework.data</groupId>
	    <artifactId>spring-data-solr</artifactId>
	    <version>1.5.5.RELEASE</version>
</dependency> 

创建配置文件

既然是Spring家族的框架,当然要进行配置使用了,创建spring-solr.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:solr="http://www.springframework.org/schema/data/solr"
       xsi:schemaLocation="http://www.springframework.org/schema/data/solr
  		http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- solr服务器地址 -->
    <solr:solr-server id="solrServer" url="http://127.0.0.1:8080/solr/new_core"/>

    <!-- solr模板,使用solr模板可对索引库进行CRUD的操作 -->
    <bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
        <constructor-arg ref="solrServer"/>
    </bean>
</beans>
  • 注意:

一定要注意solr配置中solr服务器地址的配置,这个url一定是solr项目访问地址 + /core实例名称组合的路径。一般我们新建的core实例名称就是new_core

为实体类属性添加@Field注解

上面完成了基本的环境配置,下面则需要为实体类中属性添加@Field注解标识。

一般我们定义的实体类属性名称和数据库的字段名称相似,但是,如果使用了Solr搜索,每次查询数据将不再查询数据库,那么Solr怎么获取到你要查询的是商品的title还是price呢?

这里就需要使用@Field注解,保证实体类属性名称和Solr索引库中定义的Field域名称对应,如果当前属性名称和Solr索引库域Field名称相同,就添加@Field名称,如果不相同就添加@Field("域名称")注解。

这里我们这样定义实体类 Goods

public class Goods implements Serializable{
    @Field
    private Long id; //商品ID
    @Field("item_title")
    private String title; //商品标题
    @Field("item_price")
    private String price; //商品价格
    @Field("item_image")
    private String image; //商品图片
    @Field("item_category")
    private String category; //商品类别
    @Field("item_brand")
    private String brand; //商品品牌
    @Field("item_seller")
    private String seller; //商品卖家
}
  • 注意:温馨提示一下,大家创建实体类的时候尽量养成一个习惯:实现Serializable序列化接口。不序列化迟早会遇到问题。

实例

本例详细代码请参看: Github

  1. 由于我们创建的是测试类,需要使用@RunWith@ContextConfiguration注解加载配置文件。

  2. 在测试类中用注入Spring Data Solr操作Solr索引库的核心类SolrTemplate

@Autowired
private SolrTemplate solrTemplate;

添加

@Test
public void testAdd() {
    Goods goods = new Goods(1L, "IPhone SE", "120", "手机", "Apple", "Apple");
    solrTemplate.saveBean(goods);
    solrTemplate.commit(); //提交
}

实现数据的添加:

  1. 实例化一个实体类,并添加数据。
  2. 调用solrTemplatesaveBean()方法;这个saveBean()是用来添加普通对象类型数据到Solr索引库的,如果是List集合这种类型,使用saveBeans()方法。
  3. 调用solrTemplatecommit()方法,提交更改;类似于我们请求数据库时需要关闭连接一样,必须调用commit()方法才能保存修改。

按主键查询

@Test
public void testFindById() {
    Goods goods = solrTemplate.getById(1, Goods.class);
    System.out.println("--------" + goods.getTitle());
}

按主键删除

@Test
public void testDeleteById() {
    solrTemplate.deleteById("1");
    solrTemplate.commit(); //提交
}

批量插入数据

@Test
public void testAddList() {
    List<Goods> list = new ArrayList<Goods>();
    //循环插入100条数据
    for (int i = 0; i < 100; i++) {
        Goods goods = new Goods(i + 1L, "华为Mate" + i, String.valueOf(2000 + i), "手机", "手机", "华为专卖店");
        list.add(goods);
    }
    solrTemplate.saveBeans(list); //添加集合对象,调用saveBeans();添加普通对象类型数据,使用saveBean();
    solrTemplate.commit(); //提交
}

分页查询

@Test
public void testPageQuery() {
    Query query = new SimpleQuery("*:*");
    query.setOffset(20); //开始索引(默认0)
    query.setRows(20); //每页记录数(默认10)
    ScoredPage<Goods> page = solrTemplate.queryForPage(query, Goods.class);
    System.out.println("总记录数:" + page.getTotalElements());
    List<Goods> list = page.getContent();
}

上面使用new SimpleQuery方式是声明一个Query实例,而("*:*")表示查询Solr索引库中的所有数据。Solr默认查询的数据是前十条记录,也就是即便使用了("*:*")查询,也仅仅是查询到十条记录。
不过Solr提供了分页查询的方法:setOffset()设置开始索引位置,setRows()设置结束索引位置(默认10);调用solrTemplate.queryForPage(query, clazz)即是分页查询。

分页查询到的结果存储在page对象中,使用page.getTotalElements()可以获取到查询的总记录数,使用page.getContent()获取到查询的数据。

条件查询

@Test
public void testPageQueryMutil() {
    Query query = new SimpleQuery("*:*");
    Criteria criteria = new Criteria("item_title").contains("2");
    criteria = criteria.and("item_title").contains("5");
    query.addCriteria(criteria);

    ScoredPage<Goods> page = solrTemplate.queryForPage(query, Goods.class);
    System.out.println("总记录数:" + page.getTotalElements());
    List<Goods> list = page.getContent();
}

如上,使用分页插件需要实例化Criteria类添加查询条件,查询是根据schema.xml中定义的Field域名称查询的,相当于根据数据库的字段名称查询一样。

删除所有

@Test
public void deleteAll(){
    Query query = new SimpleQuery("*:*");
    solrTemplate.delete(query);
}

交流

如果大家有兴趣,欢迎大家加入我的Java交流群:671017003 ,一起交流学习Java技术。博主目前一直在自学JAVA中,技术有限,如果可以,会尽力给大家提供一些帮助,或是一些学习方法,当然群里的大佬都会积极给新手答疑的。所以,别犹豫,快来加入我们吧!


联系

If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值