Elasticsearch基本使用

安装Elasticsearch

可以在https://www.elastic.co/cn/downloads/elasticsearch这个页面找到elasticsearch对应系统的安装包,elasticsearch用java开发的, 最新的版本内置了对应的jdk, 通过下面的方式能快速启动:

cd /安装目录/elasticsearch/bin
./elasticsearch 
#验证是否启动, 可以访问: http://127.0.0.1:9200/

安装可视化工具Kibana

可以在https://www.elastic.co/cn/downloads/kibana这个页面找到kibana对应系统的安装包,kibana用node开发, 最新的版本内置了对应的node启动程序,启动流程如下:

  • 解压后修改config/kibana.yml里的配置, 需要设置 elasticsearch.hosts, 本地就是: http://127.0.0.1:9200/
  • 如果想用中文环境, 可以修改config/kibana.yml文件, 设置i18n.locale: "zh-CN"
  • 在/bin 目录下启动kibana
    文章后面的代码都是在kibana 的Dev Tools 里操作完成

重要的概念

  • 索引(Index): 这里的索引跟我们平时用Mysql里索引是不一样的,在ES中, Index可以理解成Mysql里的一张表。ES通过不同的Index来管理数据
  • 文档(Document): 相当于Mysql里的一行记录, ES 里的数据是通过文档来记录的。
  • 倒排索引: 把数据里的每个词或者词组(这里跟具体的分词器相关)映射到文档的id。
  • 节点: 一个Elasticsearch 进程。
  • 集群: 多个节点组成的系统称为集群。
  • 分片: 逻辑的概念, 可以理解成对表做分库分表
  • 副本:某个分片的复制
  • 类型: 文档里字段的定义
  • mappings: 对Index的描述, 类似于mysql里的Schemas
  • type类型: ES对这个概念已经废弃, 不需要了解

数据类型

分类 具体类型
字符串 text: 会被分词,能根据词项快速检索,比如文章的标题,文本 。keyword: 不会被分词,只能通过精确查询,比如文章的作者
整型 byte, short , int, long
浮点型 float, double, half_float (16位) , scaled_float(缩放类型,比如价格55.55, 存的是5555, 缩放因子为100)
boolean 值为true, false
date 日期类型
二进制类型 binary: 用的比较少,没法做索引
array 数组类型,数据里的类型可以是对象
object 对象类型,文档会包含嵌套的对象
nested 嵌套类型, 他能够保证数据之间的层级结构

索引的基本操作

  • 创建索引, 类似操作mysql的建表语句, 这里初了需要关注各个字段的含义以外, 还需要关注settings里的分片数(number_of_shards)和副本数(number_of_replicas)。
PUT film
{ 
    "mappings": { 
        "properties": { 
            "id":{
                "type": "keyword",
                "store":true
            },
            "name":{
                "type": "keyword",
                "store":true
            },
            "desc":{
                "type":"text",
                "store":true,
                "analyzer": "ik_max_word"
            },
            "tag":{
                "type": "keyword",
                "store":true
            },
            "actors":{
                "type": "nested",
                "properties":{
                    "id":{
                        "type": "keyword",
                        "store":true
                    },
                    "name":{
                        "type": "keyword",
                        "store":true
                    },
                    "sex":{
                        "type": "integer",
                        "store":true
                   }
               }
            }
        } 
    },
    "settings": {
      "number_of_replicas": 1,
      "number_of_shards": 1
    }
}

  • 查看索引的元数据信息
GET film #film 是Index的名称
  • 删除索引
DELETE film #film 是Index的名称

对于索引没有所谓的更新操作,如果对于有些字段的类型需要做更新,只能删除原来的索引,重新创建新的索引。在实际的使用过程中,我们可以插入一些示例数据,通过ES帮我们生成默认mappings, 然后再对mappings做修改对于指定好的分片数, 也是没有办法修改的, 如果想增加分片数, 只能做reindex操作。

文档的基本操作

  • 新增文档, 新增的时候需要指定id, 如果没指定, ES会生成一个String类型的id
PUT /film/_doc/001
{
    "id":"001",
    "name":"甄嬛传",
    "desc":"雍正元年,结束了血腥的夺位之争,新的君主(陈建斌 饰)继位,国泰民安,政治清明,但在一片祥和的表象之下,一股暗流蠢蠢欲动,尤其后宫,华妃(蒋欣 饰)与皇后(蔡少芬 饰)分庭抗礼,各方势力裹挟其中,凶险异常。十七岁的甄嬛(孙俪饰)与好姐妹眉庄(斓曦饰)、陵容(陶昕然饰)参加选秀,她本抱着来充个数的念头,可皇帝(陈建斌饰)偏相中了她的智慧、气节与端庄,最后三人一同入选。但因华妃(蒋欣饰)嚣张,步步紧逼,眉庄被冤,陵容变心,天真的甄嬛慢慢变成了后宫精明的女子。皇帝发现年羹尧(孙宁饰)的野心,令甄父剪除年氏一族,甄嬛终于斗倒了华妃。但由于甄嬛与先故纯元皇后的神似,皇后设计以纯元皇后的礼服陷害甄嬛,父亲(沈保平饰)也被文字狱牵连和奸人陷害而遭牢狱之灾,生下女儿后,心灰意冷的甄嬛选择出宫修行。在宫外幸得十七爷允礼(李东学饰)悉心照顾,二人相亲相爱,只等有机会远走高飞。后因误传十七爷死讯,甄嬛为保全腹中骨肉,设计与皇帝相遇,狠心断绝对十七爷的爱恋,重回宫中,再度与皇后相斗。后因生下双生子,同时甄父的冤案得以平反,重新被皇帝重用,甄氏一族再度崛起。甄嬛多次躲过皇后的陷害,最终扳倒皇后。可造化弄人,由于皇帝的疑心,最终却只能看着心上人允礼死在自己怀中,而与叶澜依(热依扎饰)合谋弑君。皇帝驾崩后,甄嬛养子弘历登基,甄嬛被尊为圣母皇太后,即便享尽荣华,但眼见一生姐妹沈眉庄血崩而亡,一生爱人允礼为保其周全而无憾自尽,不过是一代封建王朝的悲情故梦罢了。",
    "tag":["后宫","古装","清朝","爱情","宫斗"],
    "actors":[
        {
            "id":"actor_001",
            "name":"孙俪",
            "sex":0
        },
        {
            "id":"actor_002",
            "name":"陈建斌",
            "sex":1
        },
        {
            "id":"actor_003",
            "name":"蔡少芬",
            "sex":0
        },
        {
            "id":"actor_004",
            "name":"蒋欣",
            "sex":0
        },
        {
            "id":"actor_005",
            "name":"蓝盈盈",
            "sex":0
        }
    ]
}
  • 修改文档,修改文档的时候,需要指定文档的id, 并且在url后面加上_update表示更新, 其中的"doc" 表示修改文档的内容, 不加上会报错
POST /film/_update/001
{ 
  "doc":{
    "id":"001",
    "tag":["后宫","古装","清朝","爱情","宫斗","玄幻"],
    "actors":[
        {
            "id":"actor_002",
            "name":"陈建斌",
            "sex":1
        },
        {
            "id":"actor_006",
            "name":"蓝盈盈",
            "sex":0
        }
    ]
  }
}
  • 删除文档
DELETE /film/_doc/001

文档查询

对于查询, 如果需要了解内部是如何解析的, 可以在查询里加上"profile": "true"。

  • 最简单的, 根据文档Id查询
GET /film/_doc/001 #001表示文档的id
  • 查看es分词器的结果
GET _analyze
{
  "analyzer": "ik_max_word", #分词器
  "text": "我是中国人"
}
  • term查询: 完全匹配,不会将指定的查询关键字进行分词,直接去分词库中匹配,找到相应的文档内容,类似于mysql里的“=”。
GET /film/_search
{
  "query": {
    "term": {
      "name.keyword": {  #这里需要指定对name的keyword查询
        "value": "甄嬛传"
      }
    }
  }
}
  • terms查询, 查询机制更term一样,类似于mysql里的in(?,?)
GET /film/_search
{
  "profile": "true", 
  "query": {
    "terms": {
      "name": ["甄", "画"]
    }
  }
}
  • match_all查询: 查询全部内容,不指定任何查询条件, 可以通过from, size 做分页查询
GET /film/_search
{
  "from": 0, 
  "size": 2, 
  "query": {
    "match_all": {}
  }
}
  • match查询:match查询属于高级查询,会根据你查询的字段的类型不一致,采用不同的查询方式
  • 如果查询的是日期或者数值的字段,他会自动将你的字符串查询内容转换成日期或者数值对待;
  • 如果查询的内容是一个不能被分词的字段(keyword).match查询不会对你的指定查询关键字进行分词;
  • 如果查询的内容是一个可以分词的字段(text),match会将你指定的查询内容根据一定的方式去分词,然后去分词库中匹配指定的内容。
    总而言之:match查询,实际底层就是多个term查询,将多个term查询的结果汇集到一起返回给你。
GET /film/_search
{
  "profile": "true", 
  "query": {
    "match": {
      "name": "甄"
    }
  }
}
  • match_phrase查询:首先解析查询字符串来产生一个词条列表(这里会分词)。然后会搜索所有的词条,只保留包含了所有搜索词条的文档,并且词条的位置要邻接可以通过slop来指定词之间相隔多远还能匹配, 如果超过这个值, 文档将匹配不上
GET /film/_search
{
  "profile": "true", 
  "query": {
    "match_phrase": {
      "desc": {
        "query": "国泰民安 祥和",
        "slop": 8
      }
    }
  }
}
  • multi_match查询:多字段进行匹配, 只要有一个字段满足搜索条件, 就能查询出来, 对于多字段匹配的问题, 涉及到评分, 可以通过type来指定评分标准,有三种类型,分别是: best_fields: 完全匹配的文档占比高, 可以通过tie_breaker指定评分的系数;most_fields:表示 越多字段匹配的文档评分越;
    cross_fields: 表示词条的分词词汇是分配到不同字段中评分高。
GET /film/_search
{
  "profile": "true", 
  "query": {
    "multi_match": {
     "query": "传",
     "fields": ["name", "desc", "tag"]
    }
  }
}

GET /film/_search
{
  "profile": "true", 
  "query": {
    "multi_match": {
     "query": "民国",
     "fields": ["name", "desc", "tag"], 
     "type": "best_fields",
     "tie_breaker": 0.5
    }
  }
}
  • bool联合查询: 其中 must: 表示文档必须完全匹配, must_not: 表示文档必须不能匹配,should表示下面的条件只要有一个满足, 就能查出来, 类似sql的or
GET /film/_search
{
  "profile": "true", 
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "tag.keyword": {
              "value": "爱情"
            }
          }
        }
      ],
      "must_not": [
        {"term": {
          "tag.keyword": {
            "value": "土匪"
          }
        }}
      ]
    }
  }
}

  • 范围查询:类似于sql里的between and
GET /sms-logs-index/_search
{
  "profile": "true", 
  "query": {
    "range": {
      "fee": {
        "gte": 50,
        "lte": 500
      }
    }
  }
}

GET /sms-logs-index/_search
{
  "profile": "true", 
  "query": {
    "range": {
      "createDate": {
        "gte": "2020-09-16",
        "lte": "2020-09-21"
      }
    }
  }
}
  • 对于nested类型的查询, 需要指定path
GET /film/_search
{
    "query": {
        "bool": {
            "must": [
              {
                    "nested": {
                        "path": "actors", #这里需要指定查询的path
                        "query": {
                            "bool": {
                                "must": [{
                                    "term": {
                                        "actors.name": "周迅"
                                    }
                                }]
                            }
                        }
                    }
                },
        {
          "term":{"tag":"民国"}
        }
            ]
        }
    }
}
  • 通过sort对数据做排序
GET /sms-logs-index/_search
{
  "profile": "true", 
  "from": 0, 
  "size": 2, 
  "query": {
    "range": {
      "fee": {
        "gte": 50,
        "lte": 500
      }
    }
  },
  "sort": [
    {
      "fee": {
        "order": "desc"
      }
    }
  ]
}

分页

  • 通过from + size 浅分页, 这种分页方式默认值能查询10000条数据
  • scroll 方式获取深分页的数据, 查询的时候URL上需要有scroll参数表示这个scroll存在的时间,比如5m表示存在5分钟。之后的请求的接口不再使用索引名了,而是 _search/scroll,GET和POST方法都可以使用, 需要重新设置scroll存在的时间在查询的时候, 不能加上"profile": "true", 不然后面的查询会报错:The collector can only be set once 问题的链接在这里:https://github.com/elastic/elasticsearch/issues/27376。scroll 方式获取数据会生成历史快照,对于数据的变更不会反映到快照上。
GET /sms-logs-index/_search?scroll=5m
{
  "from": 0, 
  "size": 2, 
  "query": {
    "range": {
      "fee": {
        "gte": 50,
        "lte": 500
      }
    }
  }
}

GET /_search/scroll
{
   "scroll": "5m",
  "scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDnF1ZXJ5VGhlbkZldGNoBRRwVTRQUm5ZQllORnB0eHdqcVRpMgAAAAAAAAB-FlAxRDB2cU96UzdhbWRQSXAyWWZnZUEUcGs0UFJuWUJZTkZwdHh3anFUaTIAAAAAAAAAfxZQMUQwdnFPelM3YW1kUElwMllmZ2VBFHAwNFBSbllCWU5GcHR4d2pxVGkyAAAAAAAAAIAWUDFEMHZxT3pTN2FtZFBJcDJZZmdlQRRxRTRQUm5ZQllORnB0eHdqcVRpMgAAAAAAAACBFlAxRDB2cU96UzdhbWRQSXAyWWZnZUEUcVU0UFJuWUJZTkZwdHh3anFUaTMAAAAAAAAAghZQMUQwdnFPelM3YW1kUElwMllmZ2VB"
}
  • search_after 深分页; 分页的方式是根据上一页的最后一条数据来确定下一页的位置, 在查询的时候需要有sort, 并且排序的字段组合能有唯一性不然中间查询的时候会有数据丢失, from一定是从零开始。
GET /sms-logs-index/_search
{
  "profile": "true", 
  "from": 0, 
  "size": 2, 
  "query": {
    "range": {
      "fee": {
        "gte": 50,
        "lte": 500
      }
    }
  },
  "sort": [
    {
      "fee": {
        "order": "desc"
      },
      "longCode":{
        "order": "desc"
      }
    }
  ]
}


GET /sms-logs-index/_search
{
  "profile": "true", 
  "from": 0, 
  "size": 2, 
  "query": {
    "range": {
      "fee": {
        "gte": 50,
        "lte": 500
      }
    }
  },
  "search_after":[200, 87454120],
  "sort": [
    {
      "fee": {
        "order": "desc"
      },
      "longCode":{
        "order": "desc"
      }
    }
  ]
}

聚合查询

聚合查询一般用于对数据做统计, 比如每个城市双11消费了多少, 用户使用系统的平均时间是多少。聚合查询分下面几类

指标聚合

  • 对文档的特定字段(field)或脚本值(generated using scripts),计算最大值(max), 最小值(min), 平均值(avg), 求和(sum)。
GET /sms-logs-index/_search
{
  "profile": "true", 
  "aggs": {
    "fee_max": { #聚合的名字
      "max": { #聚合的类型
        "field": "fee"
      }
    },
    "fee_min":{
      "min": {
        "field": "fee"
      }
    },
    "fee_sum":{
      "sum": {
        "field": "fee"
      }
    }
    ,
    "fee_avg":{
      "avg": {
        "field": "fee"
      }
    }
  }
}
  • distinct 聚合(cardinality),统计某个字段有多少种值
GET /sms-logs-index/_search
{
  "profile": "true", 
  "aggs": {
    "fee_count": {
      "cardinality": {
        "field": "fee"
      }
    }
  }
}
  • 统计聚合(stats),统计的值包含 sum, avg , min, max , count
GET /sms-logs-index/_search
{
  "profile": "true", 
  "aggs": {
    "fee_stat": {
      "stats": {
        "field": "fee"
      }
    }
  }
}
  • 百分比统计
GET /sms-logs-index/_search
{
  "profile": "true", 
  "size": 1, 
  "aggs": {
    "fee_percents": {
      "percentiles": {
        "field": "fee",
        "percents": [ #需要统计的百分比
          1,
          10,
          25,
          50,
          75,
          95,
          99
        ]
      }
    }
  }
}
  • 百分比排名聚合, 用于统计类似于: 小费在 200一下的占的百分比是多少。
GET /sms-logs-index/_search
{
  "profile": "true", 
  "size": 1, 
  "aggs": {
    "fee_percents": {
      "percentile_ranks": {
        "field": "fee",
        "values": [200, 2000]
      }
    }
  }
}

桶聚合

对文档进行分组的操作(与sql中的group by类似),把满足相关特性的文档分到一个桶里,即桶分,输出结果往往是一个个包含多个文档的桶(一个桶就是一个group)。

  • Terms Aggregation 词聚合
GET /sms-logs-index/_search
{
  "profile": "true", 
  "size": 0, 
  "query": {
    "term": {
      "province": {
        "value": "杭州"
      }
    }
  }, 
  "aggs": {
    "province_terms": {
      "terms": {
        "field": "province",
        "size": 10,
        "min_doc_count": 4 #只返回文档个数不小于该值的 buckets
      }
    }
  }
}
  • rang 范围聚合
GET /sms-logs-index/_search
{
  "profile": "true", 
  "size": 0, 
  "query": {
    "term": {
      "province": {
        "value": "杭州"
      }
    }
  }, 
  "aggs": {
    "fee_range": {
      "range": {
        "field": "fee",
        "ranges": [
          {
            "from": 0,
            "to": 100
          },
          {
            "from": 100,
            "to": 2000
          }
        ]
      }
    }
  }
}
  • date_range 时间范围查询
GET /sms-logs-index/_search
{
  "size": 0, 
  "aggs": {
    "createDate_range": {
      "date_range": {
        "field": "createDate",
        "ranges": [
          {
            "from": "2020-09-16",
            "to": "2020-09-20"
          },
          {
            "from": "2020-09-21",
            "to": "2020-09-23"
          }
          
        ]
      }
    }
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容