ElasticSearch搜索引擎
You Know, for Search!
1. 简介
- 官网:https://www.elastic.co/cn/
基本概念
索引:index
索引index是存储document文档数据的结构,意义类似于关系型数据库中的数据库。
类型(逐渐被抛弃)
类型type也是用于存储document的逻辑结构,相对于index来说,type是index的下级,所以通常在面向有实际意义的数据时,index作为大类的划分,type作为小类的划分。比如如果把book书作为一个大类来建立index的话,那么书的类型(小说类、文学类、IT技术类等)就可以作为type。
文档:doc
文档的格式是json式的。 对于文档,有几个主要的标识信息:_index(插入到哪个索引中),_type(插入到哪个类型中),_id(文档的id是多少),在插入一个文档的时候,这几个信息也是必须的。
集群
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。
一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。一个节点只能通过指定某个集群的名字,来加入这个集群。
节点
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,
默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,
因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,
这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
分片与复制
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。
为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。
每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。
集群搭建
参考博客:https://zhuanlan.zhihu.com/p/99208091
2. 下载与安装
1. ES安装
官网(国外): 比较慢
https://www.elastic.co/cn/downloads/elasticsearch
国内下载镜像网站
https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
https://mirrors.huaweicloud.com/logstash/?C=N&O=D
https://mirrors.huaweicloud.com/kibana/?C=N&O=D
https://github.com/medcl/elasticsearch-analysis-ik/releases
https://github.com/lmenezes/cerebro/releases
window
- 解压可用
- 双击bin目录的
elasticsearch.bat 文件启动 - 访问默认端口:127.0.0.1:9200
Linux
2. 安装ElasticSearch-Head插件
安装header插件需要grunt命令,因此需要安装node.js
步骤
- 安装
node.js (网上找,不赘述) - 安装
cnpm 阿里镜像:使用cnpm指令比npm速度更快,更稳定
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install express
- 安装
grunt :打开cmd,输入命令 cd D:\Nodejs(你安装nodejs的路径) 进入Nodejs的根目录下,输入npm install -g grunt -cli 进行安装grunt。 - 安装
pathomjs :打开cmd进入你安装head的根目录下,然后执行命令:npm install 进行安装pathomjs 。 - 安装完成之后运行命令
grunt server ,启动head服务(默认端口号是9100)
- 浏览器访问:localhost:9100(能连接到es说明安装成功)
3. Kibana安装
步骤
-
下载地址:kibana华为云镜像地址(注意版本统一为7.6.1) -
解压即用 -
汉化配置:打开kibana.yml将语言设置为中文 -
双击bin目录下的kibana.bat文件 -
访问:localhost:5601,安装策划你刚刚
4. 安装ik分词器插件
- 解压,放入
es 的plungs目录 - 重启es,如果加载了插件,即安装成功
4.1 ik_smart 最少切分
# get请求
GET _analyze
{
"analyzer": "ik_smart",
"text": "天生我材必有用"
}
4.2 ik_max_word 最细粒度切分
穷尽词库
GET _analyze
{
"analyzer": "ik_max_word",
"text": "天生我材必有用"
}
4.3 自定义词典
- 自定义词典文件,将词典文件名配入ik的配置文件中
注意:文件编码要选择UTF-8 BOM,如果是ANIS则无法识别
- 重启es,可以看到加载到了拓展词典的日志
- 测试
3. 配置文件
elasticsearch.yml: 用于配置Elasticsearch运行参数
jvm.options: 用于配置Elasticsearch JVM设置
log4j2.properties: 用于配置Elasticsearch日志
elasticsearch.yml配置
注意配置文件不能有中文,否则启动会报错闪退
cluster.name: salvation
node.name: wujz
http.port: 9200
http.cors.enabled: true
http.cors.allow-origin: "*"
其他配置
cluster.name : es集群的名称,默认elasticsearch,建议配置方便管理。
node.name :节点名称,一台服务器就是一个节点,es默认会随机给出节点名称,建议配置,方便管理。
path.conf : 设置配置文件的存储路径,tar 或zip 包安装默认在es根目录下的config 文件夹,rpm安装默认在/etc/ elasticsearch 。
path.data : 设置索引数据的存储路径,默认是es根目录下的data 文件夹,可以设置多个存储路径, 用逗号隔开。
path.logs : 设置日志文件的存储路径,默认是es根目录下的 logs 文件夹
path.plugins : 设置插件的存放路径,默认是es根目录下的plugins 文件夹
bootstrap.memory_lock : 设置为true 可以锁住es使用的内存,避免内存与swap 分区交换数据
network.host : 设置绑定主机的ip 地址,设置为0.0.0.0 表示绑定任何ip ,允许外网访问,生产环境建议设置为具体的 ip 。
http.port :设置对外服务的http端口,默认为9200。
transport.tcp.port :9300 集群结点之间通信端口
node.master :指定该节点是否有资格被选举成为master 结点,默认是true ,如果原来的master 宕机会重新选举新的master 。
node.data :指定该节点是否存储索引数据,默认为true
discovery.zen.ping.timeout :3s 设置ES自动发现节点连接超时的时间,默认为3秒,如果网络延迟高可设置大些。
discovery.zen.minimum_master_nodes :主结点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 ,比如:有3个符合要求的主结点,那么这里要设置为2。
node.max_local_storage_nodes : 单机允许的最大存储结点数,通常单机启动一个结点建议设置为1,开发环境如果单机启动多个节点可设置大于1.
http.cors.enabled: true
http.cors.allow-origin: "*"
4. RestFul风格
4.1 风格简介
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件.它主要是用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制
基本操作说明
method | url | 描述 |
---|
PUT | localhost:9200/索引名称/索引名称/文档id | 创建文档(指定文档id) | POST | localhost:9200/索引名称/索引类型 | 创建文档(产生随机id) | POST | localhost:9200/索引名称/索引类型/文档id/_update {请求体} | 修改文档 | DELETE | localhost:9200/索引名称/索引类型/文档id | 删除指定文档 | GET | localhost:9200/索引名称/索引类型/文档id | 获取指定文档数据 | POST | localhost:9200/索引名称/索引类型/文档id/_serach | 查询数据 |
4.2. 基本CRUD
3.1 添加操作
手动创建索引
创建并设置索引
PUT /list
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}
设置索引类型
PUT /zhangsan
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc": {
"type": "text"
},
"age": {
"type": "long"
}
}
}
}
字段类型
-
字符串类型:text, keyword
- text:表示支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;
- keyword:表示不进行分词,直接索引,支持模糊、支持精确匹配,支持聚合、排序操作。
-
数值类型:long, integer, short, byte, double, float, half float, scaled fload -
日期类型:date -
布尔值类型:boolean -
二进制类型:binart -
等等…
注意: 类型映射不能操作已经存在的类型
添加文档
PUT /salvation/zhangsan(或者_doc)/1
{
"name": "zhangsan",
"desc": "法外狂徒",
"age": 23
}
post /salvation/_doc
{
"name": "张三",
"desc": "球球你别学了",
"age": 12
}
注意:类型名称在7.x版本后已经过时,8.x版本后将被抛弃,因此**可写上自定义类型名称也可以用 “_doc “ 代替(占位)**
3.2 修改数据
1. PUT操作:相当于覆盖数据
put /salvation/zhangsan/1
{
"name": "李六"
}
2. POST:相当于覆盖数据
POSt /salvation/zhangsan/1
{
"name": "王五"
}
3. POST _update形式(推荐):局部更新,不覆盖
POST /salvation/zhangsan/1/_update
{
"doc": {
"name": "张三"
}
}
注意:****POST _update命令格式不同于其他的命令,是将修改的内容放在“doc”中
参考链接:https://blog.csdn.net/weixin_38106322/article/details/102015382
3.3 查询操作
# 获取各个节点详细信息
GET _cat/indices?v
# 获取文档数据
GET /salvation/zhangsan/1
# 条件查询
GET /salvation/zhangsan/_search?q=name:张三
# 获取索引库指定类型所有文档数据
POST(GET) /salvation/zhangsan/_search
# 条件查询,查询条件放在query的match的json内容中
POST /salvation/zhangsan/_search
{
"query": {
"match": {
"name": "张三"
}
}
}
3.4 删除操作
# 删除索引
DELETE /list
# 删除文档数据
DELETE /salvation/zhangsan/3
4.3 复杂查询
1. query关键字:
条件查询,查询条件放在query中
# 条件查询,查询条件放在query中
POST /salvation/zhangsan/_search
{
"query": {
"match": {
"name": "张三"
}
}
}
2. match:条件匹配
match中的字段多条件查询用空格隔开,例如: 标签字段的多标签查询,满足一个条件即可被查到
3. _source:结果过滤
# 将你需要查询的字段,放在“_source”的中括号中
POST /salvation/zhangsan/_search
{
"query": {
"match": {
"name": "张三"
}
},
"_source": ["name", "age", "desc"]
}
4. sort :排序
# 年龄倒序查询 desc 降序 asc 升序
POST /salvation/zhangsan/_search
{
"query": {
"match": {
"name": "张三"
}
},
"sort": [
{
"age": "desc"
}
]
}
5. from ,size (分页查询)
# from 起始位置, size 数量
POST /salvation/zhangsan/_search
{
"query": {
"match": {
"name": "张三"
}
},
"from": 0,
"size": 2
}
6 bool 查询
6.1 must 必须
文档必须匹配 must 选项下的查询条件,相当于逻辑运算的 AND
# 查询条件放在中括号中,全部匹配才被搜索到
POST /salvation/zhangsan/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "zhangsan"
}
},
{
"match": {
"age": 19
}
}
]
}
}
}
6.2 should 应该
文档可以匹配 should 选项下的查询条件也可以不匹配,相当于逻辑运算的 OR
# 或条件查询
POST /salvation/zhangsan/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "zhangsan"
}
},
{
"match": {
"age": 27
}
}
]
}
}
}
6.3 must_not 一定没有
与 must 相反,匹配该选项下的查询条件的文档不会被返回;需要注意的是,must_not 语句 不会影响评分 ,它的作用只是将不相关的文档排除。
# 查询不叫zhangsan或年龄不是27岁的
POST /salvation/zhangsan/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "zhangsan"
}
},
{
"match": {
"age": 27
}
}
]
}
}
}
6.4 filter 过滤器
和 must 一样,匹配 filter 选项下的查询条件的文档才会被返回,但是 filter 不评分 ,只起到过滤功能,与 must_not 相反。
过滤条件
- gt 大于
- gte 大于等于
- lt 小于
- lte 小于等于
POST /salvation/zhangsan/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "张三"
}
}
],
"filter": {
"range": {
"age": {
"gte": 20,
"lte": 40
}
}
}
}
}
}
7. term 精确查询
多个词匹配精确查询
关于分词
-
term:直接查询精确的 -
match:会使用分词器解析 (先分析文档,然后通过分析的文档进行查询)
text, keyword的区别
- text:表示支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;
- keyword:表示不进行分词,直接索引,支持模糊、支持精确匹配,支持聚合、排序操作。
# 多条件同时满足
POST /salvation/zhangsan/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"name": "zhangsan"
}
},
{
"term": {
"age": 19
}
}
]
}
}
}
8. highlight 高亮查询
# 对查询结果的查询条件高亮显示
POST /salvation/zhangsan/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "zhangsan"
}
},
{
"match": {
"desc": "法外狂徒"
}
}
]
}
},
"highlight": {
"fields": {
"name": {},
"desc":{}
}
}
}
自定义高亮标签
POST /salvation/zhangsan/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "zhangsan"
}
},
{
"match": {
"desc": "法外狂徒"
}
}
]
}
},
"highlight": {
"pre_tags": "<p class:\"key\", style='color: red'>", # 标签前缀
"post_tags": "</p>", # 标签后缀
"fields": {
"name": {},
"desc":{}
}
}
}
5. SpringBoot集成Es
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
配置类
@Configuration
public class ElasticSearchConfig {
@Bean(name = "restHighLevelClient")
public RestHighLevelClient restHighLevelClient() {
return new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")
)
);
}
}
测试
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
private static final String INDEX = "salvation";
1. 创建索引
@Test
void createIndex() throws IOException {
CreateIndexRequest request = new CreateIndexRequest("salvation");
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
logger.info("创建索引的响应:{}", JSONObject.toJSONString(response));
logger.info("是否创建成功:{}",response.isAcknowledged());
}
2. 判断索引库是否存在
@Test
void existIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest(INDEX);
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
logger.info("GetIndexRequest: {}",request);
logger.info("{}索引是否存在:{}", INDEX, exists);
}
3. 删除索引
@Test
void deleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("salvation");
AcknowledgedResponse acknowledgedResponse = client.indices().delete(request, RequestOptions.DEFAULT);
logger.info("acknowledgedResponse: {}" , JSONObject.toJSONString(acknowledgedResponse));
logger.info("是否删除成功:{}", acknowledgedResponse.isAcknowledged());
}
4. 添加文档数据
@Test
void addDocument() throws IOException {
User user = new User("salvation", 19, 9527);
IndexRequest indexRequest = new IndexRequest(INDEX);
indexRequest.id("1");
indexRequest.timeout(TimeValue.timeValueSeconds(1));
indexRequest.source(JSONObject.toJSONString(user), XContentType.JSON);
IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
logger.info("创建文档的响应:{}", JSONObject.toJSONString(response));
}
5. 判断文档数据是否存在
@Test
void getDocument() throws IOException {
GetRequest getRequest = new GetRequest(INDEX, "1");
getRequest.id("1");
getRequest.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none_");
boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
logger.info("{}", exists);
}
6. 获取文档数据
@Test
void getDocument() throws IOException {
GetRequest request = new GetRequest(INDEX, "1");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
String string = response.getSourceAsString();
logger.info("user: {}", JSONObject.parseObject(string, User.class));
}
7. 更新文档
@Test
void updateDocument() throws IOException {
UpdateRequest request = new UpdateRequest(INDEX, "1");
request.timeout(TimeValue.timeValueSeconds(1));
User user = new User("zhangsan", 13, 1874);
request.doc(JSONObject.toJSONString(user), XContentType.JSON);
UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
logger.info("更新响应结果:{}", JSONObject.toJSONString(response));
}
8. 删除文档
@Test
void deleteDocument() throws IOException {
DeleteRequest request = new DeleteRequest(INDEX, "1");
request.timeout(TimeValue.timeValueSeconds(1));
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
logger.info("响应结果:{}", JSONObject.toJSONString(response));
}
9. 批量处理
@Test
void bulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest(INDEX);
bulkRequest.timeout(TimeValue.timeValueSeconds(10));
ArrayList<User> users = new ArrayList<>();
users.add(new User("zhangsan 1", 13, 1874));
users.add(new User("zhangsan 2", 13, 1874));
users.add(new User("zhangsan 3", 13, 1874));
users.add(new User("zhangsan 4", 13, 1874));
users.add(new User("zhangsan 5", 13, 1874));
for (int i = 0; i < users.size(); i++) {
bulkRequest.add(new IndexRequest(INDEX)
.id(String.valueOf(i + 1))
.source(JSONObject.toJSONString(users.get(i)), XContentType.JSON));
}
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
logger.info("响应结果:{}", JSONObject.toJSONString(response));
}
10. 搜索
@Test
void searchRequest() throws IOException {
SearchRequest searchRequest = new SearchRequest(INDEX);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "zhangsan 1");
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(TimeValue.timeValueSeconds(1));
searchRequest.source(sourceBuilder);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
logger.info("响应结果:{}", JSONObject.toJSONString(response));
}
6. java爬虫 ----- Jsoup
待更新……
|