https://blog.csdn.net/weixin_43495317/article/details/104507018 基础语法
https://blog.csdn.net/qq_26502245/article/details/95607656 7x 常规操作
https://blog.csdn.net/weixin_42681513/article/details/105632063 常用搜索
https://www.elastic.co/guide/en/elasticsearch/reference/7.3/index.html 官方文档
文档是 JSON 类型的。
可以将 ES 中的这三个概念和 MySQL 类比:
一个MySQL实例中可以创建多个 Database,一个Database中可以创建多个Table。
现在ES7的概念
在 7.0 以及之后的版本中 Type 被废弃了。一个 index 中只有一个默认的 type,即 _doc
。(ES8以后直接取消)
ES 的Type 被废弃后,库表合一,Index 既可以被认为对应 MySQL 的 Database,也可以认为对应 table。
也可以这样理解:
一开始,我们把索引比作是关系型数据库中的数据库,映射type比作了表。其实这个比喻是错误的,在关系型数据库中表是相互独立的,不同表中相同的列是没有关联关系的,这个与映射type是不一样的。
在es的索引中,不同的映射类型中存在相同的属性名,他们底层都是使用同一个Lucene属性。比如上面的商品例子中,电器和生鲜都有name字段,他们必须要有相同的映射类型才可以。例如在同一个索引中,deleted属性你在A类型中想用date来表示,在B类型中想用boolean来表示,像这种情况就不支持了。
最重要的是同一个索引中共同字段比较少或者没有,那么会导致数据太稀疏,这会影响Lucene有效压缩文档的能力,导致你的搜索很慢。
就是因为这些原因,es官方决定删除映射type这个概念。
把之前的电器类型和生鲜类型分别建立电器索引和生鲜索引。
因为索引是相互独立的,不存在相同字段的冲突问题。使用这种方式有2个好处:
1、数据更加密集,有利于Lucene的压缩。
2、做统计的时候数据会更加准确,因为数据都在同一个索引中。
每个索引都可以根据自己实际需要存储多大的数据来进行调整,你可以对不同的索引设置不同的分片和副本。
使用前必须保证 elasticsearch 和 elasticsearch-head 启动 而且健康值 为绿色或者黄色 不能是灰色
ElasticSearch的接口语法
curl ‐X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' ‐d '<BODY>
参数解析:
ES Restful API GET、POST、PUT、DELETE、HEAD含义:
1)GET:获取请求对象的当前状态。
2)POST:改变对象的当前状态。
3)PUT:创建一个对象。
4)DELETE:销毁对象。
5)HEAD:请求获取对象的基础信息。
发送Body数据前 一定要先用 https://www.json.cn//# 这个网站里的json 检测一下 看看格式是否正确 否则会报错400
查看 ElasticSearch常用字段类型
这篇文章
GET http://192.168.81.141:9200/_cat/health
红色表示数据不可用,黄色表示数据可用,部分副本没有分配,绿色表示一切正常
GET http://192.168.81.141:9200/_cat/indices?v
语法:
PUT http://192.168.81.141:9200/要添加的索引名称
列:
PUT http://192.168.81.141:9200/index1
索引=表 (properties下面内容就是表示这个表的每个字段是干嘛的)
我们在es中添加索引数据时,可以不需要指定数据类型,es中有自动影射机制,字符串映射为string,数字映射为long。
字符串类型的自动影射机制,默认情况
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
ignore_above忽略长度超过256字符串。 用于设置超过指定字符后,超出的部分不能被索引或者存储
其他基本数据类型的自动影射机制,默认情况
"price": {
"type": "float"
}
我们可以自己通过mappings可以指定数据类型是否存储等属性。
比如: 如果想给指定字段用指定的分词器那么,就需要手动添加,
语法:
PUT http://192.168.81.141:9200/指定索引/_mappings
列:
PUT http://192.168.81.141:9200/index1/_mappings
{
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word"
},
"age": {
"type": "long",
"index": false
},
"like": {
"type": "text",
"analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
index为false时es不会为该属性创建索引(本质是不做分词),也就是说不能当做主查询条件 (默认为true)
在ES7中可以不用一次性把全部字段的映射信息都创建好, 可以根据后来根据业务情况自行进行补充
因为: 现在一个索引下面的数据全部都通用一个类型(_doc)
比如: 我现在index1索引映射的类型是:
{
"index1": {
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word"
},
"age": {
"type": "long",
"index": false
},
"like": {
"type": "text",
"analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
插入时候数据是
{
"name":"胡",
"age":"22",
"like":"你好世界",
"desc":"ES初学者"
}
那么现在需求来了, 添加一个时间字段,用于查询指定时间段的数据
第一步先添加映射字段
{
"properties": {
"time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis||date_optional_time ",
"index": true
}
}
}
会有三种情况发生:
如果原来存在那么判断类型是否一致如果都一样那么, 执行成功
1.
如果原来不存在,就在原来的映射里添加这个字段映射信息, 执行成功
1.
如果原来字段存在,并且原来字段内数据的类型和要修改的不一样,那么就会报错
mapper [time] of different type, current_type [text], merged_type [date]
为了避免第3点的问题发送,我们在映射字段前去,查询下当前索引的映射情况,不要和原来的字段名称一样了
GET http://192.168.81.141:9200/index1/_mappings
查询索引内映射的情况
插入的时候数据是
{
"name":"胡",
"age":"22",
"like":"你好世界",
"desc":"ES初学者",
"time": "2021-2-11"
}
语法: PUT http://192.168.81.141:9200/要添加的索引名称
列:
PUT http://192.168.81.141:9200/index1
{
"mappings": {
"properties": {
"name": {
"type":"text",
"analyzer": "ik_max_word",
"index": true
},
"age": {
"type": "long",
"index": false
},
"like":{
"type":"text",
"analyzer": "ik_max_word",
"index": true
},
"desc":{
"type":"text",
"analyzer": "ik_max_word",
"index": true
}
}
}
}
注意: 只要是需要查询的字符串类型的一定要使用
其他不参与索引的配置
elasticsearch 并不支持修改mapping映射字段,无法直接修改,所以需要曲线救国。
原来的索引 index1, 新索引index2
先创建一个新索引,然后添加玩映射信息
PUT http://192.168.81.142:9200/index2
1.
将原来索引数据迁移到新索引中
POST http://192.168.81.142:9200/_reindex?slices=auto&refresh
{
"source": {
"index": "index1",
"size": 5000
},
"dest": {
"index": "index2",
"version_type": "internal"
}
}
source->index(原来的索引名称)
dest->index(新索引名称)
其他的配置不用动
1.
删除原来的索引
DELETE http://192.168.81.142:9200/index1
1.
给新索引添加别名 ,别名的名称是原来索引的名称,这样就不会影响到程序
POSThttp://192.168.81.142:9200/_aliases
{
"actions" : [
{ "add" : { "index" : "index2", "alias" : "index1" } }
]
}
index 需要添加别名的索引名称, alias 起别名
语法:
GET http://192.168.81.141:9200/索引名称/_mappings
列:
GET http://192.168.81.141:9200/index1/_mappings
结果显示:
{
"index1": {
"mappings": {
"properties": {
"age": {
"type": "long",
"index": false
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
},
"like": {
"type": "text",
"analyzer": "ik_max_word"
},
"name": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
语法:
DELETE http://127.0.0.1:9200/索引名称
列:
DELETE http://192.168.81.141:9200/index1
语法:
GET http://192.168.81.141:9200/索引名称/_search
列:
GET http://192.168.81.141:9200/index1/_search
结果
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 0,
"relation": "eq"
},
"max_score": null,
"hits": []
}
}
我这是索引中没有数据,你可以自己添加几天试试
POST http://192.168.81.141:9200/index1/_delete_by_query
Body
{
"query": {
"match_all": {}
}
}
返回:
{
"took": 71,
"timed_out": false,
"total": 5,
"deleted": 5,
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"failures": []
}
文档等同于数据库的1行, 在es7x必须通过{index}/_doc 来自动创建id,通过{index}/_doc/{id}指定文档的id
语法:
POST http://192.168.81.141:9200/index1/_doc/文档的id
列:
POST http://192.168.81.141:9200/index1/_doc/1
Body(添加的内容)
{
"name":"胡",
"age":"22",
"like":"你好世界",
"desc":"ES初学者"
}
结果显示:
{
"_index": "index1",
"_type": "_doc",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
语法: GET http://192.168.81.141:9200/index1/_doc/文档id
列:
GET http://192.168.81.141:9200/index1/_doc/1
结果:
{
"_index": "index1",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"name": "胡",
"age": "22",
"like": "你好世界",
"desc": "ES初学者"
}
}
修改文档其实 就和创建文档一样 (覆盖就行)
语法: POST http://192.168.81.141:9200/index1/_doc/文档id
列:
POST http://192.168.81.141:9200/index1/_doc/文档id
Body
{
"name":"胡1",
"age":"23",
"like":"你好世界1",
"desc":"ES初学者1"
}
结果显示:
{
"_index": "index1",
"_type": "_doc",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
语法: DELETE http://192.168.81.141:9200/index1/_doc/文档id
列:
DELETE http://192.168.81.141:9200/index1/_doc/1
结果:
{
"_index": "index1",
"_type": "_doc",
"_id": "1",
"_version": 3,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
以分词进行 匹配查询 ,用于文档匹配 ,标题匹配 … 等
运算符
“default_operator”:“运算符”
有AND、OR,默认为OR。
比如query里面的内容是 ”搜索服务器”,那么只要分词集中 包含之一就可以匹配。
如果"default_operator":“AND”, 那么就是全匹配
比如: query里面的内容是 ”搜索服务器”
那么只要分词集内词 能拼出 ”搜索服务器” 这整句话 就可以匹配
通配符
支持一些简单的wildcard写法。比如fields:[“nam/*”]
即任何nam开头的字段
?
与/*
, ?
可以代替一个任意字符、/*
可代表任意个字符(包括零个)
比如你要查询的内容很长,记不清了但是你记得末尾是tor,那么你只需要把query内容写成/*tor即可
正则
如果要在query的内容中使用正则表达式,在两端加上正斜杠/即可。比如: name:/ob[am]{2}a/
可以到网上查 具体怎么写
需要 发送POST请求 无须文档id 需要在 表 后面追加 /_search
POST http://192.168.81.141:9200/index1/_search
Body内容
{
"query":{
"query_string":{
"default_field":"like",
"query":"你好"
}
}
}
可以发现是能查询到结果的因为 query_string 默认采用的 or 匹配 以及standard分词器
default_field: 查询的字段 默认为_all 即对所有字段进行查询
*
query: 需要查询的具体内容 (如果不指定file那么就是查询所有字段的所有内容)
{
"query":{
"query_string":{
"query":"2021"
}
}
}
field与query写在一起比如”query”:”title:搜索服务器”
这时候 default_field就没必要了
{
"query":{
"query_string":{
"query":"like:你好"
}
}
}
注意: 如果需要要使用到wildcard的话 "query":"like:/*界"
query_string 支持多字段 "fields" : ["age", "name"]
, 可以把default_field去掉使用fields,
{
"query":{
"query_string":{
"fields" : ["name", "like"],
"query":"胡"
}
}
}
Term查询会采用精确匹配。
通常用于数字,日期和枚举等结构化数据,
term是代表完全匹配,也就是精确查询,搜索词必须是分词集合中的一个
发送POST请求 http://192.168.81.141:9200/index1/_search
Body
{
"query":{
"term":{
"like":"你好"
}
}
}
match模糊匹配,需要指定字段名,但是输入会进行分词,比如"hello world"会进行拆分为hello和world,然后匹配,如果字段中包含hello或者world,或者都包含的结果都会被查询出来,也就是说match是一个部分匹配的模糊查询。查询条件相对来说比较宽松。
{
"query": {
"match": {
"like": "世界"
}
}
}
match_phase
(近似匹配):会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样。以"hello world"为例,要求结果中必须包含hello和world,而且还要求他们是连着的,顺序也是固定的,hello that word不满足,world hello也不满足条件。
POST http://192.168.81.141:9200/index1/_search
{
"query": {
"match_phrase": {
"like": "世界"
}
}
}
还有很多更高级的语法这里就不继续了,自行看上面提供的教程网站,或者自行百度就行了,毕竟我们重点是在java中操作
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_45203607/article/details/120234164
内容来源于网络,如有侵权,请联系作者删除!