1为什么使用es
ElasticSearch 搜索服务器。简称es。
初识es 搜索时数据库的问题
2.1是什么
搜索服务器 软件
2.1.1互联网搜索
https://www.baidu.com/
2.1.2站内搜索
https://www.taobao.com/
2.2为什么
不用es,可以使用数据库。
select * from xxx
问题出现:
问题一:效率极低:
SELECT * FROM goods WHERE title LIKE '%手机%';
如果使用模糊查询,左边有通配符,不会走索引,会全表扫描,性能低
问题二:不能分词
SELECT * FROM goods WHERE title LIKE ‘%华为手机%';
关系型数据库提供的查询,功能太弱
问题三:数据量大时,不能分库分表
单表数据量大(大于一亿),存储空间有限,分库分表需要借助在不程序实现。(mycat)
初识es 倒排索引
倒排索引:将各个文档中的内容,进行分词,形成词条。然后记录词条和数据的唯一标识(id)的对应关系,形成的产物。
初识es 存储、查询
存储:
json格式
es自动分词
查询:
1关键词 分词
2每个分词 去倒排索引表查询
3汇总,相关度评分,高的先返回
2es入门
es概念-了解
官网:https://www.elastic.co/
特点:
- ElasticSearch是一个基于Lucene的搜索服务器
- 是一个分布式、高扩展、高实时的搜索与数据分析引擎
- 基于RESTful web接口 http get post put delete
- Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种 流行的企业级搜索引擎
场景:
- 搜索:海量数据的查询
- 日志数据分析
- 实时数据分析
与mysql对比: nosql 面试:关系型数据库和非关系型数据库
-
MySQL有事务性,而ElasticSearch没有事务性,所以你删了的数据是无法恢复的。
-
ElasticSearch没有物理外键这个特性,,如果你的数据强一致性要求比较高,还是建议慎用
-
ElasticSearch和MySql分工不同,MySQL负责存储数据,ElasticSearch负责搜索数据。
es安装
运维人员:系统参数,用户,防火墙。
ELK
window 方便地改配置文件,不用起虚拟机。
http://localhost:9200/
es辅助插件安装
三选一:
1postman
2kibana elastic专门为es数据展现做的一个可视化工具 http://localhost:5601/
3head
es核心概念 nosql
索引(index)
ElasticSearch存储数据的地方,可以理解成关系型数据库中的数据库概念。
映射(mapping)
mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构。
文档(document)
Elasticsearch中的最小数据单元,常以json格式显示。一个document相当于关系型数据库中的一行数据。
倒排索引
一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,对应一个包含它的文档id列表。
类型(type) _doc
一种type就像一类表。如用户表、角色表等。在Elasticsearch7.X默认type为_doc
- ES 5.x中一个index可以有多种type。
- ES 6.x中一个index只能有一种type。
- ES 7.x以后,将逐步移除type这个概念,现在的操作已经不再使用,默认_doc
与mysql对比:
关系型数据库(比如Mysql) | 非关系型数据库(Elasticsearch) |
---|---|
数据库Database | 索引Index |
表Table | 索引Index(原为Type) |
数据行Row | 文档Document |
数据列Column | 字段Field |
约束 Schema | 映射Mapping |
操作es restful风格
REST(Representational State Transfer),表述性状态转移,是一组架构约束条件和原则。满足这些约束条件和 原则的应用程序或设计就是RESTful。就是一种定义接口的规范。
-
基于HTTP。
-
可以使用XML格式定义或JSON格式定义。
-
每一个URI代表1种资源。
-
客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:
GET:用来获取资源
POST:用来新建资源(也可以用于更新资源)
PUT:用来更新资源
DELETE:用来删除资源
对数据的增删改查,使用不同的http方式!
3es操作
操作es 索引 (建表) index
使用postman测试:
添加索引
PUT http://ip:端口/索引名称
查询索引
GET http://ip:端口/索引名称
GET http://ip:端口/索引名称1,索引名称2
GET http://ip:端口/_all
删除索引
DELETE http://ip:端口/索引名称
关闭索引
POST http://ip:端口/索引名称/_close
打开索引
POST http://ip:端口/索引名称/_open
操作es 映射 数据类型
11.1简单数据类型
-
字符串
text:会分词,不支持聚合
select color,count(1) from pruduct group by color. 1 藏青色 2斩男色
keyword:不会分词,将全部内容作为一个词条,支持聚合。邮编030600,身份证号1424123112131311,特有名词:大文娱
-
数值
age byte
price 12.35
口罩 5.28*100 528
小数——》分数 0.142857 1/7
-
布尔
boolean
-
二进制
binary
-
范围类型
integer_range, float_range, long_range, double_range, date_range
-
日期
date
11.2复杂数据类型
-
数组:[ ]
-
对象:{ }
12 操作es 映射
- 添加映射
PUT /person/_mapping
{
"properties":{
"name":{
"type":"text"
},
"age":{
"type":"integer"
}
}
}
- 创建索引并添加映射
PUT person
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "integer"
}
}
}
}
-
查询映射
GET person/_mapping
-
添加字段
PUT /person/_mapping
{
"properties":{
"address":{
"type":"text"
}
}
}
13 操作es 文档
-
添加文档,指定id
PUT /person/_doc/1 { "name" :"李四", "age" : 20, "address" :"太原" }
-
添加文档,不指定id
POST /person/_doc/ { "name" :"王五", "age" : 20, "address" :"武汉" }
-
查询指定id的文档
GET /person/_doc/1
-
修改文档
PUT /person/_doc/1 { "name" :"李四", "age" : 21, "address" :"太原" }
-
查询所有文档
GET /person/_search
-
删除指定id文档
DELETE /person/_doc/1
4IK分词器
14 分词器 介绍
1、分词器(Analyzer):将一段文本,按照一定逻辑,分析成多个词语的一种工具
2、ElasticSearch内置分词器:
- Standard Analyzer -默认分词器,按词切分,小写处理
- Simple Analyzer -按照非字母切分(符号被过滤),小写处理
- Stop Analyzer -小写处理,停用词过滤(the,a,is)
- Whitespace Analyzer -按照空格切分,不转小写
- Keyword Analyzer -不分词,直接将输入当作输出
- Patter Analyzer -正则表达式,默认\W+(非字符分割)
- Language -提供了30多种常见语言的分词器
3、ElasticSearch内置分词器对中文很不友好,处理方式为:一个字一个词
测试:
GET _analyze
{
"analyzer": "standard",
"text": ["我爱北京天安门"]
}
15 IK分词器安装
- IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。
- 具有60万字/秒的高速处理能力。
- 支持用户词典扩展定义。
具体安装步骤见《ik分词器安装.md》
16 ik分词器使用
ik_max_word
会将文本做最细粒度的拆分
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "乒乓球明年总冠军"
}
ik_smart
会做最粗粒度的拆分
GET /_analyze
{
"analyzer": "ik_smart",
"text": "乒乓球明年总冠军"
}
5文档操作
17 查询文档 使用IK
词条查询:term
1 词条查询不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配搜索
GET /person/_search
{
"query": {
"term": {
"address": {
"value": "太原"
}
}
}
}
**查不出来!**因为默认使用standard 分词器。
GET /person/_search
{
"query": {
"term": {
"address": {
"value": "太"
}
}
}
}
可以查出来。但不是我们想要的效果。
2 重新创建索引
PUT /person
{
"mappings": {
"properties": {
"name":{
"type": "keyword"
},
"address":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}
}
插入数据
PUT /person/_doc/1
{
"name" : "李四1",
"age" : 22,
"address" : "太原"
}
PUT /person/_doc/2
{
"name" : "李四2",
"age" : 23,
"address" : "西安"
}
PUT /person/_doc/3
{
"name" :"李四3",
"age" : 24,
"address" :"广东"
}
PUT /person/_doc/4
{
"name" :"李四4",
"age" : 44,
"address" :"北京海淀"
}
搜索
GET /person/_search
{
"query": {
"term": {
"address": {
"value": "北京"
}
}
}
}
可以搜索出来。“北京海淀”查不出来。
全文查询:match
全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集。
GET /person/_search
{
"query": {
"match": {
"address": "北京 昌平"
}
}
}
可以查出来。因为将搜索条件进行分词。
18 javaAPI
1创建spring-boot工程
2导入依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.4.0</version>
</dependency>
3测试类创建客户端
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("192.168.25.129", 9200, "http")
)
);
System.out.println(client);
4config类自动加载bean com.itheima.es11.config
public class ElasticSearchConfig {
@Value("${elasticsearch.host}")
private String host;
@Value("${elasticsearch.port}")
private int port;
@Bean
public RestHighLevelClient client() {
return new RestHighLevelClient(
RestClient.builder(
new HttpHost(host, port, "http")
)
);
}
}
5测试类直接引用
@Autowired
private RestHighLevelClient client;
6索引操作
19 索引的新增
测试类中添加
/**
* 添加索引
*/
@Test
public void addIndex() throws IOException {
// PUT person1
//1创建请求
CreateIndexRequest createIndexRequest=new CreateIndexRequest("person2");
//2执行操作
CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
//3获取结果
boolean acknowledged = createIndexResponse.isAcknowledged();
System.out.println("acknowledged:"+acknowledged);
boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
System.out.println("shardsAcknowledged:"+shardsAcknowledged);
String index = createIndexResponse.index();
System.out.println("index:"+index);
}
// PUT person3
// {
// "mappings": {
// "properties": {
// "name":{
// "type": "keyword"
// },
// "address":{
// "type": "text",
// "analyzer": "ik_max_word"
// }
// }
// }
// }
/**
* 添加索引和映射
*/
@Test
public void addIndexAndMapping() throws IOException {
// PUT person1
//1创建请求
CreateIndexRequest createIndexRequest=new CreateIndexRequest("person4");
createIndexRequest.mapping("{\n" +
" \"properties\": {\n" +
" \"name\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"address\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }", XContentType.JSON);
//2执行操作
CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
//3获取结果
boolean acknowledged = createIndexResponse.isAcknowledged();
System.out.println("acknowledged:"+acknowledged);
boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
System.out.println("shardsAcknowledged:"+shardsAcknowledged);
String index = createIndexResponse.index();
System.out.println("index:"+index);
}
20 javaApi 查询 删除 判断索引
测试类添加
/**
* 查询索引
*/
@Test
public void testGetIndex() throws IOException {
// GET person
//1创建请求
GetIndexRequest getIndexRequest = new GetIndexRequest("person");
//2执行操作
GetIndexResponse getIndexResponse = client.indices().get(getIndexRequest, RequestOptions.DEFAULT);
//3获取结果
Map<String, MappingMetaData> mappings = getIndexResponse.getMappings();
Set<String> keySet = mappings.keySet();
for (String key : keySet) {
System.out.println(key + ":" + mappings.get(key).getSourceAsMap());
}
}
/**
* 删除索引
*/
@Test
public void testDeleteIndex() throws IOException {
// DELETE person1
//1创建请求
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("person2");
//2执行操作
AcknowledgedResponse delete = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
//3获取结果
boolean acknowledged = delete.isAcknowledged();
System.out.println(acknowledged);
}
/**
* 判断索引
*/
@Test
public void testExistIndex() throws IOException {
// GET person3
//1创建请求
GetIndexRequest getIndexRequest = new GetIndexRequest("person3");
//2执行操作
boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
//3获取结果
System.out.println(exists);
}
关闭索引
打开索引
21 javaApi 新增文档
测试类添加
/**
* 添加文档
*/
@Test
public void testAddDoc() throws IOException {
// PUT /person3/_doc/1
// {
// "name" :"李四4",
// "age" : 44,
// "address" :"北京海淀"
// }
//1创建请求
IndexRequest indexRequest = new IndexRequest("person3").type("_doc").id("2");
Map<String, Object> sourceMap = new HashMap<>();
sourceMap.put("name", "李四4");
sourceMap.put("age", 44);
sourceMap.put("address", "北京海淀");
indexRequest.source(sourceMap);
//2执行操作
IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
//3获取结果
String id = indexResponse.getId();
System.out.println(id);
DocWriteResponse.Result result = indexResponse.getResult();
System.out.println(result);
}
/**
* 添加文档,对象做参数
*/
@Test
public void testAddDoc() throws IOException {
//1创建请求
// POST /person7/_doc/1
// {
// "name":"zhangsan",
// "address":"太原"
// }
// IndexRequest indexRequest=new IndexRequest("person7","_doc","2");
IndexRequest indexRequest=new IndexRequest("person7");
indexRequest.id("2");
//方式一
// HashMap<String, String> map = new HashMap<>();
// map.put("name","zhangsan");
// map.put("address","太原");
// indexRequest.source(map);
//方式二
// XContentBuilder builder = XContentFactory.jsonBuilder();
// builder.startObject();
// {
// builder.field("name","zhangsan");
// builder.field("address","太原");
// }
// builder.endObject();
// indexRequest.source(builder);
//方式三
// indexRequest.source("{\n" +
// " \"name\":\"zhangsan\",\n" +
// " \"address\":\"太原\"\n" +
// "}",XContentType.JSON);
//方式四
indexRequest.source("name","zhangsan"
,"address","太原"
,"age",18);
//2执行操作
IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
//3获取结果
// {
// "_index" : "person7",
// "_type" : "_doc",
// "_id" : "1",
// "_version" : 1,
// "result" : "created",
// "_shards" : {
// "total" : 2,
// "successful" : 1,
// "failed" : 0
// },
// "_seq_no" : 0,
// "_primary_term" : 1
// }
System.out.println(indexResponse.getId());
System.out.println(indexResponse.getIndex());
System.out.println(indexResponse.getVersion());
System.out.println(indexResponse.getResult().getLowercase());
}
22 javaApi 修改查询删除文档
测试类添加
/**
* 修改文档,对象做参数。就是添加文档!
*/
@Test
public void testUpdateDocPerson() throws IOException {
// PUT /person3/_doc/1
// {
// "name" :"李四4",
// "age" : 44,
// "address" :"北京海淀"
// }
Person person=new Person();
person.setId("33");
person.setName("zhangsan");
person.setAge(28);
person.setAddress("山西");
String json = JSON.toJSONString(person);
//1创建请求
IndexRequest indexRequest = new IndexRequest("person3").type("_doc").id(person.getId());
indexRequest.source(json,XContentType.JSON);
//2执行操作
IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
//3获取结果
String id = indexResponse.getId();
System.out.println(id);
DocWriteResponse.Result result = indexResponse.getResult();
System.out.println(result);
}
/**
* 根据Id查询文档
*/
@Test
public void testFindDocById() throws IOException {
// GET /person3/_doc/33
//1创建请求
GetRequest getRequest=new GetRequest("person3","33");
//2执行操作
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
//3获取结果
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
System.out.println(sourceAsMap);
}
/**
* 根据Id删除文档
*/
@Test
public void testDeleteDocById() throws IOException {
// Delete /person3/_doc/2
//1创建请求
DeleteRequest deleteRequest=new DeleteRequest("person3","2");
//2执行操作
DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
//3获取结果
DocWriteResponse.Result result = deleteResponse.getResult();
System.out.println(result);
}