ElasticSearch全文搜索引擎
- ElasticSearch全文搜索引擎
- 1. 什么是ElasticSearch(简称ES)?
- 2. 为什么要使用ES?
- 3. ES的特点
- 4. ES和lucene的区别
- 5. ES与MySql对比
- 6. 索引库CRUD
- 6.1 增加索引库
- 6.2 查询索引库
- 6.3 删除索引库
- 6.4 修改索引库
- 7. 文档的CRUD
- 7.1 添加文档
- 7.2 获取文档
- 7.3 修改文档
- 7.4 删除文档
- 8. 文档简单查询
- 8.1 基本查询
- 8.2 分页查询
- 8.3 字符串查询
- 8.4 批量查询
- 9. DSL查询
- 9.1 什么是DSL查询?
- 9.2 DSL查询语法
- 10. DSL过滤
- 10.1 什么是DSL过滤?
- 10.2 查询与过滤的区别?
- 10.3 DSL查询+过滤语法
- 11. 查询方式
- 11.1 全匹配(match_all)
- 11.2 标准查询(match和multi_match)
- 11.3 单词搜索与过滤(Term和Terms)
- 11. 4 组合条件搜索与过滤(Bool)
- 11. 5 范围查询与过滤(range)
- 11. 6 存在和缺失过滤器(exists和missing)
- 11. 7 前匹配搜索与过滤(prefix)
- 11. 8 通配符搜索(wildcard)
- 12. 文档类型映射
- 12.1 什么是文档映射?
- 12.2 默认的字段类型
- 12.3 映射规则
- 13. 添加映射
- 14. SpringBoot操作ES
- 14.1 导入pom依赖
- 14.2 在yml中对ES进行配置
- 14.3 编写启动类
- 15.4 创建Document对象
- 15.5 创建Repository
- 15.6 创建索引和映射
- 15.7 DLS查询+过滤
ElasticSearch全文搜索引擎
全文搜索引擎:就是把没有结构的数据,转换为有结构的数据,以此来加快对文本的查找速度,通常而言有结构的数据的查询是很快的,比如:有序数组,红黑树。
1. 什么是ElasticSearch(简称ES)?
ES是一个开源的高性能的分布式全文搜索引擎,在 Apache Lucene(可以理解为就是一堆的jar包)的基础上进行了封装,使其操作更加简单,并且提供了RESTFul API(GET http://…)来进行数据索引、搜索和分析操作。
2. 为什么要使用ES?
案例:在student表中查询学生名字包含‘果’的所有记录
sql语句:select * from student where name like '%果%' 使用like '%果%'会造成索引失效,从而无法对数据快速查找,需要进行全表扫描,当数据量特别大时,效率就会非常低。
这时候就需要用到ES了,ES使用了倒排索引数据结构,它将文档内容切分成一个个的词。对于每个分词后的词,记录其在哪些文档中出现以及出现的位置信息,能够快速找到包含他的文档。



3. ES的特点
- 分布式的实时文件存储
- 分布式全文搜索引擎,每个字段都被索引并可被搜索
- 能在分布式项目/集群中使用
- 本身支持集群扩展,可以扩展到上百台服务器
- 处理PB级结构化或非结构化数据
- 简单的 RESTful API通信方式
- 支持各种语言的客户端
- 基于Lucene封装,使操作简单
4. ES和lucene的区别
- lucene只是一个基于java的全文搜索引擎库,提供了底层的索引和搜索功能。
- 而ES则是建立在 Lucene 之上的分布式搜索和分析引擎,提供了更丰富的功能和服务,带来了集群管理、分布式搜索、复制、故障恢复等额外功能,而且Elasticsearch 提供了基于 HTTP 的 RESTful API,具有跨语言性,使得与 ES 进行交互变得简单和方便。
5. ES与MySql对比
ElastciSearch全文搜索 | Mysql关系型数据库 |
索引库(index) | 数据库(database) |
文档类型(Type) | 数据表(Table) |
文档(Document) | 一行数据(Row) |
字段(field) | 一个列(column) |
文档ID | 主键ID |
查询(Query DSL) | 查询(SQL) |
GET http://… | SELECT * FROM … |
PUT http:// | UPDATE table set… |
注意:在ES7版本之后,文档类型(Type)这个概念逐渐废弃,而是使用_doc代替使用。
6. 索引库CRUD
6.1 增加索引库
创建一个名字为 shopping的索引库,5个Master Shard分片,每个Master Shard分片有1个Replica Shard从分片。
PUT shopping
{
"settings":{
"number_of_shards":5,
"number_of_replicas":1
}
}6.2 查询索引库
// 查询所有索引库
GET _cat/indices?v
// 查看指定索引库
GET _cat/indices/shopping6.3 删除索引库
DELETE 名字6.4 修改索引库
先删除再添加就可以了7. 文档的CRUD
7.1 添加文档
PUT index/type/id
{
JSON,文档内容
}
//--解释---------------------------------------
PUT 索引库/文档类型/文档id
{
JSON格式,文档原始数据,例如:
"id":11,
"username":"zs",
}
// 注意:没有指定文档ID,ES会自动生成ID7.2 获取文档
// 获取指定文档
GET 索引库/类型/文档ID
// 指定返回的列
GET /pethome/_doc/1?_source=name,email
// 只要内容不要元数据
GET /pethome/_doc/1/_source7.3 修改文档
// 原数据:
PUT /pethome/_doc/1
{
"id":11,
"username":"zs",
"age":18
}
// 整体修改
PUT /pethome/_doc/1
{
"id":11,
"username":"zs"
}
// 注意:文档修改过程:1.标记删除旧文档,2.添加新文档,这种方式会把ES中的数据全部覆盖,即age字段会消失。
// 局部修改
POST /pethome/_doc/1/_update
{
"doc":{
"id" : 11,
"username": "xx"
}
}
// 注意:POST方法修改只会修改id,和username字段,age字段不会作任何改变。7.4 删除文档
DELETE index/type/id8. 文档简单查询
8.1 基本查询
// 查询所有
GET _search
// 查询指定索引库
GET pethome/_search
// 查询指定类型
GET pethome/_doc/_search
// 查询指定文档
GET pethome/_doc/18.2 分页查询
&size=2&from=2
// size: 每页条数
// form:从多少条数据开始查8.3 字符串查询
GET pethome/_doc/_search?q=age:17&size=2&from=2&sort=id:desc&_source=id,username
// 条件查询+分页+排序
// 这种方式不推荐,因为条件过多,拼接起来比较麻烦8.4 批量查询
// 不同索引库查询
GET _mget
{
"docs" : [
{
"_index" : "itsource",
"_type" : "blog",
"_id" : 2
},
{
"_index" : "itsource",
"_type" : "employee",
"_id" : 1,
"_source": "email,age"
}
]
}
// 同索引库同类型 - 推荐
GET pethome/blog/_mget
{
"ids" : [ "2", "1" ]
}9. DSL查询
9.1 什么是DSL查询?
DSL查询是由ES提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。DSL有两部分组成:DSL查询和DSL过滤。
9.2 DSL查询语法
GET /pethome/_doc/_search
{
"query": { //相等与MySQL中的where
"match_all": {} //match_all 表示查询所有数据
},
"from": 0, //从多少条数据开始查
"size": 2, //每页条数
"_source": ["username", "age", "id"], //查询返回fullName,age和email几个列
"sort": [{"join_date": "desc"},{"age": "asc"}] // 排序规则
}案例:
// 1. match如同:where username = hello or username = java
GET /pethome/_doc/_search
{
"query" : {
"match" : {
"username" : "Hello Java"
}
}
}
// 2. term如同:where username = "Hello Java"
GET /pethome/_doc/_search
{
"query" : {
"term" : {
"username" : "Hello Java"
}
}
}match指的是“标准查询”,该查询方式会对查询的内容进行分词,DSL查询可以支持的查询方式很多,如term词元查询 ,range范围查询等等。
10. DSL过滤
10.1 什么是DSL过滤?
DSL过滤语句和DSL查询语句非常相似,但是它们的使用目的却不同:DSL过滤文档的方式更像是对于我的条件"有"或者"没有"(等于 ;不等于),而DSL查询语句则像是"有多像"(模糊查询)。
10.2 查询与过滤的区别?
需要模糊查询的使用DSL查询 ,需要精确查询的使用DSL过滤。在开发中组合使用(组合查询) ,关键字查询使用DSL查询,其他的都是用DSL过滤。
10.3 DSL查询+过滤语法
GET /pethome/_doc/_search
{
"query": { // 查询,所有的查询条件在query里面
"bool": { // 组合搜索bool可以组合多个查询条件为一个查询对象
"must": [{ //与(must) 或(should) 非(must not)
"match": { //match : 分词匹配查询
"username": "zs"
},
}],
"filter": [
{
"range":{ //范围查询
"age":{
"gte":18,
"lte":20
}
}
},
{ // 过滤条件
"term": { // 词元查询,不会对查询条件分词
"name": "zs ls"
}
}]
}
},
"from": 20,
"size": 10,
"_source": ["name", "age", "username"],
"sort": [{
"join_date": "desc"
}, {
"age": "asc"
}]
}11. 查询方式
我们接触了 match , range 等查询方式(查询对象),在ES还有很多其他的查询方式,在不同的场景中我们需要根据情况进行合理的选择。
11.1 全匹配(match_all)
// 普通搜索(匹配所有文档)
GET _search
{
"query": {
"must": {
"match_all": {}
},
}
}11.2 标准查询(match和multi_match)
// 进行分词查询
GET _search
{
"query": {
"match": {
"fullName": "Steven King"
}
}
}
// multi_match 查询允许你做 match查询的基础上同时搜索多个字段
{
"query": {
"multi_match": {
"query": "Steven King",
"fields": ["name", "title"]
}
}
}11.3 单词搜索与过滤(Term和Terms)
{
"query": {
"bool": {
"must": {
"match_all": {
}
},
"filter": {
"term": { // 单词/词元查询
"username": "Steven King"
}
}
}
}
}
// 上面的“Steven King”会被当成一个整体去检索ES库
{
"query": {
"terms": { // Terms支持多个字段查询
"tags": [
"jvm",
"hadoop",
"lucene"
],
"minimum_match": 1 // 至少匹配个数,默认为1
}
}
}11. 4 组合条件搜索与过滤(Bool)
合搜索bool可以组合多个查询条件为一个查询对象,查询条件包括must、should和must_not。
11. 5 范围查询与过滤(range)
range过滤允许我们按照指定范围查找一批数据
11. 6 存在和缺失过滤器(exists和missing)
exists和missing只能用于过滤结果。
11. 7 前匹配搜索与过滤(prefix)
和term查询相似,前匹配搜索不是精确匹配,而是类似于SQL中的like ‘key%’
11. 8 通配符搜索(wildcard)
使用*代表0~N个,使用?代表1个。
12. 文档类型映射
12.1 什么是文档映射?
ES的文档映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型。有些字段类型可以分词,有些字段类型不可以分词,所以对于字段的类型需要我们自己去指定。
创建流程对比:Mysql创建数据库 -> 创建表(指定字段类型) -> crud数据ES创建索引库 -> 文档类型映射 -> crud文档
12.2 默认的字段类型
- 基本字段类型
字符串 | text(分词) ; | keyword(不分词) ; | StringField(不分词文本); | TextFiled(要分词文本) | |
数字 | long | integer | short | double | float |
日期 | date | ||||
逻辑 | boolean |
- 复杂字段类型
对象类型 | object |
数组类型 | array |
地理位置 | geo_point,geo_shape |
12.3 映射规则
type | 类型:基本数据类型,integer,long,date,boolean,keyword,text… |
index | 索引模式:analyzed (索引并分词,text默认模式), not_analyzed (索引不分词,keyword默认模式),no(不索引) |
analyzer | 索引分词器:索引创建时使用的分词器,如ik_smart,ik_max_word,standard |
search_analyzer | 搜索分词器:搜索该字段的值时,传入的查询内容的分词器。 |
fields | 多字段索引:当对该字段需要使用多种索引模式时使用。如:城市搜索 |
13. 添加映射
// 1 创建新的索引库
put pethome
// 2 单类型创建映射 给pethome索引库中的goods类型创建映射
put pethome/goods/_mapping
{
"goods": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text",
"analyzer": "ik_smart", // 使用ik分词器
"search_analyzer": "ik_smart" // 使用ik分词器
}
}
}
}
// 3.多类型创建映射 同时给user和dept创建文档映射
PUT aigou
{
"mappings": {
"user": {
"properties": {
"id": {
"type": "integer"
},
"info": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
}
}
},
"dept": {
"properties": {
"id": {
"type": "integer"
},
// ....更多字段映射配置
}
}
}
}
// 4 数组/对象映射
//(1) 对象
{
"id" : 1,
"girl" : {
"name" : "王小花",
"age" : 22
}
}
// 文档映射
{
"properties": {
"id": {"type": "long"},
"girl": {
"properties":{
"name": {"type": "keyword"},
"age": {"type": "integer"}
}
}
}
}
//(2) 数组
{
"id" : 1,
"hobby" : ["王小花","林志玲"]
}
// 文档映射
{
"properties": {
"id": {"type": "long"},
"hobby": {"type": "keyword"}
}
}
//(3) 对象数组
{
"id" : 1,
"girl":[{"name":"林志玲","age":32},{"name":"赵丽颖","age":22}]
}
// 文档映射
"properties": {
"id": {
"type": "long"
},
"girl": {
"properties": {
"age": { "type": "long" },
"name": { "type": "text" }
}
}
}14. SpringBoot操作ES
14.1 导入pom依赖
<!--SpringBoot-->
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<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>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>14.2 在yml中对ES进行配置
spring:
elasticsearch:
rest:
uris:
- http://localhost:920014.3 编写启动类
@SpringBootApplication
public class ESApp{
public static void main(String[] args) {
SpringApplication.run(ESApp.class);
}
}15.4 创建Document对象
对存储到ES中的数据的封装以及实现文档映射。
//标记该对象是ES的文档对象
//indexName 索引库
//type 类型
@Document(indexName = "pethome",type = "_doc")
@Data
public class pethomeDoc {
//标记为文档ID,该ID的值会作为document的id值
@Id
private Long id;
/**
* 姓名进行分词,指定为text;并使用IK分词器
*/
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String name;
/**
* 年龄指定为Integer类型
*/
@Field(type = FieldType.Integer)
private int age;
/**
* 性别指定为 Integer类型
*/
@Field(type = FieldType.Integer)
private int sex;
/**
* 生日指定为 Date类型
*/
@Field(type = FieldType.Date)
private Date irthday;
}15.5 创建Repository
SpringBoot提供了ElasticsearchRepository来操作ES,该接口中包含了针对ES的CRUD方法。
@Service //需要交给spring进行管理
public interface HomeRepository extends ElasticsearchRepository<HomeDoc,Long> {
}15.6 创建索引和映射
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ESApp.class)
public class HomeESTest {
// 操作ES的template模板
@Autowired
private ElasticsearchRestTemplate template;
@Autowired
private HomeRepository homeRepository;
@Test
public void oneTest(){
// 创建索引
template.createIndex(HomeDoc.class);
// 创建映射
template.putMapping(HomeDoc.class);
}
// 添加数据
@Test
public void addTest(){
// HomeDoc homeDoc = new HomeDoc();
// for (int i = 0; i < 5; i++) {
// homeDoc.setId(0L + i);
// homeDoc.setName("张三"+i);
// homeDoc.setAge(i);
// homeDoc.setDateTime(new Date());
// homeDoc.setNumber(20200L+i);
// homeRepository.save(homeDoc);
// }
// 查询单条数据
Optional<HomeDoc> optional = homeRepository.findById(1L);
System.out.println(optional);
// 删除单条数据
homeRepository.deleteById(1L);
optional = homeRepository.findById(1L);
System.out.println(optional);
//...
}
}15.7 DLS查询+过滤
@Test
public void findTest(){
//查询构建器
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//构建分页
nativeSearchQueryBuilder.withPageable(PageRequest.of(0,3));
//构建排序
nativeSearchQueryBuilder.withSort(new FieldSortBuilder("number").order(SortOrder.DESC));
//构建组合查询
BoolQueryBuilder builder = new BoolQueryBuilder();
builder.must(new MatchQueryBuilder("name","张三"));
builder.filter(new RangeQueryBuilder("age").gte(1).lte(3));
nativeSearchQueryBuilder.withQuery(builder);
// 通过builder构建查询对象
Page<HomeDoc> page = homeRepository.search(nativeSearchQueryBuilder.build());
long totalElements = page.getTotalElements();
List<HomeDoc> list = page.getContent();
System.out.println("总条数:" + totalElements);
list.forEach(System.out::println);
}
















