ES的介绍和使用

devtools/2024/9/20 1:20:40/ 标签: elasticsearch, 大数据, 搜索引擎

全文搜索引擎 Elastic Search

第一节 引言

当系统数据量上了10亿、100亿条的时候,我们用什么数据库好?如何解决单点故障?如何提升检索速度?如何解决统计分析问题?

传统数据库的应对解决方案

  • 关系型数据库

通过主从备份解决数据安全性问题;
通过数据库代理中间件心跳监测,解决单点故障问题;
通过代理中间件将查询语句分发到各个slave节点进行查询,并汇总结果

  • 非关系型数据库

通过副本备份保证数据安全性;
通过节点竞选机制解决单点问题;
先从配置库检索分片信息,然后将请求分发到各个节点,最后由路由节点合并汇总结果

另辟蹊径

存储数据时按有序存储;
将数据和索引分离;
压缩数据;

于是,Elastic Search就在这种背景下诞生了

第二节 Elastic Search 介绍

1.1 ES简介

​ Elastic Search 简称ES,是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。

1.2 ESLucene 的关系

  • Lucene 只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene 非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的
  • ES也使用Java开发并使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API来隐藏 Lucene 的复杂性,从而让全文搜索变得简单

第二节 安装 Elastic Search

2.1 安装 Elastic Search

解压 elasticsearch-7.6.2-windows-x86_64.zip

2.2 安装 Kibana

解压 kibana-7.6.2-windows-x86_64.zip

2.3 安装 IK Analyzer

  • 进入 elasticsearch-7.6.2\plugins 目录
  • 创建文件夹 ik
  • elasticsearch-analysis-ik-7.6.2.zip 拷贝至 ik 目录下,然后解压

ELK = Elasticsearch Logstash Kibana => 日志采集系统

第三节 ES 结构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结构说明:

  • ES中的索引(index)相当于 MySQL 中的数据库(database),但ES7之后建议每个索引下只存放一种类型(type)
  • ES中的数据分片(shard)是特有的,在集群模式下,海量的数据可以分别存储在不同的分片中,每个分片都有备份数据,其备份数据称为replica,备份数据的主要作用就是在主分片数据不可用的情况下,会将备份数据(replica)作为新的主分片使用
  • ES中的类型(type)相当于 MySQL 中的表(table)
  • ES中的文档(document)相当于 MySQL 中的表的一行数据(row)
  • ES中的字段(field)相当于 MySQL 中的表的字段(field)

第四节 ES 字段类型

4. 1 字符串类型

  • text:一般被用于全文检索。 将当前Field进行分词。
  • keyword:当前Field不会被分词。

4.2 数值类型

  • long:占用8个字节
  • integer:占用4个字节
  • short:占用2个字节
  • byte:占用1个字节
  • double:占用8个字节
  • float:占用4个字节

4.3 范围类型

  • long_range:赋值时,无需指定具体的内容,只需要存储一个范围即可,指定gt,lt,gte,lte
  • integer_range:同上
  • double_range:同上
  • float_range:同上
  • date_range:同上
  • ip_range:同上

4.4 其他类型

  • date类型,针对时间类型指定具体的格式

  • boolean类型,表达true和false

  • binary类型暂时支持Base64 encode string

  • 经纬度类型:geo_point:用来存储经纬度的

  • ip类型:ip:可以存储IPV4或者IPV6

第五节 ES RESTful 操作

5.1 索引操作

5.1.1 创建索引
# 创建索引  请求方式只能是PUT   /user表示创建一个user索引
# settings表示这个索引的一些设置  number_of_shards表示分片数量
# number_of_replicas 表示每个分片的副本数量
PUT /user
{"settings": {"number_of_shards": 3,"number_of_replicas": 1}
}

结果:

# acknowledged表示索引创建的回执信息,也就是响应的结果
# shards_acknowledged表示索引分片创建的回执信息
# index表示索引的名称
{"acknowledged" : true, "shards_acknowledged" : true,"index" : "user"
}
5.1.2 查看索引
GET /user

结果:

# user表示索引名称
# aliases表示索引的别名
# mappings表示索引中的字段
# settings表示索引的创建信息
{"user" : {"aliases" : { },"mappings" : { },"settings" : {"index" : {"creation_date" : "1667629798434","number_of_shards" : "3","number_of_replicas" : "1","uuid" : "VwShKX6-TliIeCIKttsRwA","version" : {"created" : "7060299"},"provided_name" : "user"}}}
}
5.1.3 删除索引
DELETE /user

结果:

{"acknowledged" : true
}

5.2 类型操作

5.2.1 创建类型
# /user/_mappings表示设置user索引的具体数据结构,相当于定义类中的属性
# class User{ String name; String sex; int age; Date birthday;}
PUT /user/_mappings
{"properties": {"name": {"type": "text","analyzer": "ik_max_word","index": true,"store": false},"sex": {"type": "keyword"},"age": {"type": "integer"},"birthday": {"type": "date","format": "yyyy-MM-dd"}}
}

结果:

{"acknowledged" : true,"shards_acknowledged" : true,"index" : "user"
}
5.2.2 查看类型
GET /user

结果:

{"user" : {"aliases" : { },"mappings" : {"properties" : {"age" : {"type" : "integer"},"birthday" : {"type" : "date","format" : "yyyy-MM-dd"},"name" : {"type" : "text","analyzer" : "ik_max_word"},"sex" : {"type" : "keyword"}}},"settings" : {"index" : {"creation_date" : "1667631425739","number_of_shards" : "5","number_of_replicas" : "1","uuid" : "hfhUHBKWQZukfeZLlteHKQ","version" : {"created" : "7060299"},"provided_name" : "user"}}}
}

5.3 文档操作

5.3.1 增加文档
POST /user/_doc
{"name": "张三","sex": "男","age": 20,"birthday": "2000-05-06"
}

结果:

{"_index" : "user","_type" : "_doc","_id" : "lfKkRoQB18W7DskkwOaq","_version" : 1,"result" : "created","_shards" : {"total" : 2,"successful" : 1,"failed" : 0},"_seq_no" : 0,"_primary_term" : 1
}
5.3.2 查看文档
GET /user/_search
{"query": {"match_all": {}}
}

结果:

{"took" : 1,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "user","_type" : "_doc","_id" : "lfKkRoQB18W7DskkwOaq","_score" : 1.0,"_source" : {"name" : "张三","sex" : "男","age" : 20,"birthday" : "2000-05-06"}}]}
}
5.3.3 修改文档
# 修改语法: /索引/_doc/数据ID
# 这种修改需要注意,不是只修改一个属性,而是对整个文档进行修改,可以理解为是用一个新的文档去替换原来的文档完成修改。如果新的文档
# 只有1个属性,那么替换后的文档也只有1个属性。
PUT /user/_doc/lfKkRoQB18W7DskkwOaq
{"sex": "女"
}

结果:

{"_index" : "user","_type" : "_doc","_id" : "lfKkRoQB18W7DskkwOaq","_version" : 3,"result" : "updated","_shards" : {"total" : 2,"successful" : 1,"failed" : 0},"_seq_no" : 2,"_primary_term" : 1
}
5.3.4 删除文档
DELETE /user/_doc/lfKkRoQB18W7DskkwOaq

结果:

{"_index" : "user","_type" : "_doc","_id" : "lfKkRoQB18W7DskkwOaq","_version" : 4,"result" : "deleted","_shards" : {"total" : 2,"successful" : 1,"failed" : 0},"_seq_no" : 3,"_primary_term" : 1
}

第六节 Java 操作 ES

6.1 创建maven工程

<parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.3.2.RELEASE</version>
</parent>
<groupId>com.qf</groupId>
<artifactId>es</artifactId>
<version>1.0</version><properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!--spring-data提供的操作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-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>

6.2 编写实体类

@Data
//Document表示文档,文档的类型就是Article,文档中的字段就是Article中定义的字段
//indexName表示这个文档所在的索引,shards表示分片数量,replicas表示副本的数量
@Document(indexName = "article", shards = 10, replicas = 2)
public class Article {//Id表示这个字段就是确定这条数据的@Id//Field表示字段的定义,name表示在文档中存储的名字,type表示字段类型@Field(name = "id", type = FieldType.Long)private Long id;//Field表示字段的定义,analyzer表示存储时使用的分词器,searchAnalyzer表示查询时使用的分词器@Field(name = "title", type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")private String title;//Field表示字段的定义,FieldType.Keyword表示这个字段就是个关键词,不需要再分词,//index表示是否建立索引,默认为true,不需要建立索引时需要显示指定@Field(name = "content", type = FieldType.Keyword, index = false)private String content;@Field(name = "author", type = FieldType.Text)private String author;@Field(name = "author_name", type = FieldType.Text)private String authorName;//日期格式网址说明// https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html@Field(name = "publish_date", type = FieldType.Date, format = DateFormat.year_month_day)private Date publishDate;@Field(name = "category", type=FieldType.Text)private String category;@Field(name = "publishType", type = FieldType.Text)private String publishType; //发布形式:全部可见,部门可见@Field(name = "level", type = FieldType.Text)private String level; //文章等级: 初级、中级、高级
}

6.3 索引操作

6.3.1 编写测试类
@SpringBootTest
class IndexTest {@Autowiredprivate ElasticsearchRestTemplate restTemplate;
}
6.3.2 创建索引
@Test
public void createIndex(){//获取一个索引操作对象,如果索引不存在,则直接创建IndexOperations indexOperations = restTemplate.indexOps(Article.class);System.out.println(indexOperations);
}
6.3.3 删除索引
@Test
public void deleteIndex(){//获取一个索引操作对象IndexOperations indexOperations = restTemplate.indexOps(Article.class);indexOperations.delete();
}

6.4 文档操作

6.4.1 编写Repository
//ElasticsearchRepository  继承所有父接口的特性
//PagingAndSortingRepository 分页操作
//CrudRepository 基本的CRUD操作
@Repository
public interface ArticleRepository extends ElasticsearchRepository<Article, Long> {
}
6.4.2 编写测试类
@SpringBootTest
class DocumentTest {@Autowiredprivate ArticleRepository articleRepository;
}
6.4.3 添加文档
@Test
public void addArticle(){Article article = new Article();article.setId(1L);article.setAuthor("admin");article.setContent("这是一篇很有意思的文章");article.setCreatedDate(new Date());article.setTitle("Java是一门很简单的语言");articleRepository.save(article);
}
6.4.4 修改文档
@Test
public void updateArticle(){Article article = new Article();article.setId(1L);article.setAuthor("admin");article.setContent("这是一篇很有意思的文章");article.setCreatedDate(new Date());article.setTitle("Java很牛逼");articleRepository.save(article);
}
6.4.5 查询文档
@Test
public void searchArticle(){Optional<Article> opt = articleRepository.findById(1L);Article article = opt.orElse(null);System.out.println(article);
}
6.4.6 删除文档
@Test
public void deleteArticle(){articleRepository.deleteById(1L);
}
6.4.7 批量保存
@Test
public void batchAddArticle(){List<Article> articles = new ArrayList<>();for(int i=0; i<100; i++){Article article = new Article();article.setId((long) (i+2));article.setAuthor("author"+i);article.setContent("这是一篇很有意思的文章" + i);article.setCreatedDate(new Date());article.setTitle("Java很牛逼" + i);articles.add(article);}articleRepository.saveAll(articles);
}
6.4.8 分页查询
@Test
public void pageSearch(){//查询构建器,这里用的匹配所有的查询构建器QueryBuilder builder = new MatchAllQueryBuilder();//分页对象Pageable pageable = PageRequest.of(1, 20);//分页查询Page<Article> articlePage = articleRepository.search(builder, pageable);//获取总条数long total = articlePage.getTotalElements();System.out.println("总条数:" + total);//获取查询结果List<Article> articleList = articlePage.getContent();articleList.forEach(System.out::println);
}
6.4.9 排序查询
@Test
public void sortSearch(){Sort.Order dateOrder = Sort.Order.desc("createdDate"); //日期降序排列Sort.Order idOrder = Sort.Order.asc("id");//ID升序排列Iterable<Article> articles = articleRepository.findAll(Sort.by(dateOrder, idOrder));articles.forEach(System.out::println);
}
6.4.10 自定义查询

自定义查询的命名规则:方法名必须是get、find、read、query其中之一开始,后面接字段名以及条件,条件之间的组合使用and或者or, 方法参数必须与使用的字段一一匹配

@Repository
public interface ArticleRepository extends ElasticsearchRepository<Article, Long> {//文章标题模糊查询,并且ID在给定的范围之内List<Article> getByTitleLikeAndIdBetween(String title, Long min, Long max);
}

测试:

@Test
public void customerQuery(){Calendar c = Calendar.getInstance();c.roll(Calendar.DAY_OF_MONTH, -1);List<Article> articles = articleRepository.getByTitleLikeAndIdBetween("Java", 20L, 30L);articles.forEach(System.out::println);
}

6.5 ES查询操作[重点]

新建一个测试类

@SpringBootTest
class QueryTest {@Autowiredprivate RestHighLevelClient client;
}
6.5.1 term 查询

term的查询是代表完全匹配,这里的完全匹配指的是,查询的内容不会被分词,而是作为一个整体到存储的数据中去匹配,如果数据对应的字段有进行分词,那么只要其中任何一个分词结果与查询内同匹配,那么该数据将在查询结果中展示

查询语法:

# from表示开始的位置,size表示最大查询的条数, query表示查询的条件,term表示这里使用的是精确查找
# 精确查找的条件就是id字段的值为2, term中只能有一个字段作为条件
GET /article/_search
{"from": 0,"size": 20,"query": {"term": {"id": {"value": 2}}}
}

测试:

@Test
public void termQueryTest() throws IOException {//查询资源构建器SearchSourceBuilder builder = new SearchSourceBuilder();builder.from(0).size(20); //相当于 LIMIT 0, 20//term查询 = 精确查询builder.query(QueryBuilders.termQuery("id", 2L));//创建查询请求SearchRequest request = new SearchRequest("article");//将查询构建器放入请求中request.source(builder);//查询并得到结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//从结果中获取查询命中信息SearchHits searchHits = response.getHits();//获取总命中数long totalHits = searchHits.getTotalHits();System.out.println("查询命中条数:" + totalHits);//获取所有的命中数据SearchHit[] hits = searchHits.getHits();//获取每一条被命中的信息for(SearchHit hit: hits){//将命中的数据转换为一个mapMap<String, Object> rowData = hit.getSourceAsMap();System.out.println(rowData);}client.close();
}
6.5.2 terms 查询

terms查询与term查询的原理是一样的,只是terms查询针对的是一个字段可能对应多个值的情况,相当于 MySQL 中的条件in

# 查询id在2,3,4,5中的数据
POST /article/_search
{"from": 0,"size": 20,"query": {"terms": {"id": [2, 3, 4, 5]}}
}

测试:

@Test
public void termsQueryTest() throws IOException {//查询资源构建器SearchSourceBuilder builder = new SearchSourceBuilder();builder.from(0).size(20); //相当于 LIMIT 0, 20//terms查询 = 相当于MySQL中的INbuilder.query(QueryBuilders.termsQuery("id", Arrays.asList(2,3,4,5)));//创建查询请求SearchRequest request = new SearchRequest("article");//将查询构建器放入请求中request.source(builder);//查询并得到结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//从结果中获取查询命中信息SearchHits searchHits = response.getHits();//获取总命中数long totalHits = searchHits.getTotalHits();System.out.println("查询命中条数:" + totalHits);//获取所有的命中数据SearchHit[] hits = searchHits.getHits();//获取每一条被命中的信息for(SearchHit hit: hits){//将命中的数据转换为一个mapMap<String, Object> rowData = hit.getSourceAsMap();System.out.println(rowData);}client.close();
}
6.5.3 match查询[重点]

match查询属于高层查询,会根据查询的字段类型不一样,采用不同的查询方式。

  • 查询的是日期或者是数值的话,会将你基于的字符串查询内容转换为日期或者数值对待。
  • 如果查询的内容是一个不能被分词的内容(keyword),match查询不会对你指定的查询关键字进行分词。
  • 如果查询的内容是一个可以被分词的内容(text),match会将你指定的查询内容根据一定的方式去分词,去分词库中匹配指定的内容。

match查询,实际底层就是多个term查询,将多个term查询的结果给你封装到了一起

6.5.3.1 查询日期
GET /article/_search
{"size": 500, "query": {"match": {"publish_date": "2022-11-07"}}
}

测试:

@Test
public void date_matchTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//这里的日期格式自动转换为日期匹配builder.query(QueryBuilders.matchQuery("publish_date","2022-11-07"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.3.2 查询内容分词

如果查询的字段使用了分词,那么查询的内容也将分词。只要数据匹配其中的一个分词内容即可。

GET /article/_search
{"size": 500, "query": {"match": {"title": "中国人民"}}
}

测试:

@Test
public void analyzer_matchTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//这里的title使用的查询内容是"中国人民",这里会进行分词,分词之后再匹配builder.query(QueryBuilders.matchQuery("title","中国人民"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.3.2 查询内容不分词

如果查询的字段没有使用分词,那么查询的内容就不会分词。

GET /article/_search
{"size": 500, "query": {"match": {"author_name": "张三丰"}}
}

测试:

@Test
public void none_analyzer_matchTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//这里的author_name使用的查询内容是"张三丰",这里会进行分词,分词之后再匹配builder.query(QueryBuilders.matchQuery("author_name","张三丰"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.3.3 布尔 match 查询

基于一个Field匹配的内容,采用and或者or的方式连接

# 这里的"中华共和"会被分词,分为"中华"和"共和",这两个词用 and 衔接,表示要同时匹配上
GET /article/_search
{"size": 500, "query": {"match": {"title": {"query": "中华共和","operator": "and"}}}
}

测试:

@Test
public void bool_analyzer_matchTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//这里的title使用的查询内容是"中华共和",这里会进行分词,分词之后的所有分词结果必须要全部匹配builder.query(QueryBuilders.matchQuery("title","中华共和").operator(Operator.AND));//默认衔接操作就是OR//        builder.query(QueryBuilders.matchQuery("title","中国人民").operator(Operator.OR));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.3.4 multi_match 查询

查询内容可以与多个字段匹配的就需要使用 multi_match查询

GET /article/_search
{"size": 500, "query": {"multi_match": {"query": "中国","fields": ["category", "title"]}}
}

测试:

@Test
public void multi_matchTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//这里的"中国"既可以与title匹配,也可以与category匹配,任意满足即可builder.query(QueryBuilders.multiMatchQuery("中国","title", "category"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.4 其他查询
6.5.4.1 prefix 查询
GET /article/_search
{"size": 500, "query": {"prefix": {"content": "测试1"}}
}

测试:

@Test
public void prefixQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//content字段内容以"测试1"开始builder.query(QueryBuilders.prefixQuery("content","测试1"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.4.2 fuzzy查询

在实际的搜索中,我们有时候会打错字,从而导致搜索不到。在 Elastic Search 中,我们可以使用 fuzziness 属性来进行模糊查询,从而达到搜索有错别字的情形。fuzziness 表示编辑距离,编辑距离是对两个字符串差异长度的量化,及一个字符至少需要处理多少次才能变成另一个字符,比如lucenelucece只差了一个字符他们的编辑距离是1。 编辑距离的值可以是0,1,2或者auto

GET /article/_search
{"size": 500, "query": {"fuzzy": {"title": {"value": "中华国","fuzziness": 2}}}
}

测试:

@Test
public void fuzzyQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//中华国变化两次可以与title的内容匹配builder.query(QueryBuilders.fuzzyQuery("title","中华国").fuzziness(Fuzziness.TWO));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.4.3 wildcard 查询

通配查询,和 MySQL 中的like是一个套路,可以在查询时,在字符串中指定通配符*和占位符?

GET /article/_search
{"size": 500, "query": {"wildcard": {"title": {"value": "中华*"}}}
}

测试:

@Test
public void wildcardQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//title的内容需要以中华开始builder.query(QueryBuilders.wildcardQuery("title","中华*"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.4.4 range查询

范围查询,只针对数值类型,对某一个Field进行大于或者小于的范围指定。需要注意的是,ES中的存储比较都是按照字符串顺序来的

GET /article/_search
{"size": 500, "query": {"range": {"id": {"gte": 100,"lte": 50}}}
}

测试:

@Test
public void rangeQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);//id的字符串顺序在100~50builder.query(QueryBuilders.rangeQuery("id").gte(100).lte(50));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.5 复合查询[重点]
6.5.5.1 bool 查询

复合过滤器,将多个查询条件,以一定的逻辑组合在一起。

  • must: 所有的条件,用must组合在一起,表示And的意思
  • must_not:将must_not中的条件,全部都不能匹配,表示Not的意思
  • should:所有的条件,用should组合在一起,表示Or的意思
GET /article/_search
{"size": 500, "query": {"bool": {"should": [{"term": {"author_name": {"value": "张三"}}},{"term": {"category": {"value": "中国文化"}}}],"must_not": [{"match": {"category": "黄赌毒"}}],"must": [{"range": {"publish_date": {"gte": "2022-11-06","lte": "2022-11-08"}}}]}}
}

测试

@Test
public void boolQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();boolQueryBuilder.should(QueryBuilders.termQuery("author_name", "张三"));boolQueryBuilder.should(QueryBuilders.termQuery("category", "中国文化"));boolQueryBuilder.mustNot(QueryBuilders.matchQuery("category", "黄赌毒"));boolQueryBuilder.must(QueryBuilders.rangeQuery("publish_date").gte("2022-11-06").lte("2022-10-08"));builder.query(boolQueryBuilder);request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 输出结果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}
}
6.5.5.2 boosting 查询

boosting查询可以帮助我们去影响查询后的score。

  • positive:只有匹配上positive的查询的内容,才会被放到返回的结果集中。
  • negative:如果匹配上和positive并且也匹配上了negative,就可以降低这样的文档score。
  • negative_boost:指定系数,必须小于1.0

关于查询时,分数是如何计算的:

  • 搜索的关键字在文档中出现的频次越高,分数就越高
  • 指定的文档内容越短,分数就越高
  • 我们在搜索时,指定的关键字也会被分词,这个被分词的内容,被分词库匹配的个数越多,分数越高
GET /article/_search
{"size": 500, "query": {"boosting": {"positive": {"match": {"title": "中国共和"}},"negative": {"match": {"level": "高级"}},"negative_boost": 0.4}}
}

测试:

@Test
public void boostingQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);BoostingQueryBuilder boostingQueryBuilder = QueryBuilders.boostingQuery(QueryBuilders.matchQuery("title", "中华共和"),QueryBuilders.matchQuery("level", "高级")).negativeBoost(0.4f);builder.query(boostingQueryBuilder);request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);SearchHits hits = resp.getHits();//4. 输出结果for (SearchHit hit : hits.getHits()) {float score = hit.getScore();Map<String, Object> map = hit.getSourceAsMap();System.out.println(map + " => " + score);}
}
6.5.6 filter 查询[重点]

query,根据你的查询条件,去计算文档的匹配度得到一个分数,并且根据分数进行排序,不会做缓存的。

filter,根据你的查询条件去查询文档,不去计算分数,而且filter会对经常被过滤的数据进行缓存。

GET /article/_search
{"size": 500, "query": {"bool": {"filter": [{"term": {"title": "中国"}},{"range": {"publish_date": {"gte": "2022-11-06","lte": "2022-11-08"}}}]}}
}

测试:

@Test
public void filterQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();boolQueryBuilder.filter(QueryBuilders.termQuery("title", "中国"));boolQueryBuilder.filter(QueryBuilders.rangeQuery("publish_date").gte("2022-11-06").lt("2022-11-08"));builder.query(boolQueryBuilder);request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);SearchHits hits = resp.getHits();//4. 输出结果for (SearchHit hit : hits.getHits()) {float score = hit.getScore();Map<String, Object> map = hit.getSourceAsMap();System.out.println(map + " => " + score);}
}
6.5.7 高亮查询[重点]

高亮查询就是用户输入的关键字,以一定的特殊样式展示给用户,让用户知道为什么这个结果被检索出来。

高亮展示的数据,本身就是文档中的一个Field,单独将Field以highlight的形式返回给你。

ES提供了一个highlight属性,和query同级别的。

  • fragment_size:指定高亮数据展示多少个字符回来。
  • pre_tags:指定前缀标签,举个例子< font color=“red” >
  • post_tags:指定后缀标签,举个例子< /font >
  • fields:指定哪几个Field以高亮形式返回
GET /article/_search
{"size": 500, "query": {"bool": {"filter": [{"term": {"title": "中国"}},{"range": {"publish_date": {"gte": "2022-11-06","lte": "2022-11-08"}}}]}},"highlight": {"fields": {"title": {}}, "pre_tags": "<font color='red'>","post_tags": "</font>"}
}

测试:

@Test
public void highlightQueryTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(500);BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();boolQueryBuilder.filter(QueryBuilders.termQuery("title", "中国"));boolQueryBuilder.filter(QueryBuilders.rangeQuery("publish_date").gte("2022-11-06").lt("2022-11-08"));builder.query(boolQueryBuilder);HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field("title").preTags("<font color='red'>").postTags("</font>");builder.highlighter(highlightBuilder);request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);SearchHits hits = resp.getHits();//4. 输出结果for (SearchHit hit : hits.getHits()) {HighlightField highlightField = hit.getHighlightFields().get("title");String fieldName = highlightField.getName();Text[] fragments = highlightField.getFragments();System.out.println(fieldName + " => "+ Arrays.stream(fragments).map(Text::string).collect(Collectors.joining(",")));}
}
6.5.8 聚合查询[重点]

ES的聚合查询和 MySQL 的聚合查询类似,ES的聚合查询相比 MySQL 要强大的多,ES提供的统计数据的方式多种多样。

# ES聚合查询的RESTful语法
POST /index/type/_search
{"aggs": {"名字(agg)": {"agg_type": {"属性": "值"}}}
}
6.5.8.1 去重计数查询
GET /article/_search
{"aggs": {"authorNameAgg": {"cardinality": {"field": "author_name"}}}
}

测试:

@Test
public void cardinalityTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.aggregation(AggregationBuilders.cardinality("authorNameAgg").field("author_name"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);Aggregations aggregations = resp.getAggregations();Cardinality cardinality = aggregations.get("authorNameAgg");String name = cardinality.getName();long value = cardinality.getValue();System.out.println(name + "=" + value);
}
6.5.8.2 范围统计

统计一定范围内出现的文档个数,比如,针对某一个Field的值在 0100,100200,200~300之间文档出现的个数分别是多少。

范围统计可以针对普通的数值,针对时间类型,针对ip类型都可以做相应的统计。

range,date_range,ip_range

GET /article/_search
{"aggs": {"publishDateAgg": {"range": {"field": "publish_date","ranges": [{"from": "2022-11-01", "to": "2022-11-10"},{"from": "2022-11-11", "to": "2022-11-20"},{"from": "2022-11-21", "to": "2022-11-30"}]}}}
}

测试:

@Test
public void rangeAggregationTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.aggregation(AggregationBuilders.dateRange("publishDateAgg").field("publish_date").addRange("2022-11-01", "2022-11-10").addRange("2022-11-11", "2022-11-20").addRange("2022-11-21", "2022-11-30"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);Aggregations aggregations = resp.getAggregations();Range range = aggregations.get("publishDateAgg");List<? extends Range.Bucket> buckets = range.getBuckets();buckets.forEach(bucket -> {String key = bucket.getKeyAsString();long docCount = bucket.getDocCount();System.out.println(key + " => " + docCount);});
}
6.5.8.2 统计聚合查询

可以查询指定Field的最大值,最小值,平均值,平方和等

GET /article/_search
{"aggs": {"agg": {"extended_stats": {"field": "publish_date"}}}
}

测试:

@Test
public void extended_statsAggregationTest() throws IOException {//1. 创建RequestSearchRequest request = new SearchRequest("article");//2. 指定查询条件SearchSourceBuilder builder = new SearchSourceBuilder();builder.aggregation(AggregationBuilders.extendedStats("agg").field("publish_date"));request.source(builder);//3. 执行查询SearchResponse resp = client.search(request, RequestOptions.DEFAULT);Aggregations aggregations = resp.getAggregations();ExtendedStats extendedStats = aggregations.get("agg");String minAsString = extendedStats.getMinAsString();String maxAsString = extendedStats.getMaxAsString();String avgAsString = extendedStats.getAvgAsString();System.out.println(minAsString);System.out.println(maxAsString);System.out.println(avgAsString);
}

第七节 倒排索引

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将存放的数据,以一定的方式进行分词,并且将分词的内容存放到一个单独的分词库中。

当用户去查询数据时,会将用户的查询关键字进行分词。

然后去分词库中匹配内容,最终得到数据的id标识。

根据id标识去存放数据的位置拉取到指定的数据。


http://www.ppmy.cn/devtools/100625.html

相关文章

Spark MLlib 特征工程系列—特征转换N-Gram

Spark MLlib 特征工程系列—特征转换N-Gram 在Spark中,NGram 是一个特征转换器,用于从输入的词汇序列生成n-gram特征。n-gram 是一种常用的文本处理方法,指的是由 n 个连续的词组成的序列。n-gram 模型在自然语言处理(NLP)任务中被广泛用于捕捉词语之间的关系。 N-Gram …

如何高质量将PDF拆分多个文件?这6款工具超好用

在工作和学习中&#xff0c;PDF文件的使用非常广泛。无论是合同、报告、论文&#xff0c;还是其他文档&#xff0c;PDF格式因其稳定性和兼容性&#xff0c;已成为分享和存储信息的首选格式。然而&#xff0c;有时我们需要从一个PDF文件中提取特定页面&#xff0c;或将其拆分成多…

ClickHouse与Elasticsearch:大数据时代的两大引擎比较

目录 1. 基本介绍 ClickHouse Elasticsearch 2. 优劣势分析 ClickHouse的优势 ClickHouse的劣势 Elasticsearch的优势 Elasticsearch的劣势 3. 应用案例 4. 总结与选择建议 随着大数据技术的不断发展&#xff0c;企业对数据分析和实时搜索的需求也日益增长。ClickH…

设计模式反模式:UML图示常见误用案例分析

设计模式反模式&#xff1a;UML图示常见误用案例分析 在软件开发过程中&#xff0c;设计模式&#xff08;Design Patterns&#xff09;作为解决常见设计问题的最佳实践&#xff0c;被广泛地应用于提高代码质量和可维护性。然而&#xff0c;当这些设计模式被误用或滥用时&#…

基于Ubuntu22.04 安装SSH服务

安全外壳协议&#xff08;Secure Shell&#xff0c;简称 SSH&#xff09;是一种在不安全网络上用于安全远程登录和其他安全网络服务的协议。 SSH 由 IETF 的网络小组&#xff08;Network Working Group&#xff09;所制定&#xff0c;SSH 为建立在应用层基础上的安全协议。SSH…

速盾的高防 IP 和 CDN 服务有哪些优势?

速盾的高防 IP 和 CDN 服务具有如下优势&#xff1a; 高可靠性&#xff1a;速盾的高防 IP 和 CDN 服务采用分布式架构和全球多节点部署&#xff0c;能够实时监控和管理流量分发&#xff0c;确保网络的稳定性和可靠性。多节点的部署能够在发生故障或攻击时自动切换到其他节点&am…

Swift 可选链

Swift 可选链 Swift 是一种强类型、编译式的编程语言,由苹果公司开发,用于iOS、macOS、watchOS和tvOS应用程序的开发。Swift 强调安全性和性能,同时也提供了许多现代编程语言的特性。其中,可选链(Optional Chaining)是 Swift 中处理可选类型的一种机制,它允许我们以一种…

抖音小红书爆款预定,Tiktok爆火的短视频玩法,Ai生成宝宝走秀视频,萌翻全场

大家好&#xff0c;我是方知有&#xff0c;每天分享一个互联网副业&#xff0c;喜欢的朋友可以关注~ 今天给大家分享在Tiktok爆火的短视频玩法&#xff0c;现在抖音小红书制作这类型视频的人数还不多&#xff0c;大家可以赶快操作起来&#xff0c;这个玩法就是用Ai生成宝宝走秀…

Eureka的生命周期管理:服务注册、续约与下线的完整流程解析

Eureka的生命周期管理&#xff1a;服务注册、续约与下线的完整流程解析 引言 在分布式系统中&#xff0c;服务发现是微服务架构的核心问题之一。Eureka是Netflix开源的一个服务发现框架&#xff0c;它能够有效地管理微服务的生命周期&#xff0c;包括服务注册、续约和下线。这…

Linux驱动学习之点灯(四,linux2.6)

上篇最后的第二种点灯方法年代比较久远&#xff0c;register_chrdev&#xff08;&#xff09;这个函数一下申请了255个设备号&#xff0c;不建议使用 如下图 下图的函数在linux2.6里是上图函数的升级版&#xff0c;不过他是静态分配&#xff0c;后续还得添加到cdev里 从上图函…

pytorch 参数冻结 parameter-efficient fine-tuning

目标&#xff1a;在网络中冻结部分参数进行高效训练 框架&#xff1a;pytorch &#xff08;version 1.11.0) 基本实现&#xff1a; 需要学习的参数requires_grad设置为True&#xff0c;冻结的设置为False需要学习的参数要加到 optimizer的List中&#xff1b;对于冻结的参数&…

JavaScript 模块化开发:ES6 模块与 CommonJS 的对比与应用

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 随着前端项目规模的增长&#xff0c;代码组织和管理变得越来越复杂。模块化开发成为解决这一问题的有效手段&#xff0c;能够帮助开发者将代码进行分割、复用和维护。JavaScript 在发展过程中出现了多种模块化规范&…

基于x86 平台opencv的图像采集和seetaface6的人脸跟踪功能

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 配置资源文件3.3 验证功能一、概述 本文档是针对x86 平台opencv的图像采集和seetaface6的人脸跟踪功能,opencv通过摄像头采集视频图像,将采集的视频图像送给seetaface6的人脸跟踪模块从而实现…

string类的使用与实现

标准库中的string类 string类(了解) string类的文档介绍 注意&#xff1a;在使用string类时&#xff0c;必须包含#include头文件以及using namespace std; auto和范围for 在了解string的用法前在学习一个知识&#xff1b; auto关键字 auto是作为一个新的类型指示符来指示编译器…

反转链表 II 简单链表问题

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a;[1,4,3,2…

OpenAI 将向企业开放 GPT-4o 模型定制版

OpenAI 最近发布了一项新功能&#xff0c;使企业客户可以通过微调技术定制 GPT-4o 模型&#xff0c;从而应对日益激烈的人工智能竞争&#xff0c;并展示其投资回报。这一新功能的推出使得企业能够使用自己的数据对 GPT-4o 模型进行个性化调整&#xff0c;以满足他们的特定需求和…

Linux云计算 |【第二阶段】SECURITY-DAY4

主要内容&#xff1a; Kali系统、扫描与抓包、Nginx安全加固、Linux基本防护 补充&#xff1a;使用Curl命令查看网页头部信息和页面内容 不加选项&#xff0c;默认查看网页的内容&#xff1b; [ -I ] 选项&#xff1a;访问服务器页面时&#xff0c;显示HTTP的头部信息&#xf…

git提交项目,报403无权限

这个在公司内网git上提交项目时&#xff0c;使用的是刚分配到的账号和密码。创建完组和项目后一切准备完毕了&#xff0c;但是在提交时缺出了乌龙&#xff0c;报403&#xff0c;上面一堆英文&#xff0c;大致的意思是说我没有上传本项目的权限&#xff0c;报错信息如下图所示&a…

盘古信息IMS MCM制造协同管理系统:为中小企业数字化转型量身打造的数字化方案

近年来&#xff0c;全球经济的不稳定性&#xff0c;给中小企业的经营和发展带来了巨大的挑战。为提升企业竞争力&#xff0c;中小企业纷纷谋求数字化转型路径&#xff0c;优化生产流程、提高运营效率、降低生产成本&#xff0c;以应对变幻莫测的市场环境。IMS MCM是盘古信息为广…

浅谈Java Spring Boot

一、基本介绍 Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。通过这种方式&#xff0c;Spring Boot致力于在蓬勃发展…