ElasticSearch(一)--入门

安装好ElasticSearch及其Kibana,Marvel,Sense插件之后,就可以简单的使用ES了.

1. 使用简述

使用ES,是基于HTTP协议以及JSON为数据交互格式的.

使用Linux终端命令curl对ES进行HTTP请求,格式:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
其中,

VERB,是HTTP请求的方法,有GET,POST,PUT,HEAD,DELETE.

PROTOCOL,有http,或者https.

HOST,部署在ES集群系统中的主机地址,本地节点使用localhost

PORT,ES服务器所使用的端口号,默认为9200

PATH,路径,不同的路径对应不同的功能,例如_count返回集群中文件的数量

QUERY_STRING,可选的查询参数,例如pretty参数,可以使返回的数据更加美观,在linux终端中可以体现.

BODY,是基于JSON格式的请求体.

例如,在linux终端输入命令:

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
    "query": {
        "match_all": {}
    }
}
'
该命令查询集群中文件的数量,ES返回一个类似于200 OK的HTTP状态码,以及基于JSON格式的响应主体,响应主体如下:

{
    "count" : 32330,
    "_shards" : {
        "total" : 4,
        "successful" : 4,
        "failed" : 0
    }
}

通过在curl命令加参数i,可以得到响应的头部和主体:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 99

{
  "count" : 32700,
  "_shards" : {
    "total" : 4,
    "successful" : 4,
    "failed" : 0
  }
}

在Sense中对ES进行交互时,命令格式简写如下:

GET /_count
{
    "query": {
        "match_all": {}
    }
}

2. 面向文档及序列化

ElasticSearch是面向文档的,它可以存储整个对象或文档,而不是是像传统数据库一样只能存储行列的表,不仅仅是存储,ES还会索引每个文档的内容使之可以被搜索.

在ES中,可以对文档进行索引,检索,排序,过滤.这种理解数据的方式与以往完全不同,这也是ES能够执行复杂的全文搜索的原因之一.

ES对文档进行序列化,使用Java对象符号JSON(JavaScript Object Notation)作为文档序列化后的格式.

几乎所有的语言都有相应的模块将任意数据结构转换为JSON格式.


通过例子,介绍概念:索引indexing,搜索search以及聚合aggregations

例:为公司Megacorp创建一个员工目录,实现如下需求:

数据能够包含多个值的标签、数字和纯文本。

检索任何员工的所有信息。

支持结构化搜索,例如查找30岁以上的员工。

支持简单的全文搜索和更复杂的短语(phrase)

搜索高亮搜索结果中的关键字

能够利用图表管理分析这些数据

3. 索引

首先,中文索引分两个含义:名词和动词.名词对应英文的index,动词对应英文的indexing.

ES存储中是按照:索引index->类型type->文档document->字段field 级别层次划分的.

这可以和传统的数据库做类比:数据库Database->表Table->行,列

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices   -> Types  -> Documents -> Fields
ES集群可以包含多个索引,每个索引可以包含多个类型,每个类型可以包含多个文档,每个文档可以包含多个字段.

动词索引,索引一个文档,表示把一个文档存储到索引Index里,可以用来查询和检索,

索引一个文档时,如果文档已经存在,新的文档将覆盖旧的文档

默认情况下,文档中的所有字段都会被索引,只有这样才可以被用来检索.ES使用一种称为"倒排索引"的数据结构,加速ES的检索,如同传统数据库中,为数据表的列增加索引一样.

现在执行例子,根据需求,作出如下操作:

为每个员工的文档建立索引,每个文档中包含了员工的所有信息;

定义每个文档所属的类型Type为employee;

employee类型归属与索引megacorp;

megacorp索引存储在ES集群中;

通过使用PUT的方式,为ES集群添加文档,加入三个员工的文档:

员工1

PUT /megacorp/employee/1
{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        25,
    "about" :      "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}
员工2,3

PUT /megacorp/employee/2
{
    "first_name" :  "Jane",
    "last_name" :   "Smith",
    "age" :         32,
    "about" :       "I like to collect rock albums",
    "interests":  [ "music" ]
}

PUT /megacorp/employee/3
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}

在使用 /megacorp/employee/1 这样的层次方式中,也就定义了索引,类型,文档的信息.

4. 搜索

现在ES集群中已经存储了一些员工的信息,下边进行各种业务的需求的操作.

检索单个员工的信息,也就是检索一个文档,使用HTTP GET请求+文档地址:索引/类型/文档 的方式:

GET /megacorp/employee/1
返回原始的JSON文档,存储在_source字段中:

{
  "_index" :   "megacorp",
  "_type" :    "employee",
  "_id" :      "1",
  "_version" : 1,
  "found" :    true,
  "_source" :  {
      "first_name" :  "John",
      "last_name" :   "Smith",
      "age" :         25,
      "about" :       "I love to go rock climbing",
      "interests":  [ "sports", "music" ]
  }
}
通过GET方法获取文档,通过DELETE请求删除文档,HEAD请求检查文档是否存在,更新文档只需再PUT一次.

如果需要返回所有员工的文档,使用_search替代GET方法中的文档名:

GET /megacorp/employee/_search
响应内容的hits数组字段包含了我们加入的三个文档,默认情况搜索返回前10个结果.
{
   "took":      6,
   "timed_out": false,
   "_shards": { ... },
   "hits": {
      "total":      3,
      "max_score":  1,
      "hits": [
         {
            "_index":         "megacorp",
            "_type":          "employee",
            "_id":            "3",
            "_score":         1,
            "_source": {
               "first_name":  "Douglas",
               "last_name":   "Fir",
               "age":         35,
               "about":       "I like to build cabinets",
               "interests": [ "forestry" ]
            }
         },
         {
            "_index":         "megacorp",
            "_type":          "employee",
            "_id":            "1",
            "_score":         1,
            "_source": {
               "first_name":  "John",
               "last_name":   "Smith",
               "age":         25,
               "about":       "I love to go rock climbing",
               "interests": [ "sports", "music" ]
            }
         },
         {
            "_index":         "megacorp",
            "_type":          "employee",
            "_id":            "2",
            "_score":         1,
            "_source": {
               "first_name":  "Jane",
               "last_name":   "Smith",
               "age":         32,
               "about":       "I like to collect rock albums",
               "interests": [ "music" ]
            }
         }
      ]
   }
}
响应内容不但不含了哪些文档被匹配到,还完整包含了这些文档,方便向用户展示.

接下来,我们搜索姓氏中包含"Smith"的员工信息,通过为HTTP请求加参数的方式,成为查询字符串query string的搜索

GET /megacorp/employee/_search?q=last_name:Smith
q=字段:检索值

响应:

{
  "took": 58,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.30685282,
    "hits": [
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "2",
        "_score": 0.30685282,
        "_source": {
          "first_name": "Jane",
          "last_name": "Smith",
          "age": 32,
          "about": "I like to collect rock albums",
          "interests": [
            "music"
          ]
        }
      },
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "1",
        "_score": 0.30685282,
        "_source": {
          "first_name": "John",
          "last_name": "Smith",
          "age": 25,
          "about": "I love to go rock climbing",
          "interests": [
            "sports",
            "music"
          ]
        }
      }
    ]
  }
}
DSL语句查询

DSL(Domian in Specific Language特定领域语言)以JSON格式请求体的形式.

使用DSL替代上边查询字符串的方法,查询信息写在请求体内:

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}
query , match都是特定的语句.

结构化查询

稍微复杂点的查询,得到年龄大于30岁的姓氏为Smith的员工,使用过滤器filter,进行结构化搜索:

GET /megacorp/employee/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "range" : {
                    "age" : { "gt" : 30 } <1>
                }
            },
            "query" : {
                "match" : {
                    "last_name" : "smith" <2>
                }
            }
        }
    }
}
可以看出这样的语句层次:query->filtered->filter+query

其中,filter部分属于区间过滤器,用于查询所有年龄大于30岁的员工,"gt"为"greater than"的缩写

filter后的query->match语句就是在信息经过过滤器之后,进行匹配查询.

响应结果,只得到了一个年龄为32岁的姓氏Smith的员工:

{
  "took": 15,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.30685282,
    "hits": [
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "2",
        "_score": 0.30685282,
        "_source": {
          "first_name": "Jane",
          "last_name": "Smith",
          "age": 32,
          "about": "I like to collect rock albums",
          "interests": [
            "music"
          ]
        }
      }
    ]
  }
}
全文搜索
全文搜索是一种更高级的搜索,传统数据库很难实现这样的功能.

搜索所有喜欢"rock climbing"的员工,使用之前的qury->match语句,为about字段设定检索值:

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "about" : "rock climbing"
        }
    }
}
响应结果给出了两个匹配文档:

{ ...
  "hits": {
    "total": 2,
    "max_score": 0.16273327,
    "hits": [
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "1",
        "_score": 0.16273327,
        "_source": {
          "first_name": "John",
          "last_name": "Smith",
          "age": 25,
          "about": "I love to go rock climbing",
          "interests": [
            "sports",
            "music"
          ]
        }
      },
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "2",
        "_score": 0.016878016,
        "_source": {
          "first_name": "Jane",
          "last_name": "Smith",
          "age": 32,
          "about": "I like to collect rock albums",
          "interests": [
            "music"
          ]
        }
      }
    ]
  }
}
默认情况下,ES根据相关性评分对结果集进行排序,相关性就是文档与查询条件匹配的程度.John Smith和Jane Smith都在结果集中,John的about字段因为有"rock climbing",所以其相关性分数_score最高,而Jane中的about字段提到了"rock",所以也被检索出,但相关性低.

相关性的概念在ES中非常重要.

短语搜索:前边通过语句match进行,有时候需要精确匹配若干个单词或者一个短语phrase,使用match_phrase语句.

查询同时包含rock和climbing的(并且是相邻的)员工记录:

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}
那么就只有John被匹配:

{
  ...
  "hits": {
    "total": 1,
    "max_score": 0.23013961,
    "hits": [
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "1",
        "_score": 0.23013961,
        "_source": {
          "first_name": "John",
          "last_name": "Smith",
          "age": 25,
          "about": "I love to go rock climbing",
          "interests": [
            "sports",
            "music"
          ]
        }
      }
    ]
  }
}
高亮关键字

很多应用需要在搜索结果中高亮highlight匹配到的关键字,这样用户可以知道为什么这些文档和查询相匹配.

ES中通过highlight语句的方式高亮片段:

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    },
    "highlight": {
        "fields" : {
            "about" : {}
        }
    }
}

响应结果中,增加了highlight字段,这里包含了about字段的文本,并用<em></em>来标识匹配到的单词.

{
  ...
  "hits": {
    "total": 1,
    "max_score": 0.23013961,
    "hits": [
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "1",
        "_score": 0.23013961,
        "_source": {
          "first_name": "John",
          "last_name": "Smith",
          "age": 25,
          "about": "I love to go rock climbing",
          "interests": [
            "sports",
            "music"
          ]
        },
        "highlight": {
          "about": [
            "I love to go <em>rock</em> <em>climbing</em>"
          ]
        }
      }
    ]
  }
}

5. 聚合

聚合aggregation是指对搜索的结果进行分析,得到需要的统计数据,就像传统数据库中GROUP BY语句一样.

查询所有员工中最大的共同兴趣爱好是什么:

GET /megacorp/employee/_search
{
  "aggs": {
    "all_interests": {
      "terms": { "field": "interests" }
    }
  }
}
暂时忽略不理解的语法,后边逐渐学习,现主要直观的认识ES所提供的功能.

响应结果不但返回匹配的文档信息,还对文档信息进行了分析,分析结果在aggregations字段中:

{
  ...
  "hits": {
    "total": 3,
    "max_score": 1,
    "hits": [
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "2",
        "_score": 1,
        "_source": {
          "first_name": "Jane",
          "last_name": "Smith",
          "age": 32,
          "about": "I like to collect rock albums",
          "interests": [
            "music"
          ]
        }
      },
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "1",
        "_score": 1,
        "_source": {
          "first_name": "John",
          "last_name": "Smith",
          "age": 25,
          "about": "I love to go rock climbing",
          "interests": [
            "sports",
            "music"
          ]
        }
      },
      {
        "_index": "megacorp",
        "_type": "employee",
        "_id": "3",
        "_score": 1,
        "_source": {
          "first_name": "Douglas",
          "last_name": "Fir",
          "age": 35,
          "about": "I like to build cabinets",
          "interests": [
            "forestry"
          ]
        }
      }
    ]
  },
  "aggregations": {
    "all_interests": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "music",
          "doc_count": 2
        },
        {
          "key": "forestry",
          "doc_count": 1
        },
        {
          "key": "sports",
          "doc_count": 1
        }
      ]
    }
  }
}

从aggregations字段中,可以看出喜欢music的职员有2个,喜欢forestry的一个,喜欢sports的一个

如果我们想知道所有姓Smith的职员中,兴趣爱好的分析,只需在查询Smith的语句后,加上聚合语句:

GET /megacorp/employee/_search
{
  "query": {
    "match": {
      "last_name": "smith"
    }
  },
  "aggs": {
    "all_interests": {
      "terms": {
        "field": "interests"
      }
    }
  }
}

分级汇总

分级汇总,在聚合一层次之后,再进行聚合.

查询所有职员的兴趣爱好,并得到每种兴趣爱好的职员的平均年龄:

GET /megacorp/employee/_search
{
    "aggs" : {
        "all_interests" : {
            "terms" : { "field" : "interests" },
            "aggs" : {
                "avg_age" : {
                    "avg" : { "field" : "age" }
                }
            }
        }
    }
}
响应结果:

{
  ...
  "hits": {
    ...
  },
  "aggregations": {
    "all_interests": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "music",
          "doc_count": 2,
          "avg_age": {
            "value": 28.5
          }
        },
        {
          "key": "forestry",
          "doc_count": 1,
          "avg_age": {
            "value": 35
          }
        },
        {
          "key": "sports",
          "doc_count": 1,
          "avg_age": {
            "value": 25
          }
        }
      ]
    }
  }
}
从结果看,爱好music的员工有2个,这两个人的平均年龄为28.5.

ES这个特性可以完成相当复杂的聚合工作.

6. 分布式

ES可以扩展到上百甚至上千的数量的服务器上,处理PB级别的数据.

ES为分布式而生,它的设计隐藏了分布式本身的复杂性.

Elasticsearch在分布式概念上做了很大程度上的透明化,在教程中你不需要知道任何关于分布式系统、分片、集群发现或者其他大量的分布式概念。

所有的教程你即可以运行在你的笔记本上,也可以运行在拥有100个节点的集群上,其工作方式是一样的。

ES致力于隐藏分布式系统的复杂特性,这些操作都是在底层完成的:

1. 将文档分区到不同的容器或者分片Shards中,它们可以存储在一个或多个节点中.

2. 将分片均匀的分配到各个节点中,对索引和搜索做负载均衡

3. 冗余每一个分片,防止因硬件损坏造成的数据丢失

4. 将集群中任意一个节点上的请求,路由到相应数据所在的节点

5. 无论是增加节点,还是移除节点,分片都能做到无缝的扩展和迁移

关于分布式的内容,教给你如何扩展集群,故障转移,处理文档存储,执行分布式搜索,分片是什么以及如何工作.

这些关于分布式的内容不是必读的,这些都是内部机制,不懂这些内部机制也可以使用ES,但是这些机制可以是你更深入和完整的了解ES.

可以暂时忽略它们,在需要深入理解的时候,再回头翻阅.

猜你喜欢

转载自blog.csdn.net/WuyZhen_CSDN/article/details/51379091