Elasticsearch 使用 Java 开发并使用 Lucene 做为其核心来实现全部索引和搜索的功能,它的目的是经过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。node
特色:数据库
Elasticsearch 在安装以前要安装 Java ,而后从官网中下载 Elasticsearch 的压缩包,在本地的自定义的目录下进行解压便可。另外有一个 Elasticsearch 的管理和监控工具 Marvel ,它包含了一个叫作 Sense 的交互式控制台,使用户方便的经过浏览器直接与 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" ]
}
复制代码
执行 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 一次。
尝试一个最简单的搜索所有员工的请求,与上一个命令不一样指出是在结尾使用关键字 _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 的结果。
查询字符串搜索便于经过命令行完成特定的搜索,可是它也有局限性(参阅简单搜索章节)。Elasticsearch 提供丰富且灵活的查询语言叫作 DS L查询 (Query DSL) ,它容许你构建更加复杂、强大的查询。
DSL (Domain Specific Language 特定领域语言) 以 JSON 请求体的形式出现。咱们能够这样表示以前关于 Smith 的查询:
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
复制代码
返回的结果和以前查询的结果同样,可是这里不使用查询字符串 (query string) 作为参数,而是使用请求体代替。这个请求体使用 JSON 表示,其中使用了 match 语句。
若是须要结合更复杂条件进行搜索,则须要添加过滤器,以下:
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" ]
}
}
]
}
}
复制代码
到目前为止搜索都很简单:搜索特定的名字,经过年龄筛选。接下来可使用一种更高级的搜索,全文搜索——一种传统数据库很难实现的功能。搜索全部喜欢 “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 中很是重要,而这个概念在传统关系型数据库中是不可想象的,由于传统数据库对记录的查询只有匹配或者不匹配。
目前咱们能够在字段中搜索单独的一个词,可是有时候你想要确切的匹配若干个单词或者短语 (phrases) 。例如咱们想要查询同时包含 "rock" 和 "climbing" (而且是相邻的)的员工记录。 要作到这个,咱们只要将 match 查询变动为 match_phrase 查询便可:
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}
}
复制代码
毫无疑问,该查询返回John Smith的文档。
不少应用喜欢从每一个搜索结果中高亮 (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) 中,它们能够存在于一个或多个节点中。
将分片均匀的分配到各个节点,对索引和搜索作负载均衡。
冗余每个分片,防止硬件故障形成的数据丢失。
将集群中任意一个节点上的请求路由到相应数据所在的节点。
不管是增长节点,仍是移除节点,分片均可以作到无缝的扩展和迁移。复制代码