SpringBoot集成Elasticsearch
什么是搜索?
-
百度、Google:我们想寻找一个我们喜欢的电影或者书籍就会去百度或者Google搜索一下。
-
互联网搜索:电商搜索商品,招聘网站搜索简历或者岗位
-
IT系统的搜索:员工管理搜索,会议管理搜索
用Mysql数据库做搜索会怎么样?
-
如果表记录上千万上亿了,会有性能问题,另外一个如果有一个本文字段要在里面模糊配置,这个就会出现严重的性能问题;
-
还不能将搜索词拆分开来,比如有个字段值“张三”,“张三丰”只能搜索以“张三”开头才有结果,如果想用“张小三”那是搜索不出来任何数据的。
总体来说,用数据库来实现搜索,是不太靠谱的,通常性能也会很差。
什么是全文检索、倒排索引和Lucene
举个简单的例子:比如最近上映的热剧(碟中谍6:全面瓦解),我们想搜索一下全面瓦解这个电视剧,可是在输入的过程,不小心输入了”全瓦解”,我们看看百度这个返回了什么,百度返回的结果确实是我想要找到的内容。
倒排索引就是将数据中的词拆分构建一个大表,将关键字拆出来,后面带上这个文章的documentid号,例如下图中间这个就是倒排索引了。
全文检索就比较好理解的,就是当我们输入“全瓦解”,会被拆分成”全”,“瓦解”2个此,用2个词去倒排索引里面去检索数据,检索到的数据返回。整个过程就叫做全文检索。
如果这个用数据库的思维来做的话,假如一共100W的记录,按照之前的思路就是扫描100W次,而且每次扫描,都需要匹配那个文本所有的字符,确认是否包含搜索的关键词,而且还不能将搜索词拆解来进行检索。
如果是利用倒排索引的话,假设还是100W,拆分出来的词语,假设有1000W个词语,那么在倒排索引中,就有1000W行。我们可能不需要检索1000W词,有可能检索1次,就能找到我们需要的数据,也有可能是100W次,也有可能是1000W次。
lucene:就是一个jar包,里面包含了封装好的各种建立倒排索引,以及进行搜索的代码,包括各种算法。
ElasticSearch是什么?
Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。
Lucene是单机的模式,如果你的数据量超过了一台物理机的容量,你需要扩容,将数据拆分成2份放在不同的集群,这个就是典型的分布式计算了。需要拷贝容错,机器宕机,数据一致性等复杂的场景,这个实现就比较复杂了。
ElasticSearch解决了哪些问题?
-
自动维护数据的分布到多个节点的索引的建立,还有搜索请求分布到多个节点的执行
-
自动维护数据的冗余副本,保证了一旦机器宕机,不会丢失数据
-
封装了更多高级的功能,例如聚合分析的功能,基于地理位置的搜索
ElasticSearch基本概念
Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比如下图
Elasticsearch | Mysql |
---|---|
Index(索引) | Database(数据库) |
Types(类型) | Table(表) |
Documents(文档) | Row(行) |
Fileds(字段) | Column(列) |
ElasticSearch有哪些功能?
1、分布式的搜索引擎和数据分析引擎
- 搜索:网站的站内搜索,IT系统的检索
- 数据分析:电商网站,统计销售排名前10的商家
2、全文检索,结构化检索,数据分析
- 全文检索:我想搜索商品名称包含某个关键字的商品
- 结构化检索:我想搜索商品分类为日化用品的商品都有哪些
- 数据分析:我们分析每一个商品分类下有多少个商品
3、对海量数据进行近实时的处理
- 分布式:ES自动可以将海量数据分散到多台服务器上去存储和检索
- 海联数据的处理:分布式以后,就可以采用大量的服务器去存储和检索数据,自然而然就可以实现海量数据的处理了
- 近实时:检索数据要花费1小时(这就不要近实时,离线批处理,batch-processing);在秒级别对数据进行搜索和分析
ElasticSearch的特点
-
可以作为一个大型分布式集群(数百台服务器)技术,处理PB级数据,服务大公司;也可以运行在单机上,服务小公司
-
Elasticsearch不是什么新技术,主要是将全文检索、数据分析以及分布式技术,合并在了一起
-
对用户而言,是开箱即用的,非常简单,作为中小型的应用,直接3分钟部署一下ES
-
Elasticsearch作为传统数据库的一个补充,比如全文检索,同义词处理,相关度排名,复杂数据分析,海量数据的近实时处理;
ElasticSearch的应用场景
- 维基百科
- The Guardian(国外新闻网站)
- Stack Overflow(国外的程序异常讨论论坛)
- GitHub(开源代码管理)
- 电商网站
- 日志数据分析
- 商品价格监控网站
- BI系统
- 站内搜索
SpringBoot集成ES步骤
依赖引入
<!-- ElasticSearch依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
配置文件
es:
port: 9200
servers: localhost
配置Config类
此处为单机配置,集群模式再次基础上修改也行
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@Value("${es.clusterName}")
private String clusterName;
@Value("${es.servers}")
private String servers;
@Value("${es.port}")
private int port;
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(servers + ":" + port)
.build();
return RestClients.create(clientConfiguration).rest();
}
}
定义数据类型
定义数据类型,类似于mysql的表,定义好字段,@Field()可以定义字段类型以及分词等。
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
//@Document(indexName = "es_architecture")
public class ArchitectureDto {
@ApiModelProperty(value = "id", required = true)
@Id
private String id;
@ApiModelProperty(value = "建筑名称", required = true)
@Field(type = FieldType.Text,analyzer = "ik-max-word")
private String name;
@ApiModelProperty(value = "所在省份", required = true)
@Field(type = FieldType.Text)
private String province;
@ApiModelProperty(value = "所在城市", required = true)
@Field(type = FieldType.Text)
private String city;
@ApiModelProperty(value = "所在区", required = true)
@Field(type = FieldType.Text)
private String area;
@ApiModelProperty(value = "详细街道地址", required = true)
@Field(type = FieldType.Text)
private String address;
@ApiModelProperty(value = "经纬度", required = true)
private LocationPo location;
@ApiModelProperty(value = "描述", required = true)
@Field(type = FieldType.Text)
private String description;
@ApiModelProperty(value = "评分", required = true)
@Field(type = FieldType.Double)
private double score;
@ApiModelProperty(value = "门票价格", required = true)
@Field(type = FieldType.Double)
private double price;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LocationPo {
@ApiModelProperty(value = "经度", required = true)
private double lon;
@ApiModelProperty(value = "纬度", required = true)
private double lat;
}
增删改查代码
通过restHighLevelClient对象对ElasticSearch数据库进行操作,restHighLevelClient由springboot容器创建管理,用户不需要进行配置,使用的时候注入即可,本次使用的是测试类代码编写方式。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = EsApplication.class)
public class EsHandTest {
@Autowired
private RestHighLevelClient restHighLevelClient;
// 测试文档的添加
@Test
public void testCreateDoc() throws IOException {
// CreateDoc5()创建实体方法,可自己实现
ArchitectureDto architectureDto = DocDemo.CreateDoc5();
// 创建好index请求
IndexRequest indexRequest = new IndexRequest("architecture_index");
// 设置索引
indexRequest.id("5");
// 设置超时时间(默认)
indexRequest.timeout(TimeValue.timeValueSeconds(5));
// 往请求中添加数据
indexRequest.source(JSON.toJSONString(architectureDto), XContentType.JSON);
//执行添加请求
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(indexResponse);
}
@Test
public void getDoc() throws IOException {
//获得查询索引的请求对象
GetRequest gerRequest = new GetRequest("architecture_index").id("2");
//获得文档对象
GetResponse doc = restHighLevelClient.get(gerRequest, RequestOptions.DEFAULT);
//获得文档数据
System.out.println(doc.getSourceAsString());
}
@Test
public void delDoc() throws IOException {
//获得删除的索引请求对象
DeleteRequest delRequest = new DeleteRequest("architecture_index").id("1");
//删除文档
DeleteResponse delete = restHighLevelClient.delete(delRequest, RequestOptions.DEFAULT);
System.out.println(delete.getIndex());
}
@Test
public void delIndex() throws IOException {
IndicesClient indices = restHighLevelClient.indices();
DeleteIndexRequest delReq = new DeleteIndexRequest("architecture_index");
AcknowledgedResponse delete = indices.delete(delReq, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
@Test
public void contextLoads() throws IOException {
//查询mysql中所有数据
List<ArchitectureDto> architectures = new ArrayList<>();
//创建批量处理对象
BulkRequest bulkRequest = new BulkRequest();
//循环添加新增处理请求
for (ArchitectureDto architecture : architectures) {
String architecturJson = JSON.toJSONString(architecture);
IndexRequest indexRequest = new IndexRequest("architecture_index").id(architecture.getId() + "").source(architecturJson, XContentType.JSON);
bulkRequest.add(indexRequest);
}
//提交批量处理对象
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
//查看添加状态
System.out.println(bulk.status());
}
}
高级查询代码
通过restHighLevelClient对象对ElasticSearch数据库进行高级查询操作
@RunWith(SpringRunner.class)
@SpringBootTest(classes = EsApplication.class)
public class EsSearchTest {
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 查询条件数据 匹配查询不到将字段类似设置为.keyword
* @throws IOException
*/
@Test
public void searchAll() throws IOException {
//定义请求对象
SearchRequest request = new SearchRequest("architecture_index");
//制定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchAllQuery()); //查询所有
// builder.query(QueryBuilders.termQuery("address","huahexi777")); //非String 类型查询
// builder.query(QueryBuilders.termQuery("location.lat",33.2)); //非String 类型查询
// builder.query(QueryBuilders.matchPhraseQuery("area","高新区")); //精准查询String
// builder.query(QueryBuilders.matchQuery("area.keyword","高新区")); // 不能匹配到
// builder.query(QueryBuilders.termQuery("area.keyword","高新区"));
// builder.sort("price", SortOrder.DESC);
request.source(builder);
//获得文档对象
SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//获得文档数据
for (SearchHit hit : search.getHits().getHits()) {
ArchitectureDto art = JSONObject.parseObject(hit.getSourceAsString(), ArchitectureDto.class);
System.out.println(JSON.toJSONString(art));
}
}
/**
* 类似于数据库的 or 查询
* @throws IOException
*/
@Test
public void searchByBolt() throws IOException {
//定义请求对象
SearchRequest request = new SearchRequest("architecture_index");
//制定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.matchQuery("price",10));
boolQueryBuilder.should(QueryBuilders.matchQuery("score",4.6).boost(10));
builder.query(boolQueryBuilder); //非String 类型查询
request.source(builder);
//获得文档对象
SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//获得文档数据
for (SearchHit hit : search.getHits().getHits()) {
ArchitectureDto art = JSONObject.parseObject(hit.getSourceAsString(), ArchitectureDto.class);
System.out.println(JSON.toJSONString(art));
}
}
/**
* 查询部分字段
* @throws IOException
*/
@Test
public void searchByParam() throws IOException {
//定义请求对象
SearchRequest request = new SearchRequest("architecture_index");
//制定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchAllQuery()); //查询所有
String[] includes = {"name","address","price"};
String[] excludes = {};
/** 会多出一个score字段 默认值都为0 具体原因不详 */
builder.fetchSource(includes,excludes);
request.source(builder);
//获得文档对象
SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//获得文档数据
for (SearchHit hit : search.getHits().getHits()) {
ArchitectureDto art = JSONObject.parseObject(hit.getSourceAsString(), ArchitectureDto.class);
System.out.println(JSON.toJSONString(art));
}
}
/**
* 范围查询 大于小于
* @throws IOException
*/
@Test
public void searchByFilter() throws IOException {
//定义请求对象
SearchRequest request = new SearchRequest("architecture_index");
//制定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//定制查询条件
boolQueryBuilder.filter (QueryBuilders.rangeQuery("price").gte(10).lte(30));
builder.query(boolQueryBuilder); //非String 类型查询
request.source(builder);
SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
for (SearchHit hit : search.getHits().getHits()) {
ArchitectureDto art = JSONObject.parseObject(hit.getSourceAsString(), ArchitectureDto.class);
System.out.println(JSON.toJSONString(art));
}
}
/**
* 迷糊查询
* @throws IOException
*/
@Test
public void searchByLike() throws IOException {
//定义请求对象
SearchRequest request = new SearchRequest("architecture_index");
//制定检索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//定制查询条件
TermQueryBuilder builder = QueryBuilders.termQuery("name.keyword","北京大").fuzziness(Fuzziness.ONE));
searchSourceBuilder.query(builder);
request.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
for (SearchHit hit : search.getHits().getHits()) {
ArchitectureDto art = JSONObject.parseObject(hit.getSourceAsString(), ArchitectureDto.class);
System.out.println(JSON.toJSONString(art));
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步