Elasticsearch(es)是建立在全文搜索引擎库 Apache Lucene™ 基础上的实时分布式搜索分析引擎。它通过隐藏 Lucene 的复杂性,提供一套简单的 RESTful API。它不仅有Lucene的能力,还是一个分布式的实时文档存储,每个字段可以被索引与搜索、还是一个分布式实时分析搜索引擎、能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据。es常被用作全文检索、结构化搜索、分析以及这三个功能的组合。
es是面向文档的,对文档而不是像结构化数据那样的行列数据进行索引、检索、排序和过滤。它在存储整个对象或文档的同时会索引每个文档的内容,使之可以被检索。正因为es是在面向文档的,才使es能支持复杂的全文检索。es中很重要的一个数据结构是倒排索引(反向索引),它把要搜索的词作为key,把包含该词的文档作为value,由于包含该词的文档不止一个,所以这个value是文档的数组。
一个es集群可以包含多个节点,每个节点可以包含多个索引。相应的每个索引可以包含多个类型(高版本逐渐取消多类型) 。 每个类型存储着多个文档 ,每个文档又有多个字段(es叫域),每个域都有自己的类型。需要注意的是相同索引不同类型下文档的域必须相同。
es域的简单类型有:
关键字 | 说明 |
---|---|
string | 字符串,es5后被移除,被拆成keyword和text |
keyword | 精确匹配的字符串,不会做分词处理 |
text | 全文检索的字符串,分析器会做分词处理 |
byte、short、integer、long | 整型 |
float、double | 浮点数 |
boolean | 布尔型 |
date | 日期 |
es域的复杂类型有:
类型 | 说明 | 示例 |
---|---|---|
多值域 | 域包含多个值,类似数组。用[ ]包围多个值,这些值的类型必须一样 | { "sex": [ "male", "female" ]} |
空域 | 存在null值的域即为空域,包括:null、[]、[null] | |
多层级对象 | 多层JSON对象嵌套 | |
内部对象的映射 | 属性type为"object",属性properties中又是内部的对象映射 |
用法在官方手册已经有很多示例,请参考:
es官网:https://www.elastic.co/cn/
es中文官网:https://www.elastic.co/cn/
es中文文档:https://www.elastic.co/guide/cn/index.html
es参考手册:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
es Github源码:https://github.com/elastic/elasticsearch
集群(cluster)由一个或多个拥有相同cluster.name
配置的节点(实例)组成, 它们共同承担数据和负载的压力。当有新节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。节点有主节点和非主节点之分,主节点负责管理集群范围内所有的变更。
分片(_shards)是数据的容器,被分配到集群内的各个节点里。分片其实就是Lucene实例,所以一个分片就是一个完整的搜索引擎。分片有主分片和副本分片之分,索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。实际上最大数据量还跟硬件、文档的大小和复杂程度、索引和查询文档的方式以及期望的响应时长等有关。主分片的数量在创建时被确定并且不能改变,因为每个文档存储在哪个主分片上是根据公式:shard = hash(routing) % number_of_primary_shards确定的,如果number_of_primary_shards发生了变化,原本保存的文档在主分片的位置就错误了。副本分片只是主分片的拷贝, 作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。
elasticsearch.yml配置说明如下:
配置项 | 说明 |
---|---|
cluster.name | 集群名称 |
node.name | 节点名称 |
node.master | 是否是主节点。es默认集群中的第一台机器为主节点,如果这台机挂了就会重新选主节点 |
node.data | 该节点是否存储索引数据,默认为true |
index.number_of_shards | 索引的主分片个数,默认为5 |
index.number_of_replicas | 索引的副本分片个数,默认为1 |
path.conf | 配置文件的存储路径,默认是es根目录下的config文件夹 |
path.data | 索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径,用逗号隔开 |
path.work | 临时文件的存储路径,默认是es根目录下的work文件夹 |
path.logs | 日志文件的存储路径,默认是es根目录下的logs文件夹 |
path.plugins | 插件的存放路径,默认是es根目录下的plugins文件夹 |
bootstrap.mlockall | 是否锁住内存,因为当jvm开始swapping时es的效率 会降低,所以要保证它不swap |
network.bind_host | 设置绑定的ip地址,可以是ipv4或ipv6的,默认为0.0.0.0 |
network.publish_host | 设置其它节点和该节点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址 |
network.host | 同时设置bind_host和publish_host |
transport.tcp.port | 节点间交互的tcp端口,默认是9300 |
transport.tcp.compress | 是否压缩tcp传输时的数据,默认为false |
http.port | 对外服务的http端口,默认为9200 |
http.max_content_length | 内容的最大容量,默认100mb |
http.enabled | 是否使用http协议对外提供服务,默认为true |
gateway.type | gateway的类型,默认为local即为本地文件系统。可以设置为本地文件系统,分布式文件系统,hadoop的HDFS、amazon的s3服务器以及其它文件系统 |
gateway.recover_after_nodes | 集群中N个节点启动后进行数据恢复,默认为1 |
gateway.recover_after_time | 初始化数据恢复进程的超时时间,默认是5分钟 |
gateway.expected_nodes | 集群中节点的数量,默认为2,一旦这N个节点启动,就会立即进行数据恢复 |
cluster.routing.allocation.node_initial_primaries_recoveries | 初始化数据恢复时,并发恢复线程的个数,默认为4 |
cluster.routing.allocation.node_concurrent_recoveries | 添加删除节点或负载均衡时并发恢复线程的个数,默认为4 |
indices.recovery.max_size_per_sec | 数据恢复时限制的带宽,单位mb,默认为0,即无限制 |
indices.recovery.concurrent_streams | 限制从其它分片恢复数据时最大同时打开并发流的个数,默认为5 |
discovery.zen.minimum_master_nodes | 有主节点资格的节点个数,默认为1 |
discovery.zen.ping.timeout | 自动发现其它节点时ping连接超时时间,默认为3秒 |
discovery.zen.ping.multicast.enabled | 是否打开多播发现节点,默认是true |
discovery.zen.ping.unicast.hosts | 设置主节点的初始列表,可以通过这些节点来自动发现新加入集群的节点 |
action.auto_create_index | 是否自动创建索引 |
action.destructive_requires_name | 是否禁止使用_all和通配符删除索引 |
script.groovy.sandbox.enabled | 是否启用脚本 |
修改配置有两种方式:
1、持久性修改,使用关键字:persistent,集群重启后不失效;
2、临时性修改,使用关键字:transient,集群重启后就会失效;
PUT http://$user:$passwd@$host:$port/_cluster/settings
{
"persistent" : {
配置名 : 值
},
"transient" : {
配置名 : 值
}
}
常用API:
1:查看集群信息:
GET http://$user:$passwd@$host:$port/
2:查看集群的健康信息:
GET http://$user:$passwd@$host:$port/_cluster/health
文档的每个域都有两个重要属性:index和analyzer:
1、index:控制怎样索引字符串,它可以是下面三个值:
1>analyzed:只有string类型的index属性才可能有此值。默认会用分析器进行处理,以便进行全文检索;
2>not_analyzed:不使用分析器处理,所以只能进行精确匹配;
3>no:不索引这个域,所以这个域不会被搜索到;
2、analyzer:指定在搜索和索引时使用的分析器。默认使用标准分析器(standard)分析器。
分析器对文档内容的分析包含以下过程:
1、将文本分成适合倒排索引的独立的词条;
2、将这些词条统一为标准格式以提高它们的“可搜索性”,比如:
1>大小写转换;
2>词根提取;
3>同义词转换;
分析器实际上是将三个功能封装到了一个包里:
1、字符过滤器
字符串按顺序通过每个字符过滤器,过滤器的任务是在分词前整理字符串。一个字符过滤器可以用来去掉HTML,或者将&转化成 `and`;
2、分词器
字符串被分词器分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条;
3、token过滤器
词条按顺序通过每个token过滤器。这个过程可能会改变词条(比如转换大小写),删除词条(比如像`a`,`and
`,
`the
`等无用词),或者增加词条(比如增加同义词)。
es常见的内置分析器有:
1、标准分析器(standard):根据Unicode联盟定义的单词边界划分文本,删除绝大部分标点,最后将词条小写;
2、简单分析器(simple):在任何不是字母的地方分隔文本,将词条小写;
3、空格分析器(whitespace):在空格的地方划分文本;
4、语言分析器:根据指定语言的特点,对文本进行划分;
常用API:
1、查看文本放入字段后的拆词结果:
GET http://$user:$passwd@$host:$port/$index/_analyze
{
"field": 字段名,
"text": 文本内容
}
2、查看文本经过分析器处理后的结果:
GET http://$user:$passwd@$host:$port/$index/_analyze
{
"analyzer": 分析器名,
"text": 文本内容
}
3、创建自定义分析器:
PUT http://$user:$passwd@$host:$port/$index
{
"settings": {
"analysis": {
"analyzer": {
"dash_remove_analyzer": { //创建名为dash_remove_analyzer的分析器,用于去“-”
"tokenizer": "standard",
"stopwords": "_spanish_", //停用词使用预定义的西班牙语停用词列表
"char_filter": [
"dash_remove_char_filter"
]
}
},
"char_filter": {
"dash_remove_char_filter": {
"type": "pattern_replace",
"pattern": "(\\S*)-(\\S*)|(\\S*)",
"replacement": "$1$2"
}
}
}
}
}
常用API:
1:查看所有索引信息
GET http://$user:$passwd@$host:$port/$index/_cat/indices?v
2、查看某个索引信息:
GET http://$user:$passwd@$host:$port/$index
3、查看索引下类型下的映射信息:
GET http://$user:$passwd@$host:$port/$index/_mapping/$type
4:创建索引:
PUT http://$user:$passwd@$host:$port/$index
{
"settings": {
"number_of_shards": 1, //索引的主分片个数,默认值是5,这个配置在索引创建后不能修改
"number_of_replicas" : 0 //每个主分片的副本分片个数,默认值是1,对于活动的索引库,这个配置可以随时修改
},
"mappings": {
"$type": {
"properties": {
字段名1: {
"type": "text",
"analyzer": "english",
"fields": {
"keyword": {//定义分析器:字段名1.keyword
"type": "keyword",
"ignore_above": 512
},
"standard": {//定义分析器:字段名1.standard
"type": "text",
"analyzer": "standard"
}
}
},
字段名2: {
"type": "boolean"
},
字段名3: {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
字段名4: {
"type": "long"
}
}
}
}
}
5:修改索引配置:
PUT http://$user:$passwd@$host:$port/$index/_settings
{
"refresh_interval": "2s"
}
6:新增域:
PUT http://$user:$passwd@$host:$port/$index/_mappings/$type
{
"properties": {
字段名: {
"type": "string",
"index": "not_analyzed"
}
}
}
7:删除索引,可以有多种方式:
DELETE /$index
DELETE /$index0,$index1
DELETE /$index*
DELETE /_all
DELETE /*
文档类似于传统关系数据库中表的一条记录,es用关键字_doc表示。每个文档必须包含三个元素:文档在哪存放(_index)、文档表示的对象类别(_type)、文档唯一标识(_id)。
1、新增或修改:
PUT /$index/$type/$id
{
"name": "tom",
"age": 32,
"about": "I love to go rock climbing",
"interests": [ "music" , "sport"]
}
PUT /$index/$type/$id?version=5&version_type=external //创建或修改成功后的版本号是5的文档
{
"name": "tom",
"age": 32,
"about": "I love to go rock climbing",
"interests": [ "music" , "sport"]
}
新增
POST /$index/$type/
{
"name": "tom",
"age": 32,
"about": "I love to go rock climbing",
"interests": [ "music" , "sport"]
}
新增,如果存在则返回409 Conflict,如果创建成功返回201 Created。
PUT /$index/$type/$id?op_type=create
{
"name": "tom",
"age": 32,
"about": "I love to go rock climbing",
"interests": [ "music" , "sport"]
}
PUT /$index/$type/$id/_create
{
"name": "tom",
"age": 32,
"about": "I love to go rock climbing",
"interests": [ "music" , "sport"]
}
修改
POST /$index/$type/$id/_update
{
"doc":{
"height":185
}
}
使用脚本修改
POST /$index/$type/$id/_update
{
"script" : "ctx._source.height+=1"
}
或者:
POST /$index/$type/$id/_update
{
"script" : "ctx._source.interests+=new_interests",
"params" : {
"new_interests" : "film"
}
}
或者指定ctx.op=delete进行删除:
POST /$index/$type/$id/_update
{
"script" : "ctx.op = ctx._source.height > height ? 'delete' : 'none'",
"params" : {
"height": 185
}
}
指定id的文档如果不存在时需要es自动新增,如果由于版本冲突更新失败时可以用retry_on_conflict指定重试的次数,比如:
POST /$index/$type/$id/_update?retry_on_conflict=5
{
"script" : "ctx._source.height+=1",
"upsert": {
"height": 0
}
}
删除,成功返回200 ok,失败返回404 Not Found
DELETE /$index/$type/$id
_delete_by_query
批量操作:
POST /$index/$type/_bulk
{"index":{"_index":"website","_type":"blog"}}\n
{"title":"Mysecondblogpost"}\n
{"create":{"_index":"website","_type":"blog","_id":"123"}}\n
{"title":"Myfirstblogpost"}\n
{"update":{"_index":"website","_type":"blog","_id":"123","_retry_on_conflict":3}}\n
{"doc":{"title":"Myupdatedblogpost"}}\n
{"delete":{"_index":"website","_type":"blog","_id":"123"}}\n
支持多索引,多类型搜索:
GET /_search?size=5&from=0 //分页查询指定节点下所有索引的文档,每页显示5条,显示第1页
GET /$index1/_search //查询指定节点下索引index1的文档
GET /$index1,$index2/_search //查询指定节点下索引index1、index2的文档
GET /a*,b*/_search //查询指定节点下以a,b开头的索引的文档
GET /$index1/$type1/_search //查询指定节点下索引是index1,类型是type1的文档
GET /$index1,$index2/$type1,$type2/_search //查询指定节点下索引是index1和index2,类型是type1和type2的文档
GET /_all/$type1,$type2/_search //查询指定节点下所有索引中类型是type1和type2的文档
GET /$index/$type/$id?pretty //pretty方式展示文档信息
GET /$index/$type/$id?_source=title,text //只展示title和text字段
GET /$index/$type/$id?_source //只返回_source中的字段
如果存在指定id的文档,则返回200 ok的状态码,否则返回404 Not Found的状态码。
GET /index/type/_search //查询所有文档
GET /_search?timeout=10ms //10ms必须返回,所以返回的文档可能为空
轻量搜索,查name包含tom的文档,条件与条件之间空格分隔,+前缀表示必须与查询条件匹配,-前缀表示一定不与查询条件匹配,没有+或者-的所有其他条件都是可选的(匹配的越多,文档就越相关)。
GET /index/type/_search?q=+title:完善 -content:认证
轻量搜索,查不管哪个字段,包含tom的文档。
GET /index/type/_search?q=认证
GET /index/type/_search?q=+name:(mary john) +date:>2014-09-10 +(aggregations geo)
请求体查询,在es中是支持GET请求的请求体的。但因为带请求体的GET请求并不被广泛支持,所以同时支持POST请求的请求体。
空查询:
GET /_search
{}
分页
GET /_search
{
"from": 30,
"size": 10
}
查询表达式:
match_all:常用于空查询或与filter结合使用:
GET /_search
{
"query": {
"match_all": {}
}
}
match:根据字段是否是text类型,决定是否用分词器进行分词:
GET /_search
{
"query" : {
"match" : {
"name" : "tom"
}
}
}
multi_match:可以在多个字段上执行相同的match查询:
{
"multi_match": {
"query": "full text search",
"fields": [ "title", "body" ]
}
}
match_phrase:查询短语的match查询:
{
"match_phrase": {
"content": {
"query": "my name is Tom",
"slop": 1
}
}
}
range:找出那些落在指定区间内的数字或者时间,可以使用:gt(大于)、gte(大于等于)、lt(小于)、lte(小于等于):
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
term: 用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些not_analyzed的字符串,区分大小写:
{"term": {"age": 26}}
{"term": {"date": "2014-09-01"}}
{"term": {"public": true}}
{"term": {"tag": "full_text"}}
terms:用于精确值匹配,但它允许你指定多值进行匹配,区分大小写。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件:
{"terms": {"tag": ["search","full_text","nosql"]}}
exists、missing:用于查询指定字段中是否有值,相当于sql中的IS NOT NULL和IS NULL:
{
"exists": {
"field": "title"
}
}
GET /index/type/_search
{
"query" : {
"match" : { //查询表达式,查name是tom的文档
"name" : "tom"
}
},
"highlight": { //高亮显示
"fields" : {
"about" : {}
}
}
}
复合查询
GET /index/type/_search
{
"query" : {
"bool": {
"must": {
"match" : { //查name是tom
"name" : "tom"
}
},
"filter": { //过滤器,并且年龄大于30
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
GET /index/type/_search
{
"query" : {
"match" : { //全文检索:即查询包含rock、climbing或两个词都包含(即使顺序颠倒)的文档
"about" : "rock climbing"
}
}
}
GET /index/type/_search
{
"query" : {
"match_phrase" : { //短语搜索,即查询包含整个短语的文档,只包含单个词的不会查到
"about" : "rock climbing"
}
}
}
GET /index/type/_search
{
"aggs": { //聚合,查字段interests中每个元素出现的次数
"all_interests": {
"terms": { "field": "interests" }
}
}
}
GET /index/type/_search
{
"aggs" : {
"all_interests" : {
"terms" : { "field" : "interests" },
"aggs" : { //分级聚合,查询每个interests元素中,age的平均值
"avg_age" : {
"avg" : { "field" : "age" }
}
}
}
}
}
同时获取多个文档,必须通过docs数组指定_index,_type以及_id,还可以使用_source指定文档内容返回的字段。
GET /_mget
{
"docs" : [
{
"_index" : "website",
"_type" : "blog",
"_id" : 2
},
{
"_index" : "website",
"_type" : "pageviews",
"_id" : 1,
"_source": "views"
}
]
}
如果是相同的_index或_type,可以统一放在URL的path中
GET /$index/$type/_mget
{
"docs" : [
{
"_id" : 1
},
{
"_id" : 2,
"_source": "views"
}
]
}
如果只有ID不同,可以使用ids代替docs:
GET /$index/$type/_mget
{
"ids" : [ "1", "2" ]
}