Elasticsearch 入门介绍

是什么

Elasticsearch 使用 Java 开发并使用 Lucene 做为其核心来实现全部索引和搜索的功能,它的目的是经过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。node

特色:数据库

  • 全文搜索
  • 分布式的实时文件存储,每一个字段都被索引并可被搜索
  • 分布式的实时分析搜索引擎
  • 能够扩展到上百台服务器,处理PB级结构化或非结构化数据
  • 能够经过简单的 RESTful API 与各类开发语言交互
  • 上手简单,对初学者隐藏了复杂的搜索引擎理论
  • 配置灵活

安装

Elasticsearch 在安装以前要安装 Java ,而后从官网中下载 Elasticsearch 的压缩包,在本地的自定义的目录下进行解压便可。另外有一个 Elasticsearch 的管理和监控工具 Marvel ,它包含了一个叫作 Sense 的交互式控制台,使用户方便的经过浏览器直接与 Elasticsearch 进行交互。这个不是必需要安装的。数组

与Elasticsearch交互

  • 若是是用 Java 开发语言,那能够直接使用 Java API,Elasticsearch 为 Java 用户提供了两种内置客户端:节点客户端和传输客户端浏览器

  • 以 JSON 为数据交互格式的 RESTful API ,全部程序语言均可以使用 RESTful API ,经过默认的 9200 端口的与Elasticsearch 进行通讯。向 Elasticsearch 发出的请求的组成部分与其它普通的HTTP请求是同样的:服务器

    curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
      
      VERB HTTP方法:GET, POST, PUT, HEAD, DELETE
      PROTOCOL http或者https协议(只有在Elasticsearch前面有https代理的时候可用)
      HOST Elasticsearch集群中的任何一个节点的主机名,若是是在本地的节点,那么就叫localhost
      PORT Elasticsearch HTTP服务所在的端口,默认为9200
      PATH API路径(例如_count将返回集群中文档的数量),PATH能够包含多个组件,例如_cluster/stats或者_nodes/stats/jvm
      QUERY_STRING 一些可选的查询请求参数,例如?pretty参数将使请求返回更加美观易读的JSON数据
      BODY 一个JSON格式的请求主体(若是请求须要的话)
    复制代码

    举例:数据结构

    curl -XGET 'http://localhost:9200/_count?pretty' -d '
      {
          "query": {
              "match_all": {}
          }
      }
      	'
    复制代码

面向文档

Elasticsearch 是面向文档 (document oriented) 的,这意味着它能够存储整个对象或文档 (document) 。然而它不只仅是存储,还会索引 (index) 每一个文档的内容使之能够被搜索。在 Elasticsearch 中,你能够对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。这种理解数据的方式与以往彻底不一样,这也是 Elasticsearch 可以执行复杂的全文搜索的缘由之一。负载均衡

ELasticsearch 使用 JSON ,做为文档序列化格式。JSON 如今已经被大多语言所支持,并且已经成为NoSQL领域的标准格式。它简洁、简单且容易阅读。curl

JSON 文档来表示一个用户对象:
{
    "email":      "john@smith.com",
    "first_name": "John",
    "last_name":  "Smith",
    "info": {
        "bio":         "Eco-warrior and defender of the weak",
        "age":         25,
        "interests": [ "dolphins", "whales" ]
    },
    "join_date": "2014/05/01"
}
复制代码

索引

在 Elasticsearch 中,文档归属于一种类型 (type) ,而这些类型存在于索引 (index) 中,咱们能够画一些简单的对比图来类比传统关系型数据库:jvm

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices   -> Types  -> Documents -> Fields
Elasticsearch 集群能够包含多个索引 (indices) (数据库),每个索引能够包含多个类型 (types)(表),每个类型包含多个文档 (documents)(行),而后每一个文档包含多个字段 (Fields)(列)。
复制代码

“索引”的含义:分布式

索引 (index) 这个词在 Elasticsearch 中有着不一样的含义,因此有必要在此作一下区分:
索引(名词): 如上文所述,一个索引 (index) 就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是 indices 或 indexes。
索引(动词): “索引一个文档”表示把一个文档存储到索引(名词)里,以便它能够被检索或者查询。这很像SQL中的 INSERT 关键字,差异是,若是文档已经存在,新的文档将覆盖旧的文档。
倒排索引:传统数据库为特定列增长一个索引,例如 B-Tree 索引来加速检索。Elasticsearch 和 Lucene 使用一种叫作倒排索引 (inverted index) 的数据结构来达到相同目的。
复制代码

增长一个员工信息具体操做案例:

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

其中 megacorp 是索引名,employee 是类型名, 1 是员工 ID。
复制代码

而后能够继续试着添加若干个员工:

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" ]
}
复制代码

搜索

  1. 执行 HTTP 的 GET 请求并指出文档的“地址”——索引、类型和 ID 既可:

    GET /megacorp/employee/1
    复制代码

    根据这三部分信息,咱们就能够返回原始 JSON 文档,响应的内容中包含一些文档的元信息。John Smith 的原始 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" ]
       }
     }
    复制代码

    经过 HTTP 方法 GET 来检索文档,一样的,咱们可使用 DELETE 方法删除文档,使用 HEAD 方法检查某文档是否存在。若是想更新已存在的文档,咱们只需再 PUT 一次。

  2. 尝试一个最简单的搜索所有员工的请求,与上一个命令不一样指出是在结尾使用关键字 _search 来取代原来的文档 ID 。响应内容的 hits 数组中包含了咱们全部的三个文档。默认状况下搜索会返回前 10 个结果:

    GET /megacorp/employee/_search
     
     {
        "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” 的员工。能够在命令行中使用轻量级的搜索方法。这种方法常被称做查询字符串 (query string) 搜索,由于咱们像传递 URL 参数同样去传递查询语句:

    GET /megacorp/employee/_search?q=last_name:Smith
    复制代码

    请求中依旧使用 _search 关键字,而后将查询语句传递给参数 q= 。这样就能够获得全部姓氏为 Smith 的结果。

  3. 查询字符串搜索便于经过命令行完成特定的搜索,可是它也有局限性(参阅简单搜索章节)。Elasticsearch 提供丰富且灵活的查询语言叫作 DS L查询 (Query DSL) ,它容许你构建更加复杂、强大的查询。

    DSL (Domain Specific Language 特定领域语言) 以 JSON 请求体的形式出现。咱们能够这样表示以前关于 Smith 的查询:

    GET /megacorp/employee/_search
     {
         "query" : {
             "match" : {
                 "last_name" : "Smith"
             }
         }
     }
    复制代码

    返回的结果和以前查询的结果同样,可是这里不使用查询字符串 (query string) 作为参数,而是使用请求体代替。这个请求体使用 JSON 表示,其中使用了 match 语句。

  4. 若是须要结合更复杂条件进行搜索,则须要添加过滤器,以下:

    GET /megacorp/employee/_search
     {
         "query" : {
             "filtered" : {
                 "filter" : {
                     "range" : {
                         "age" : { "gt" : 30 } <1>
                     }
                 },
                 "query" : {
                     "match" : {
                         "last_name" : "smith" <2>
                     }
                 }
             }
         }
     }
    复制代码

    <1> 这部分查询属于区间过滤器 (range filter) ,它用于查找全部年龄大于 30 岁的数据—— gt 为 "greater than" 的缩写。

    <2> 这部分查询与以前的 match 语句(query)一致。

    结果中只显示了一个员工:

    {
        ...
        "hits": {
           "total":      1,
           "max_score":  0.30685282,
           "hits": [
              {
                 ...
                 "_source": {
                    "first_name":  "Jane",
                    "last_name":   "Smith",
                    "age":         32,
                    "about":       "I like to collect rock albums",
                    "interests": [ "music" ]
                 }
              }
           ]
        }
     }
    复制代码
  5. 到目前为止搜索都很简单:搜索特定的名字,经过年龄筛选。接下来可使用一种更高级的搜索,全文搜索——一种传统数据库很难实现的功能。搜索全部喜欢 “rock climbing” 的员工:

    GET /megacorp/employee/_search
     {
         "query" : {
             "match" : {
                 "about" : "rock climbing"
             }
         }
     }
    复制代码

    获得两个匹配的结果文档:

    {
        ...
        "hits": {
           "total":      2,
           "max_score":  0.16273327,
           "hits": [
              {
                 ...
                 "_score":         0.16273327, <1>
                 "_source": {
                    "first_name":  "John",
                    "last_name":   "Smith",
                    "age":         25,
                    "about":       "I love to go rock climbing",
                    "interests": [ "sports", "music" ]
                 }
              },
              {
                 ...
                 "_score":         0.016878016, <2>
                 "_source": {
                    "first_name":  "Jane",
                    "last_name":   "Smith",
                    "age":         32,
                    "about":       "I like to collect rock albums",
                    "interests": [ "music" ]
                 }
              }
           ]
        }
     }
    复制代码

    默认状况下,Elasticsearch 根据结果相关性评分来对结果集进行排序,所谓的“结果相关性评分”就是文档与查询条件的匹配程度。排名第一的 John Smith 的 about 字段明确的写到 “rock climbing” 。可是为何 Jane Smith 也会出如今结果里呢?缘由是 “rock” 在她的 abuot 字段中被说起了。由于只有 “rock” 被说起而 “climbing” 没有,因此她的 _score 要低于 John 。 这个例子很好的解释了 Elasticsearch 如何在各类文本字段中进行全文搜索,而且返回相关性最大的结果集。相关性 (relevance) 的概念在 Elasticsearch 中很是重要,而这个概念在传统关系型数据库中是不可想象的,由于传统数据库对记录的查询只有匹配或者不匹配。

  6. 目前咱们能够在字段中搜索单独的一个词,可是有时候你想要确切的匹配若干个单词或者短语 (phrases) 。例如咱们想要查询同时包含 "rock" 和 "climbing" (而且是相邻的)的员工记录。 要作到这个,咱们只要将 match 查询变动为 match_phrase 查询便可:

    GET /megacorp/employee/_search
     {
         "query" : {
             "match_phrase" : {
                 "about" : "rock climbing"
             }
         }
     }
    复制代码

    毫无疑问,该查询返回John Smith的文档。

  7. 不少应用喜欢从每一个搜索结果中高亮 (highlight) 匹配到的关键字。在 Elasticsearch 中高亮片断是很是容易的。在语句上增长highlight参数:

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

    最后运行会获得相同的结果,可是在返回结果中会有一个新的部分叫作 highlight ,这里包含了来自 about 字段中的文本。

    {
        ...
        "hits": {
           "total":      1,
           "max_score":  0.23013961,
           "hits": [
              {
                 ...
                 "_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>" <1>
                    ]
                 }
              }
    复制代码

聚合

Elasticsearch 有一个功能叫作聚合 (aggregations) ,它容许你在数据上生成复杂的分析统计。它很像 SQL 中的 GROUP BY 可是功能更强大。举例找全部职员中最大的共同点(兴趣爱好)是什么:

GET /megacorp/employee/_search
{
  "aggs": {
    "all_interests": {
      "terms": { "field": "interests" }
    }
  }
}
复制代码

运行结果:

{
   ...
   "hits": { ... },
   "aggregations": {
      "all_interests": {
         "buckets": [
            {
               "key":       "music",
               "doc_count": 2
            },
            {
               "key":       "forestry",
               "doc_count": 1
            },
            {
               "key":       "sports",
               "doc_count": 1
            }
         ]
      }
   }
}
复制代码

咱们能够看到两个职员对音乐有兴趣,一个喜欢林学,一个喜欢运动。这些数据并无被预先计算好,它们是实时的从匹配查询语句的文档中动态计算生成的。若是咱们想知道全部姓 "Smith" 的人最大的共同点(兴趣爱好),咱们只须要增长合适的语句既可:

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

all_interests 聚合已经变成只包含和查询语句相匹配的文档了:

...
  "all_interests": {
     "buckets": [
        {
           "key": "music",
           "doc_count": 2
        },
        {
           "key": "sports",
           "doc_count": 1
        }
     ]
  }
复制代码

聚合也容许分级汇总。例如让咱们统计每种兴趣下职员的平均年龄:

GET /megacorp/employee/_search
{
    "aggs" : {
        "all_interests" : {
            "terms" : { "field" : "interests" },
            "aggs" : {
                "avg_age" : {
                    "avg" : { "field" : "age" }
                }
            }
        }
    }
}
复制代码

虽然此次返回的聚合结果有些复杂,但任然很容易理解:

...
  "all_interests": {
     "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
           }
        }
     ]
  }
复制代码

能够看出经过这个特性能够完成至关复杂的聚合工做,你能够处理任何类型的数据。

分布式的特性

Elasticsearch 在分布式概念上作了很大程度上的透明化,用户不须要知道任何关于分布式系统、分片、集群发现或者其余大量的分布式概念。既能够运行在你的笔记本上,也能够运行在拥有100个节点的集群上,其工做方式是同样的。

Elasticsearch 致力于隐藏分布式系统的复杂性。如下这些操做都是在底层自动完成的:

将你的文档分区到不一样的容器或者分片 (shards) 中,它们能够存在于一个或多个节点中。
将分片均匀的分配到各个节点,对索引和搜索作负载均衡。
冗余每个分片,防止硬件故障形成的数据丢失。
将集群中任意一个节点上的请求路由到相应数据所在的节点。
不管是增长节点,仍是移除节点,分片均可以作到无缝的扩展和迁移。复制代码
相关文章
相关标签/搜索