1,基本内容

当系统中存在海量数据需要进行全文检索时,传统的检索方法的 性能开销会随着数据量的增长而线性增加,因此数据量越大,性能越差。搜索(全文检索)引擎,则完全不存在这个问题,其性能开销会随着数据量的增长到达一个顶点(在这个顶点依然具有很好的性能),以后无论数据量怎么增长,搜索引擎的性能开销基本都可以稳定在这个顶点处,因此搜索引擎同样是处理海量数据的必需技术。

1.1,LIKE模糊查询和全文索引

在实际应用中不可避免地会遇到全文检索的需求,例如,从电商网站上查询所有商品描述中包含“cow”关键词的商品,或者从站内消息中搜索所有包含“cow”关键词的消息……它们共同的特征就是要求查询某个字段包含特定关键词。如果使用传统RDBMS处理这种需求,就需要使用如下SQL语句进 行查询:

select * from goods where name like '%cow%';

如果你稍有SQL优化的基础,就会知道:这种使用LIKE的“模糊查询”本身就是很影响性能的,再加上海量数据,高并发的场景,这种模糊查询是完全不可接受的。所以早期有些论坛系统(如 Discuz),要么彻底禁用全站检索功能,要么只对高级用户开放全文检索功能,而且往往限制每个小时只能检索一次。

LIKE模糊查询只能这样从目标列的值中“逐个”检查,验证是否有要查询的关键词,因此每搜索一条记录,都需要大致固定的时间开销,如果该列的文本内容很长,那么处理时间就会略长一些。当处理千万条记录时,整个LIKE模糊查询的时间开销就是单条记录的处理时间再乘以千万,因此传统LIKE模糊查询的时间开销与表中记录的数量成正比,这对于处理海量数据检索是完全不可接受的。

1.2,反向索引库与Lucene

为了解决LIKE模糊查询的性能问题,Lucene做了一个革命性的创新:先建立反向索引库,再通过反向索引库进行检索。反向索引库需要先对目标内容进行分词,然后以分好的关键词为key建立索引库,value保存了该key出现在哪些文档中在文档中哪些位置等信息。

Document Words
Document_1 the,cow,says,moo
Document_2 the,cat,and,the,hat
Document_3 the,dish,ran,away,with,the,spoon
Word Documents
the Document_1, Document_3, Document_4, Document_5, Document_7
cow Document_2, Document_3, Document_4
says Document_5
moo Document_6

上方的表格中保存了原始的要检索的数据;下方的表格就是为它建立的反向索引库的示意图,该反向索引库的“关键词”列保存了所有关键词,该列本身也是有聚簇索引的,因此对该列执行查询的效率非常高。例如,程序依然要查询哪些文档中出现了“cow”关键词,此时程序不需要在上方的表格中执行查询,而是对下方表格中的 “关键词”列执行查询。对“关键词”列执行查询有两个特征:

  • 不需要使用LIKE模糊查询,性能很好。
  • “关键词”列本身带有聚簇索引,性能很好。

还有一点需要说明的是,不管哪一种语言,它能支持的“词”是有限的,以英语为例,大部分母语为英语的大学生的词汇量在3万个左右;类似地,中文的汉字,单词也是有限的。这就意味着:不管目标文档是百万个也好,是百亿个也罢,反向索引库的关键词并不会显著增加,因此对“关键词”列的检索性能总是有保证的。

Lucene正是因为利用了反向索引库的特征,从而为全文检索提供了性能保证。Lucene是目前世界上最流行的全文检索框架,它解决了传统SQL查询搞不定的情况,或者使用SQL语句能够搞定查询,但要用到很 多LIKE…OR,查询速度很慢,此时就要用到Lucene全文检索技术。

但如果直接使用Lucene,又会存在如下问题:

  • Lucene本身的API比较难用,Lucene框架的开发者自身应该不是Java的开发者,因此他设计的Lucene API比较晦涩难用。
  • Lucene只是一个Java框架,因此只有Java程序员才能使用 Lucene 为项目增加全文检索功能。

考虑到其他语言的开发者也需要为应用增加全文检索功能,而他 们又没有类似于Lucene的搜索引擎框架可供使用,因此 Solr、Elasticsearch 等技术对 Lucene 进行了包装,包装之后的 Solr、Elasticsearch 不再是简单的框架,它们更像搜索引擎的服务器。

虽然Solr、Elasticsearch底层都是基于Lucene的,但它们自己提供了对Lucene索引库的操作管理,开发者不再需要直接面向Lucene API编程,而是面向Solr、Elasticsearch所提供的RESTful接口编程。

1.3,安装 Solr

Apache Solr 是一个开源的搜索引擎,它的底层就是基于Lucene(全文检索引擎)构建的。现在的 Solr 以独立应用的方式运行,就像一个NoSQL存储引擎一样,它既可用于管理Lucene索引库,也可用于作为同样的NoSQL存储库。

与Lucene相比,Solr具有如下优势:

  • Solr是独立应用,而不是简单的框架。Lucene只是一个Java框架,如果开发者不懂Java,那么就没法调用Lucene的API来编写全文检索功能。
  • Solr提供了RESTful接口,开发者能以多种文档格式(XML、JSON或CSV)来输入数据,Solr也能提供对应格式的响应。这种RESTful接口完全与编程语言无关。
  • Solr 是企业级的存储引擎,既支持独立部署,也支持作为大数据存储的分布式 NoSQL 数据库,还能以云端方式部署。
  • 全文检索,Solr提供了全文检索所需的所有功能,如令牌,短语,拼写检查,通配符和自动完成等。 
  • 作为独立应用,Solr提供了一个易于使用,用户友好,功能强大的用户界面,使用它可以执行所有可能的任务,如管理日志以及添加,删除,更新和搜索文档。降低了Lucene的使用门槛。

安装Solr

  • 从官网下载(下载地址)

  • 下载后得到一个压缩包:solr-8.11.2.zip

  • 配置环境变量
JAVA_HOME:由于Solr和Lucene都是基于Java的,因此需要Java环境。配置该环境变量指向JDK的安装路径(不要指向JDK的bin目录),这就是告诉Solr到哪里去找Java环境。
PATH:在PATH环境变量中添加Solr的bin目录所在的路径。这一步是为了让操作系统能找到Solr工具。该环境变量不是必需的,但配置该环境变量可以更方便地在命令行窗口中执行Solr命令。

2,基本操作

2.1,启动关闭

solr start -p <端口>
  • start:启动Solr
  • stop -all:停止Solr服务器。
  • restart:重启Solr服务器。
  • healthcheck:执行状态检查。
  • create_core:用于为Solr服务器创建Core。
  • create_collection:用于为Solr服务器创建Collection。
  • create:根据Solr的运行状态选择创建Core或Collection。如果Solr以单机模式运行,则该命令是创建core;若Solr以云模式运行,则该命令是创建Collection。
  • delete:删除Core或Collection。
  • version:显示Solr的版本。

2.2,用户管理

通过 “solr start -p <端口>” 命令启动Solr之后,启动浏览器,访问 http://localhost:8983/(假设没有使用-p选项改变Solr的默认端口),将看到如图所示的管理界面。

  • Dashboard(仪表盘):显示Solr运行状态一览。
  • Logging(日志):显示Solr运行日志。
  • Core Admin(Core管理):提供了图形用户界面来管理Core。
  • Java Properties(Java属性):显示当前运行的JVM属性一览。
  • Thread Dump(线程Dump):显示Solr内部的线程Dump。

【修改密码】默认启动的 Solr 不需要用户名,密码,任何人都可直接访问,很明显这是不合适的。为了支持权限控制,Solr提供了如下几种身份验证插件:

  • Kerberos Authentication Plugin:Kerberos身份验证插件。
  • Basic Authentication Plugin:基本身份验证插件。
  • Hadoop Authentication Plugin:Hadoop身份验证插件。
  • JWT Authentication Plugin:JWT身份验证插件。

这些身份验证插件的用法都很简单,此处以“基本身份验证插件”为Solr配置用户名,密码。

在Solr安装路径下的server\solr子目录下添加一个security.json文件,该文件的内容如下:

{"authentication":{ "blockUnknown": true, "class":"solr.BasicAuthPlugin","credentials":{"root":"pPKs8BkTXNNLlzipK0LAm6gh64kBEfIuKx1HYU4rHnc= hOJ+WQ/ubP/DPfTnGbjF+ANOZHmnaQ8jAnJh4xxdYu8="}, "realm":"Solr users", "forwardCredentials": false },"authorization":{"class":"solr.RuleBasedAuthorizationPlugin","permissions":[{"name":"security-edit","role":"admin"}], "user-role":{"root":"admin"} }
}

blockUnknown属性指定为true,表明阻止所有未知用户访问;class属性指定使用基本身份验证插件;credentials属性配 置了一个超级用户,其用户名是root,密码是32147。permissions属性定义了一个admin角色,该角色允许执行“secur ity-edit”操作;user-role属性定义了root用户的角色是admin,这样root用 户就拥有执行“security-edit”操作的权限。

添加用户(添加用户需要输入加盐加密的密码),对用户的其他操作(如删除用户、为用户添加权限等)都可通过Solr 管理网站来实现。

2.3,管理 Core

在单机模式下,一个Core等于一个Collection。Solr的Core有点类似于RDBMS的表,Solr Core同样具有支持唯一标识的主键,也需要定义多个Field。与RDBMS不同的是,Core中存放的是各种文档,且这些文档不需要具有相同的Field。因此,在正式使用Solr之前,必须先创建Core。

  • 使用solr命令的 create_core子命令创建 Core。

使用solr命令的create_core子命令创建Core时,没有提供选项来指 定用户名和密码,因此需要先将security.json文件中的blockUnknown属性 设为false,它表示关闭Solr的用户认证功能。

solr create_core -c Core 名称 [-d 配置文件目录] [-p 端口]-p:用于指定Solr实例的端口,如果不指定该选项,该命令将自动使用它搜索得到的第一个Solr实例的端口。
-d:用于指定这些配置模板所在的路径,如果不指定-d 选项,Solr 将默认为该选项使用_default值,也就是使用server\solr\configsets路径下_default目录下的配置文件作为配置模板。

PS:不推荐将_default目录下的配置文件作为产品级的Core来使用。 在server\solr\configsets路径下还提供了一个sample_techproducts_configs目录,该目录下的配置文件可作为产品级的Core来使用,因此推荐使用该目录作为Core配置文件的目录。对于有经验的开发者来说,可以针对不同特征的索引库预先准备好不同的配置模板,完全可改为使用自定义的配置模板目录。

solr create_core -c test -d sample_techproducts_configs

使用如下命令即可删除Core

solr delete [-c Core 名称] [-p 端口]
  • 通过图形用户界面创建 Core:只需要在name和instanceDir文本框中分别输入Core名称和保存目录。

在通过图形用户界面创建Core时,Solr并不会为Core创建目录及配置文件,因此在通过图所示界面中的“Add Core”按钮创建Core之前,先要完成如下两步:

  • 在server\solr路径下创建一个new_core目录,将instanceDir指定为new_core
  • 将server\solr\configsets\sample_techproducts_configs目录下的conf整个目录复制到第1步创建的new_core目录中。如果之前预定义好了配置文件,当然也可使用自己的配置文件。

如果要通过图形用户界面删除 Core,只要在界面中选中指定的 Core,然后单击“Unload”按钮即可删除该Core。 使用“solr delete”命令删除Core和通过图形用户界面删除Core是有区别的:使用“solr delete”命令删除Core时,会把整个Core对应的目录都彻底删除;但通过图形用户界面删除Core时,只是将该Core从Solr系统中删除,并未删除该Core对应的目录,因此以后还可重载添加回来。

在默认情况下,每个Core都对应于server\solr目录下的一个子目录,以前面创建的test Core为例,它对应于test子目录,该子目录下有如下文件夹和文件。

  • conf:保存该Core的配置文件。
  • data:保存该Core的索引库数据。
  • core.properties文件。

core.properties文件包含如下内容,指定了该Core的名称。

#Written by CorePropertiesLocator
#Tue Nov 01 13:34:24 UTC 2022
name=test

在Core目录的conf目录下可以看到如下常见的配置文件:

  • managed-schema:定义该Core的整体Schema,包括该Core包括哪 些Field类型、Field约束、哪些Field、哪些动态Field、哪些Copy Field。该文件以前的文件名是schema.xml,用户可通过文本编辑器直接编辑,现在则推荐使用图形用户界面编辑,这样更安全、有效。
  • solrconfig.xml:该Core的索引库相关配置。
  • protwords.txt:该Core 额外的保护词配置。所谓“保护词”就是停止对该词“词干化”,在正常词干化的处理方式下,如managing、managed、manageable 这些单词最终都会变成manage。如果不希望某个单词被词干化,就将该单词添加到此文件中。
  • stopword.txt:该 Core 额外的停用词配置。Lucene 不会对停用词创建反向索引库,因此程序也不能对停用词执行搜索。
  • synonyms.txt:该Core的所有同义词配置。 通过可视化界面左边的“Core Selector”列表框(只有当前系统有可用的Core时,此处才会显示列表框)可选择要操作的 Core,以选择“Test”Core 为例,将会看到如图所示的Core概览界面。

  • “Analysis” 标签可以对选定的字段进行索引或查询分析。
  • “Documents” 标签可以向当前Core中添加文档或删除文档。
删除文档:{"delete":{"id":1}}
  • “Query”标签可以检索文档。
  • “Schema”标签可以对Field或Dynamic Field执行删除或修改操作。

3,SpringBoot整合Solr

3.1,使用 SolrClient 连接Solr

SolrClient是Solr本身提供的一个API,它是一个操作Solr索引库的门面类,它几乎可以完成对索引库的各种操作:

  • add(String collection, Collection<SolrInputDocument> docs, int commitWithinMs):对collection添加或更新多个文档。每个SolrInputDocument代表一个文档。commitWithinMs参数指定在多少毫秒之内提交修改。
  • add(String collection, SolrInputDocument doc, int commitWithinMs):添加或更新单个文档。
  • addBean(String collection, Object obj, int commitWithinMs):以面向对象的方式添加或更新单个文档。其中obj代表映射文档的实体对象。该方法的本质就是添加单个文档的add()方法。
  • addBeans(String collection, Collection<?> beans,int commitWithinMs):以面向对象的方式添加或更新多个文档。
  • deleteById(String collection, List<String> ids,int commitWithinMs):根据id删除一个或多个文档。其中第2个参数既可以是List集 合,也可以是单个id值。
  • deleteByQuery(String collection, String query, int commitWithinMs):删除符合query条件的所有文档。
  • getById(String collection, String id, SolrParams params):根据id加载文档。其中第3个参数params用于指定额外的Solr参数。
  • optimize(String collection, boolean waitFlush, boolean waitSearcher):优化索引库。
  • query(String collection, SolrParams params, SolrRequest.METHOD method):以params参数从Solr索引库执行检索。
  • request(SolrRequest request, String collection):向Solr索引库发送请求。
  • commit(String collection):提交修改。
  • rollback(String collection):回滚修改。

上面对文档进行修改的方法都可指定一个commitWithinMs参数,该参数指定在多少毫秒内会对所做的修改进行提交;如果上面的修改方法没有指定commitWithinMs参数,就必须显式地使用commit()方法来提交修改。

这些方法都可省略collection参数,如果省略了该参数,那么就要求程序在创建SolrClient时 指定Collection(在单机模式下对应于Core)。

#指定 SolrClient 连接的是Solr 实例的 springboot 这个 Core 。
spring.data.solr.host=http:///127.0.0.1:8983/solr/test#仅指定 SolrClient 连接的是 Solr 实例,并未指定连接哪个 Core,这样该SolrClient在执行上面这些方法时就需要指定collection参数。
spring.data.solr.host=http:///127.0.0.1:8983/solr/

Spring Boot为整合Solr提供了一个Starter:spring-boot-starter-data-solr,但这个Starter的功能很简陋,也就提供了两个类:SolrAutoConfiguration 和 SolrProperties,其中SolrProperties用于加载以“spring.data.solr”开头的属性,SolrAutoConfiguration则负责在容器中自动配置一个SolrClient。而且,该SolrClient还不支持读取用户名,密码信息。

以“spring.data.solr”开头的属性只支持host和zk-host:

  • host用于指定在单机模式下Solr实例的地址;
  • zk-host 则用于指定在云模式下Solr所在ZooKeeper的主机地址。

(1)添加 spring-boot-starter-data-solr.jar 依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-solr</artifactId><version>2.4.10</version>
</dependency>

(2)定义 application.properties 文件

spring.data.solr.host=http:///127.0.0.1:8983/solr/
spring.data.solr.username = root
spring.data.solr.password = 32147

该配置文件中的spring.data.solr.host只指定了连接的Solr服务器实例的地址,并未指定要操作的Core,因此后面调用SolrClient的方法时需要指定collection参数。 该配置文件还配置了用户名和密码,Spring Boot 默认并不会加载这两个属性,因此添加一个自定义的配置类来创建SolrClient。

package com.example.springboot;import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.solr.SolrProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;import java.io.IOException;
import java.util.Arrays;
import java.util.Optional;@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({HttpSolrClient.class, CloudSolrClient.class})
@EnableConfigurationProperties(SolrProperties.class)
public class SolrConfig {@Value("${spring.data.solr.username}")private String username;@Value("${spring.data.solr.password}")private String password;@Beanpublic SolrClient solrClient(SolrProperties properties) throws IOException, SolrServerException {// 设置使用基本认证的客户端System.setProperty("solr.httpclient.builder.factory","org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory");// 设置认证的用户名和密码System.setProperty("basicauth", username + ":" + password);// 如果zk-host属性存在,使用CloudSolrClient创建SolrClientif (StringUtils.hasText(properties.getZkHost())) {return new CloudSolrClient.Builder(Arrays.asList(properties.getZkHost()), Optional.empty()).build();}// 创建单机模式下的SolrClientreturn new HttpSolrClient.Builder(properties.getHost()).build();}
}

SolrConfig类就是对Spring Boot提供的SolrAutoConfiguration 的改进,主要就是系统属性指定了使用基本认证的客户端,这与前面 Solr 配置的认证实现类对应,并为认证信息指定了用户名和密码,从而使得该SolrClient能连接带用户认证的Solr服务器。

(3)定义实体类:该实体类只需要简单地使用 @Field 注解,该注解指定将被修饰的属性(实例变量加上getter,setter方法就变成属性)映射到Core的哪个字段。在使用@Field注解时,可通过value属性指定将它映射到哪个字段;如果省略该属性,则默认映射同名字段。

import org.apache.solr.client.solrj.beans.Field;public class Book {@Field("id")private Integer id;@Fieldprivate String name;@Fieldprivate String description;@Fieldprivate Double price;//构造器、getter、setter
}

(4)创建用SolrClient来操作Solr索引库

package com.example.springboot.Controller;import com.example.springboot.domin.Book;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;
import java.util.ArrayList;@RestController
public class HelloController {@Autowiredprivate SolrClient solrClient;@RequestMapping("/save")public void Save() throws SolrServerException, IOException {var book1 = new Book(1, "狂人日记", "小说通过被迫害者“狂人”的形象以及“狂人”的自述式的描写,揭示了封建礼教的“吃人”本质,表现了作者对以封建礼教为主体内涵的中国封建文化的反抗;也表现了作者深刻的忏悔意识。作者以彻底的“革命民主主义”的立场对中国的文化进行了深刻的反思,同时对中国的甚至是人类的前途表达了深广的忧愤。", 55.5);var book2 = new Book(2, "孔乙己", "小说描写了孔乙己在封建腐朽思想和科举制度毒害下,精神上迂腐不堪、麻木不仁,生活上四体不勤、穷困潦倒,在人们的嘲笑戏谑中混度时日,最后被封建地主阶级所吞噬的悲惨形象。篇幅不长,但是深刻揭露了当时科举制度对知识分子精神的毒害和封建制度“吃人”的本质,具有强烈的反封建意义。", 44.4);var book3 = new Book(3, "故乡", "小说以“我”回故乡的活动为线索,按照“回故乡”——“在故乡”——“离故乡”的情节安排,依据“我”的所见所闻所忆所感,着重描写了闰土和杨二嫂的人物形象,从而反映了辛亥革命前后农村破产、农民痛苦生活的现实。", 33.3);ArrayList a = new ArrayList();a.add(book1);a.add(book2);a.add(book3);// 指定向springboot Core保存文档// 调用addBeans()方法则可保存多个文档solrClient.addBeans("test", a, 100);}@RequestMapping("/select")public void Query(String field, String term) throws IOException, SolrServerException {//GET http://localhost:8080/select?field=name&term=孔*//GET http://localhost:8080/select?field=description&term=精神*// 创建SolrQuerySolrQuery query = new SolrQuery();// 设置查询语法query.setQuery(field + ":" + term);// 指定对springboot Core执行查询QueryResponse queryResponse = solrClient.query("test", query);// 获取查询结果SolrDocumentList docs = queryResponse.getResults();for (var doc : docs) {System.out.println(doc);}}@RequestMapping("/delete")public void Delete(String id) throws IOException, SolrServerException {// 指定对springboot Core执行删除solrClient.deleteById("test", id, 100);}
}

其中 * 表示:查询des cription Field中包含“精神不振”,“精神萎靡” 等关键词。

3.2,使用Spring Data连接Solr与SolrTemplate

虽然Spring Boot的 SolrAutoConfiguration 只自动配置了一个SolrClient,但加上Spring DataSolr,则同样还会自动配置一个SolrTemplate。当然,该SolrTemplate底层其实也是依赖SolrClient的。与所有自动配置的处理方式类似,当开发者在容器中配置了SolrClient之后,Spring Boot将不再自动配置SolrClient。SolrTemplate则是SpringData Solr提供的一个门面类,它提供了如下方法来操作Solr索引库:

  • count(String collection,SolrDataQueryquery,Class<?> domainType):从collection中查询符合query条件的文档的数量。
  • delete(String collection,SolrDataQueryquery,Class<?> domainType):从collection中删除符合query条件的文档。
  • deleteByIds(String collection,Collection<String> ids):根据 id 从collection中删除文档。
  • getById(String collection,Object id,Class<T> clazz):根据单个id返回单个文档对应的实体对象。
  • getByIds(String collection,Collection<?> ids,Class<T> clazz):根据多个id返回多个文档对应的实体对象。
  • query(String collection,Query query,Class<T> clazz,RequestMethod method):查询多个符合query条件的文档对应的实体对象。
  • queryForObject(String collection,Query query,Class<T> clazz,RequestMethod method):查询第一个符合query条件的文档对应的实体对象。
  • saveBeans(String collection,Collection<?> beans,Duration commitWithin):保存多个实体对象对应的文档。
  • saveDocuments(String collection,Collection<SolrInputDocument> documents,Duration commitWithin):保存多个文档。
  • execute(SolrCallback<T> action):这是最灵活的方法,调用该方法时需要传入一个SolrCallback参数,而程序在实现SolrCallback时又可访问到SolrClient,并通过SolrClient操作Solr索引库。

SolrTemplate与SolrClient:将Solr本身提供的SolrClient与SolrTemplate进行对比,不难发现SolrTemplate并没有太大的优势,无非就是 SolrTemplate 更加以操作实体为主。实际上,SolrClient 也能面向实体编程,只不过SolrTemplate更彻底 一些。

Spring Data Solr大致包括如下几方面功能:

  • DAO接口只需继承CrudRepository,Spring Data Solr能为DAO组件提供实现类。
  • Spring Data Solr支持方法名关键字查询,只不过Solr查询都是全文检索查询。
  • Spring Data Solr同样支持DAO组件添加自定义的查询方法—通过添加额外的接口,并为额外的接口提供实现类,Spring Data Solr就能将该实现类中的方法“移植”到DAO组件中。

Solr属于全文检索引擎,因此它的方法名关键字查询也是基于全文检索的。Spring Data Solr的Repository操作的数据类除了用@Field注解修饰,还可用Spring Data Solr提供的@SolrDocument注解修饰,该注解可指定一个collection属性,用于指定该实体类被映射到哪个Core。

实体类

import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.solr.core.mapping.SolrDocument;@SolrDocument(collection = "test")
public class Book {@Field("id")private Integer id;@Fieldprivate String name;@Fieldprivate String description;@Fieldprivate Double price;//省略构造器,getter和setter方法...
}

DAO

package com.example.springboot.Dao;import com.example.springboot.domin.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.solr.repository.Query;
import org.springframework.stereotype.Component;import java.util.List;
public interface BookDao extends CrudRepository<Book, Integer>, BookCustomDao {// 方法名关键字查询List<Book> findByName(String name);List<Book> findByIdIn(List<Integer> list);List<Book> findByPriceBetween(double start, double end);List<Book> findByDescriptionMatches(String descPattern);// 使用@Query定义查询语句@Query(value = "?0: ?1")List<Book> findByQuery(String field, String term);
}

BookDao还继承了BookCustomDao接口,该接口用于为该DAO组件添加自定义的查询方法。下面是BookCustomDao接口的代码。

package com.example.springboot.Dao;
import com.example.springboot.domin.Book;
public interface BookCustomDao {List<Book> customQuery(String name, String description);
}

Controller

package com.example.springboot.Controller;import com.example.springboot.Dao.BookDao;
import com.example.springboot.domin.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
public class HelloController {@Autowiredprivate BookDao bookDao;@RequestMapping("/save")public void Save(Integer id, String name, String description, Double price) {var book = new Book(id, name, description, price);bookDao.save(book);}@RequestMapping("/delete")public void Delete(Integer id) {bookDao.deleteById(id);}@RequestMapping("/selectByName")public void FindByName(String name) {bookDao.findByName(name).forEach(System.out::println);}@RequestMapping("/selectByIdIn")public void FindByIdIn(Integer id1, Integer id2) {bookDao.findByIdIn(List.of(id1, id2)).forEach(System.out::println);}@RequestMapping("/selectByPriceBetween")public void FindByPriceBetween(double start, double end) {bookDao.findByPriceBetween(start, end).forEach(System.out::println);}@RequestMapping("/selectByDescriptionMatches")public void FindByDescriptionMatches(String descPattern) {bookDao.findByDescriptionMatches(descPattern).forEach(System.out::println);}@RequestMapping("/selectByQuery")public void FindByQuery(String field, String term) {bookDao.findByQuery1(field, term).forEach(System.out::println);}@RequestMapping("/selectByCustomQuery")public void CustomQuery(String name, String description) {bookDao.customQuery1(name, description).forEach(System.out::println);}
}

【自定义查询】下面为BookCustomDao提供实现类,该实现类通过SolrTemplate来实现自定义的查询方法使用SolrTemplate执行查询也很简单, 程序只要通过查询语句创建Query对象,然后调用SolrTemplate的query()方法即可完成查询。

package com.example.springboot.Dao;import com.example.springboot.domin.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Query;
import java.util.List;public class BookCustomDaoImpl implements BookCustomDao {@Autowiredprivate SolrTemplate solrTemplate;@Overridepublic List<Book> customQuery1(String name, String description) {// 定义查询语句Query query = Query.query("name:" + name +" AND description:" + description);// 调用SolrTemplate的方法执行查询return solrTemplate.query("test", query, Book.class).toList();}
}

Spring Data Solr也提供了@EnableSolrRepositories注解,该注解用于手动启用Solr Repository支持。一旦程序显式使用该注解,Spring Data Solr的Repository自动配置就会失效。因此,当需要连接多个Solr索引库或进行更多定制时,可手动使用该注解。使用@EnableSolrRepositories注解时,也要指定如下几个属性:

  • basePackages:指定扫描哪个包下的DAO组件(Repository组 件)。
  • solrClientRef:指定基于哪个SolrClient来实现Repository组件, 默认值是solrClient。
  • solrTemplateRef:指定基于哪个SolrTemplate来实现Repository组件,默认值是solrTemplate。

上面solrClientRef与solrTemplateRef两个属 性只要指定其中之一即可。

SpringBoot:整合Solr相关推荐

  1. 创新实训(9)——SpringBoot整合solr

    SpringBoot整合solr 相关配置 实体类DmsArticle 查询逻辑 SolrController SolrService SolrServiceImpl 查询结果 插入逻辑 相关配置 D ...

  2. SpringBoot 整合ElasticSearch全文检索

    ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java语言开发的,并作为Apa ...

  3. SpringBoot整合Spring Data Elasticsearch

    特点: 分布式,无需人工搭建集群(solr就需要人为配置,使用Zookeeper作为注册中心) Restful风格,一切API都遵循Rest原则,容易上手 近实时搜索,数据更新在Elasticsear ...

  4. 跟着狂神学Redis(NoSql+环境配置+五大数据类型+三种特殊类型+Hyperloglog+Bitmap+事务+Jedis+SpringBoot整合+Redis持久化+...)

    跟着狂神学Redis 狂神聊Redis 学习方式:不是为了面试和工作学习!仅仅是为了兴趣!兴趣才是最好的老师! 基本的理论先学习,然后将知识融汇贯通! 狂神的Redis课程安排: nosql 讲解 阿 ...

  5. Springboot整合Elasticsearch搜索引擎+vue页面

    这里我用的是Elasticsearch 6.2.1,logstash 6.2.1,mysql 一.ElasticSearch: 一.介绍 ElasticSearch是一个基于Lucene的搜索服务器. ...

  6. Linux环境下ElasticSearch的安装与使用(SpringBoot整合云服务器上的ElasticSearch)

    0. Elaticsearch 简介 Elaticsearch,简称为ES,ES是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储.检索数据:本身扩展性很好,可以扩展到上百台服务器,处理 P ...

  7. SpringBoot第九篇: springboot整合Redis

    这篇文章主要介绍springboot整合redis,至于没有接触过redis的同学可以看下这篇文章:5分钟带你入门Redis. 引入依赖: 在pom文件中添加redis依赖: <dependen ...

  8. es springboot 不设置id_原创 | 一篇解决Springboot 整合 Elasticsearch

    ElasticSearch 结合业务的场景,在目前的商品体系需要构建搜索服务,主要是为了提供用户更丰富的检索场景以及高速,实时及性能稳定的搜索服务. ElasticSearch是一个基于Lucene的 ...

  9. springboot整合shiro使用shiro-spring-boot-web-starter

    此文章仅仅说明在springboot整合shiro时的一些坑,并不是教程 增加依赖 <!-- 集成shiro依赖 --> <dependency><groupId> ...

  10. db2 springboot 整合_springboot的yml配置文件通过db2的方式整合mysql的教程

    springboot整合MySQL很简单,多数据源就master,slave就行了,但是在整合DB2就需要另起一行,以下是同一个yml文件 先配置MySQL,代码如下 spring: datasour ...

最新文章

  1. Python gui编程pyQt5安装步骤
  2. SAP与Ariba在数据分析领域的互补共赢
  3. 将一个datetime的now转换为只有日期的_不要眨眼!中英文、大小写转换,一秒就搞定!
  4. 单链表式并查集应用举例
  5. LeetCode动态规划 环形子数组的最大和
  6. LeCun自曝使用C语言23年之久,2年前才用Python,还曾短暂尝试Lua
  7. PAT 乙级 1037. 在霍格沃茨找零钱(20)Java版
  8. git学习-fetch命令
  9. (转)DPDK内存管理 04 ---- rte_malloc内存管理
  10. html无节日为空,这个生死相拥的节日_311.Html
  11. 朴素的模式匹配(布鲁特-福斯算法)
  12. Image Retrieval using Scene Graphs ——基于场景图的图像检索 读书笔记
  13. html表格只设外边框,只设内边框
  14. 对“黑暗森林”的质疑和讨论(总结各家言论)
  15. 计算机不能再U盘新建文件夹,如何让你的U盘永不中毒?新建一个文件夹就搞定!-u盘文件夹变成exe...
  16. 青春岁月杂志青春岁月杂志社青春岁月编辑部2022年第11期目录
  17. 2范数和F范数的区别
  18. 照葫芦画瓢之python爬虫系列----(2)初次爬取简单的动态网页数据(网易、QQ音乐排行榜)
  19. DLT698.45之数据类型(RSD)
  20. 计算机毕业设计SSM 校园疫情防控系统【附源码数据库】

热门文章

  1. win7计算机硬盘很慢,Win7系统电脑反应慢 win7系统反应慢的9个解决方法
  2. for i in range()使用方法
  3. 石头购房经验谈(下)
  4. mac地址被路由器拉黑_我买了一个新路由器,告诉我我被黑了
  5. 设计主导型思维在商业领域的崛起,将对创意职业产生积极的溢出效应
  6. 常见电子元器件的常用品牌汇总
  7. SWS_BICUBIC未声明的标识符解决方法
  8. python饼状图显示其比例_Python学习笔记--使用matplotlib绘制饼状图
  9. 类人猿X64安卓手游封包技术教程(主要易语言+个别C++)
  10. Matlab在线运行网址