一、什么是搜索?
百度:我们比如说想找寻任何的信息的时候,就会上百度去搜索一下,比如说找一部自己喜欢的电影,或者说找一本喜欢的书,或者找一条感兴趣的新闻(提到搜索的第一印象)。百度 != 搜索
- 互联网的搜索:电商网站,招聘网站,新闻网站,各种app
- IT系统的搜索:OA软件,办公自动化软件,会议管理,日程管理,项目管理。 搜索,就是在任何场景下,找寻你想要的信息,这个时候,会输入一段你要搜索的关键字,然后就期望找到这个关键字相关的有些信息
二、什么是全文检索和Lucene?
- 全文检索,倒排索引
全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。全文搜索搜索引擎数据库中的数据。
- lucene
lucene,就是一个jar包,里面包含了封装好的各种建立倒排索引,以及进行搜索的代码,包括各种算法。我们就用java开发的时候,引入lucene jar,然后基于lucene的api进行去进行开发就可以了。
三、Elasticsearch是什么?
- Elasticsearch,基于lucene.分布式的Restful实时搜索和分析引擎(实时)
- 分布式的实时文件存储,每个字段都被索引并可被搜索
- 高扩展性,可扩展至上百台服务器,处理PB级结构化或非结构化数据
- Elasticsearch用于全文检索,结构化搜索,分析/合并使用
四、Elasticsearch的特性:
- Elasticsearch没有典型意义的事务(无事务性)
- Elasticsearch是一种面向文档的数据库
- Elasticsearch没有提供授权和认证特性
五、Elasticsearch 可以做什么?
- 你有一个在线网上商城,提供用户搜索你所卖的商品功能。在这个例子中,你可以使用Elasticsearch去存储你的全部的商品目录和存货清单并且提供搜索和搜索自动完成以及搜索推荐功能。
- 你想去收集日志或者业务数据,并且去分析并从这些数据中挖掘寻找市场趋势、统计资料、摘要信息或者反常情况。在这个例子中,你可以使用Logstash(part of the Elasticsearch/Logstash/Kibana stack)去收集、聚合并且解析你的数据,然后通过Logstash将数据注入Elasticsearch。一旦数据进入Elasticsearch,你就可以运行搜索和聚集并且从中挖掘任何你感兴趣的数据。
- 你运行一个价格预警平台,它可以让那些对价格精明的客户指定一个规则,比如:“我相中了一个电子产品,并且我想在下个月任何卖家的这个电子产品的价格低于多少钱的时候提醒我”。在这个例子中,你可以抓取所有卖家的价格,把价格放入Elasticsearch并且使用Elasticsearch的反向搜索(过滤器/抽出器)功能来匹配价格变动以应对用户的查询并最终一旦发现有匹配结果时给用户弹出提示框。
- 你有分析学/商业情报的需求并且想快速审查、分析并使用图像化进行展示,并且在一个很大的数据集上查询点对点的问题(试想有百万或千万的记录)。在这个例子中,你可以使用Elasticsearch去存储你的数据然后使用Kibana(part of the Elasticsearch/Logstash/Kibana stack)去构建定制化的仪表盘。这样你就可以很直观形象的了解对你重要的数据。此外,你可以使用Elasticsearch的集成功能,靠你的数据去展现更加复杂的商业情报查询。
怎么说呢?其实elk 就是一个技术解决方案,可以做很多的东西。目前我们只是单单先把es入门把,他的搜索功能学会,用好。
Cluster(集群)
集群包含多个节点,每个节点属于哪个集群是通过一个配置(集群名称,默认是elasticsearch)来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常
Node(节点)
集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时候),默认节点会去加入一个名称为“elasticsearch”的集群,如果直接启动一堆节点,那么它们会自动组成一个elasticsearch集群,当然一个节点也可以组成一个elasticsearch集群。
Index(索引-数据库)
索引包含一堆有相似结构的文档数据,比如可以有一个客户索引,商品分类索引,订单索引,索引有一个名称。一个index包含很多document,一个index就代表了一类类似的或者相同的document。比如说建立一个product index,商品索引,里面可能就存放了所有的商品数据,所有的商品document。
索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。
提示:
事实上,我们的数据被存储和索引在分片(shards)中,索引只是一个把一个或多个分片分组在一起的逻辑空间。然而,这只是一些内部细节——我们的程序完全不用关心分片。对于我们的程序而言,文档存储在索引(index)中。剩下的细节由Elasticsearch关心既可。 我们唯一需要做的仅仅是选择一个索引名。这个名字必须是全部小写,不能以下划线开头,不能包含逗号
Type(类型-表)
每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,比如博客系统,有一个索引,可以定义用户数据type,博客数据type,评论数据type。
商品index,里面存放了所有的商品数据,商品document
但是商品分很多种类,每个种类的document的field可能不太一样,比如说电器商品,可能还包含一些诸如售后时间范围这样的特殊field;生鲜商品,还包含一些诸如生鲜保质期之类的特殊field 例如
- 日化商品type:product_id,product_name,product_desc,category_id,category_name
- 电器商品type:product_id,product_name,product_desc,category_id,category_name,service_period
- 生鲜商品type:product_id,product_name,product_desc,category_id,category_name,eat_period
Document(文档-行)
文档是es中的最小数据单元,一个document可以是一条客户数据,一条商品分类数据,一条订单数据,通常用JSON数据结构表示,每个index下的type中,都可以去存储多个document。
Field(字段-列)
Field是Elasticsearch的最小单位。一个document里面有多个field,每个field就是一个数据字段。
mapping(映射-约束)
数据如何存放到索引对象上,需要有一个映射配置,包括:数据类型、是否存储、是否分词等。
这样就创建了一个名为blog的Index。Type不用单独创建,在创建Mapping 时指定就可以。Mapping用来定义Document中每个字段的类型,即所使用的 analyzer、是否索引等属性,非常关键等。创建Mapping 的代码示例如下:
client.indices.putMapping({
index : 'blog',
type : 'article',
body : {
article: {
properties: {
id: {
type: 'string',
analyzer: 'ik',
store: 'yes',
},
title: {
type: 'string',
analyzer: 'ik',
store: 'no',
},
content: {
type: 'string',
analyzer: 'ik',
store: 'yes',
}
}
}
}
});
elasticsearch与数据库的类比
六、基本使用
// 创建一个文档并且创建索引test1/user
PUT /test1/user/1
{
"name":"程一同学",
"age":6,
"desc":"青年才俊精神小伙子",
"tag":["直男","看电影","高颜值"]
}
//创建一个索引
POST /test1/user
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"desc":{
"type": "text"
},
"tag":{
"type": "text"
}
}
}
}
//创建一个索引
POST /test1
{
"user":{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"desc":{
"type": "text"
},
"tag":{
"type": "text"
}
}
}
}
}
//获取一个文档信息
GET test1/type1/1
POST /test1/type1/1/_update
{
"doc":{
"name":"李四小伙子"
}
}
//添加文档信息
PUT /test1/type1/1
{
"name":"王五",
"age":"24",
"brithday":"2023-10-31"
}
//删除索引
DELETE /test1
//获取文档信息
POST /test1/type1/_search
//查询文档信息
GET /test1/type1/_search
{
"query": {
"term": {
"name": {
"value": "程一同学"
}
}
}
}
//使用高亮查询
GET test1/user/_search
{
"query": {
"match": {
"name":"程一"
}
},
"highlight": {
"fields": {
"name":{}
}
}
}
1、项目结构
2、pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mk</groupId>
<artifactId>es-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>es-api</name>
<description>es-api</description>
<properties>
<java.version>1.8</java.version>
<!--自己定义es版本依赖,保证和本地一致-->
<elasticsearch.version>7.6.1</elasticsearch.version>
</properties>
<dependencies>
<!--导入了elasticsearch-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3、config 连接es的配置
package com.mk.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
* @version 1.0
* @date 2023/11/2 15:10
*/
@Configuration
public class ElasticsearchConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost",9200,"http")));
return client;
}
}
4、创建一个实体类user
package com.mk.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
/**
*
* @version 1.0
* @date 2023/11/2 15:55
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
private String name;
private int age;
}
5、测试方法
package com.mk;
import com.alibaba.fastjson.JSON;
import com.mk.pojo.User;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* es7.6.x 高级客户端测试API
*/
@SpringBootTest
class EsApiApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
//测试索引的创建
@Test
void testCreateIndex() throws IOException {
//1、创建索引请求
CreateIndexRequest request = new CreateIndexRequest("kuang_index");
//2、客户端执行请求 IndicesClient,请求后获得响应
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
//获取一个索引
@Test
void testExistIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("kuang_index");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
//删除一个索引
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("kuang_index");
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
//测试添加文档
@Test
void testAddDocument() throws IOException {
//1、创建对象user
User user = new User("张三", 18);
//2、创建请求
IndexRequest request = new IndexRequest("kuang_index");
//规则 PUT kuang_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
//3、将我们的数据放进请求 json
IndexRequest source = request.source(JSON.toJSONString(user), XContentType.JSON);
//4、客户端发送请求 获取响应结果
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
System.out.println(indexResponse.toString());
System.out.println(indexResponse.status());
}
//获取文档 判断文档是否存在
@Test
void testIsExistDocument() throws IOException {
GetRequest request = new GetRequest("kuang_index", "1");
GetRequest getRequest = request.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none_");
boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 获取文档的信息
@Test
void testGetDocument() throws IOException {
GetRequest request = new GetRequest("kuang_index", "1");
GetResponse getResponse = client.get(request, RequestOptions.DEFAULT);
String source = getResponse.getSourceAsString();
System.out.println(getResponse);
System.out.println(source);
}
//更新文档的信息
@Test
void testUpdateDocument() throws IOException {
//1、创建更新请求
UpdateRequest request = new UpdateRequest("kuang_index", "1");
//2、设置请求响应时间
request.timeout("1s");
//3、创建要修改的对象
User user = new User("李四", 6);
//4、将我们的数据放进请求中 json
UpdateRequest updateRequest = request.doc(JSON.toJSONString(user), XContentType.JSON);
//5、客户端发送请求
UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update);
System.out.println(update.status());
}
//删除文档信息
@Test
void testDeleteDocument() throws IOException {
DeleteRequest request = new DeleteRequest("kuang_index", "1");
request.timeout("1s");
DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
System.out.println(delete);
System.out.println(delete.status());
}
//特殊情况 一般真实项目中批量插入数据
@Test
void testBulkDocument() throws IOException {
BulkRequest request = new BulkRequest();
request.timeout("10s");
List<User> userList = new ArrayList<>();
userList.add(new User("张三",18));
userList.add(new User("李四",6));
userList.add(new User("王五",20));
//批量请求
for (int i = 0; i < userList.size(); i++) {
request.add(new IndexRequest("kuang_index")
.id(""+i+1)//设置ID
.type("user")//设置type
.source(JSON.toJSONString(userList.get(i)),XContentType.JSON)
);
}
BulkResponse bulk = client.bulk(request, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures());//是否失败,FALSE表示操作成功
System.out.println(bulk);
}
//查询文档信息
@Test
void testSearchDocument() throws IOException {
SearchRequest request = new SearchRequest("kuang_index");
// 构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//查询条件,我们可以使用 QueryBuilder工具类来实现 注意使用name.keyword一定要加上这个keyword不然搜索不到
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name.keyword", "张三");
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(120, TimeUnit.SECONDS));
request.source(sourceBuilder);
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
System.out.println(JSON.toJSON(search.getHits()));
System.out.println("=======================");
for (SearchHit documentFields : search.getHits().getHits()) {
System.out.println(documentFields.getSourceAsMap());
}
}
// 批量更新
BulkRequest bulkUpdateRequest = new BulkRequest();
for (int i = 0; i < usersList.size(); i++) {
users = usersList.get(i);
users.setName(users.getName() + " updated");
UpdateRequest updateRequest = new UpdateRequest("kuang_index", "_doc", users.getId().toString());
updateRequest.doc(JSON.toJSONString(users), XContentType.JSON);
bulkUpdateRequest.add(updateRequest);
}
BulkResponse bulkUpdateResponse = client.bulk(bulkUpdateRequest, RequestOptions.DEFAULT);
System.out.println("bulkUpdate: " + JSON.toJSONString(bulkUpdateResponse));
search(INDEX_TEST, TYPE_TEST, "updated");
// 批量删除
BulkRequest bulkDeleteRequest = new BulkRequest();
for (int i = 0; i < testsList.size(); i++) {
users = usersList.get(i);
DeleteRequest deleteRequest = new DeleteRequest("kuang_index", "_doc", users.getId().toString());
bulkDeleteRequest.add(deleteRequest);
}
BulkResponse bulkDeleteResponse = client.bulk(bulkDeleteRequest, RequestOptions.DEFAULT);
System.out.println("bulkDelete: " + JSON.toJSONString(bulkDeleteResponse));
search(INDEX_TEST, TYPE_TEST, "this");
}