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使用了倒排索引数据结构,它将文档内容切分成一个个的词。对于每个分词后的词,记录其在哪些文档中出现以及出现的位置信息,能够快速找到包含他的文档。

es检索高亮标红_搜索引擎


es检索高亮标红_大数据_02


es检索高亮标红_搜索_03

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/shopping
6.3 删除索引库
DELETE 名字
6.4 修改索引库
先删除再添加就可以了

7. 文档的CRUD

7.1 添加文档
PUT index/type/id
{
	JSON,文档内容
}
//--解释---------------------------------------

PUT 索引库/文档类型/文档id
{
	JSON格式,文档原始数据,例如:
	"id":11,
	"username":"zs",
}

// 注意:没有指定文档ID,ES会自动生成ID
7.2 获取文档
// 获取指定文档 
GET 索引库/类型/文档ID 

// 指定返回的列 
GET /pethome/_doc/1?_source=name,email

// 只要内容不要元数据 
GET /pethome/_doc/1/_source
7.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/id

8. 文档简单查询

8.1 基本查询
// 查询所有
GET _search

// 查询指定索引库
GET pethome/_search

// 查询指定类型
GET pethome/_doc/_search

// 查询指定文档
GET pethome/_doc/1
8.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. 查询方式

我们接触了 matchrange 等查询方式(查询对象),在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:9200
14.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);
    }