-
安装并运行Elasetisearch
cd elasticsearch-<version> ./bin/elasticsearch
如果你想把 Elasticsearch 作为一个守护进程在后台运行,那么可以在后面添加参数 |
|
如果你是在 Windows 上面运行 Elasticseach,你应该运行 |
测试 Elasticsearch 是否启动成功,可以打开另一个终端,执行以下操作:
curl 'http://localhost:9200/?pretty' 你应该得到和下面类似的响应(response):
{ "name" : "Tom Foster", "cluster_name" : "elasticsearch", "version" : { "number" : "2.1.0", "build_hash" : "72cd1f1a3eee09505e036106146dc1949dc5dc87", "build_timestamp" : "2015-11-18T22:40:03Z", "build_snapshot" : false, "lucene_version" : "5.3.1" }, "tagline" : "You Know, for Search" } 这就意味着你现在已经启动并运行一个 Elasticsearch 节点了,你可以用它做实验了。 单个 节点 可以作为一个运行中的 Elasticsearch 的实例。 而一个 集群 是一组拥有相同cluster.name
的节点, 他们能一起工作并共享数据,还提供容错与可伸缩性。(当然,一个单独的节点也可以组成一个集群) 你可以在elasticsearch.yml
配置文件中 修改cluster.name
,该文件会在节点启动时加载 (译者注:这个重启服务后才会生效)。
-
安装Kibana
启动 Kibana.
-
./bin/kibana
Windows 上启动 kibana:
bin\kibana.bat
。 - 在你的浏览器中打开dev_toos http://localhost:5601/app/kibana#/dev_tools/console?_g=()
-
和Elasticsearch交互
节点客户端(Node client)
节点客户端作为一个非数据节点加入到本地集群中。换句话说,它本身不保存任何数据,但是它知道数据在集群中的哪个节点中,并且可以把请求转发到正确的节点。
传输客户端(Transport client)
轻量级的传输客户端可以将请求发送到远程集群。它本身不加入集群,但是它可以将请求转发到集群中的一个节点上。
两个 Java 客户端都是通过 9300 端口并使用本地 Elasticsearch 传输 协议和集群交互。集群中的节点通过端口 9300 彼此通信。如果这个端口没有打开,节点将无法形成一个集群。
注:Java 客户端作为节点必须和 Elasticsearch 有相同的 主要 版本;否则,它们之间将无法互相理解。
-
RESTful API WITH JSON OVER HTTP
所有其他语言可以使用 RESTful API 通过端口 9200 和 Elasticsearch 进行通信,你可以用你最喜爱的 web 客户端访问 Elasticsearch 。事实上,正如你所看到的,你甚至可以使用 curl
命令来和 Elasticsearch 交互。
注:Elasticsearch 为以下语言提供了官方客户端 --Groovy、JavaScript、.NET、 PHP、 Perl、 Python 和 Ruby--还有很多社区提供的客户端和插件,所有这些都可以在 Elasticsearch Clients 中找到。
一个 Elasticsearch 请求和任何 HTTP 请求一样由若干相同的部件组成:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
被 < >
标记的部件:
|
适当的 HTTP 方法 或 谓词 : |
|
|
|
Elasticsearch 集群中任意节点的主机名,或者用 |
|
运行 Elasticsearch HTTP 服务的端口号,默认是 |
|
API 的终端路径(例如 |
|
任意可选的查询字符串参数 (例如 |
|
一个 JSON 格式的请求体 (如果请求需要的话) |
例如,计算集群中文档的数量,我们可以用这个:
curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } } '
Elasticsearch 返回一个 HTTP 状态码(例如:200 OK`)和(除`HEAD`请求)一个 JSON 格式的返回值。前面的 `curl
请求将返回一个像下面一样的 JSON 体:
{ "count" : 0, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 } }
在返回结果中没有看到 HTTP 头信息是因为我们没有要求curl`显示它们。想要看到头信息,需要结合 `-i
参数来使用 curl
命令:
curl -i -XGET 'localhost:9200/'
在书中剩余的部分,我们将用缩写格式来展示这些 curl
示例,所谓的缩写格式就是省略请求中所有相同的部分,例如主机名、端口号以及 curl
命令本身。而不是像下面显示的那样用一个完整的请求:
curl -XGET 'localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }'
我们将用缩写格式显示:
GET /_count { "query": { "match_all": {} } }
-
面向文档
在应用程序中对象很少只是一个简单的键和值的列表。通常,它们拥有更复杂的数据结构,可能包括日期、地理信息、其他对象或者数组等。
也许有一天你想把这些对象存储在数据库中。使用关系型数据库的行和列存储,这相当于是把一个表现力丰富的对象挤压到一个非常大的电子表格中:你必须将这个对象扁平化来适应表结构--通常一个字段>对应一列--而且又不得不在每次查询时重新构造对象。
Elasticsearch 是 面向文档 的,意味着它存储整个对象或 文档_。Elasticsearch 不仅存储文档,而且 _索引每个文档的内容使之可以被检索。在 Elasticsearch 中,你 对文档进行索引、检索、排序和过滤--而不是对行列数据。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。
-
JSON
Elasticsearch 使用 JavaScript Object Notation 或者 JSON 作为文档的序列化格式。JSON 序列化被大多数编程语言所支持,并且已经成为 NoSQL 领域的标准格式。 它简单、简洁、易于阅读。
考虑一下这个 JSON 文档,它代表了一个 user 对象:
{ "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" }
虽然原始的 user
对象很复杂,但这个对象的结构和含义在 JSON 版本中都得到了体现和保留。在 Elasticsearch 中将对象转化为 JSON 并做索引要比在一个扁平的表结构中做相同的事情简单的多。
注:几乎所有的语言都有相应的模块可以将任意的数据结构或对象 转化成 JSON 格式,只是细节各不相同。具体请查看 serialization 或者 marshalling 这两个 处理 JSON 的模块。官方 Elasticsearch 客户端 自动为您提供 JSON 转化。
-
适应新环境
-
创建新雇员
我们受雇于 Megacorp 公司,作为 HR 部门新的 “热爱无人机” (_"We love our drones!"_)激励项目的一部分,我们的任务是为此创建一个雇员目录。该目录应当能培养雇员认同感及支持实时、高效、动态协作,因此有一些业务需求:
- 支持包含多值标签、数值、以及全文本的数据
- 检索任一雇员的完整信息
- 允许结构化搜索,比如查询 30 岁以上的员工
- 允许简单的全文搜索以及较复杂的短语搜索
- 支持在匹配文档内容中高亮显示搜索片段
- 支持基于数据创建和管理分析仪表盘
-
索引雇员文档
第一个业务需求就是存储雇员数据。 这将会以 雇员文档 的形式存储:一个文档代表一个雇员。存储数据到 Elasticsearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里。
一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。
Index Versus Index Versus Index
你也许已经注意到 索引 这个词在 Elasticsearch 语境中包含多重意思, 所以有必要做一点儿说明:
索引(名词):
如前所述,一个 索引 类似于传统关系数据库中的一个 数据库 ,是一个存储关系型文档的地方。 索引 (index) 的复数词为 indices 或 indexes 。
索引(动词):
索引一个文档 就是存储一个文档到一个 索引 (名词)中以便它可以被检索和查询到。这非常类似于 SQL 语句中的 INSERT
关键词,除了文档已存在时新文档会替换旧文档情况之外。
倒排索引:
关系型数据库通过增加一个 索引 比如一个 B树(B-tree)索引 到指定的列上,以便提升数据检索速度。Elasticsearch 和 Lucene 使用了一个叫做 倒排索引 的结构来达到相同的目的。
+ 默认的,一个文档中的每一个属性都是 被索引 的(有一个倒排索引)和可搜索的。一个没有倒排索引的属性是不能被搜索到的。我们将在 倒排索引 讨论倒排索引的更多细节。
对于雇员目录,我们将做如下操作:
- 每个雇员索引一个文档,包含该雇员的所有信息。
- 每个文档都将是
employee
类型 。 - 该类型位于 索引
megacorp
内。 - 该索引保存在我们的 Elasticsearch 集群中。
实践中这非常简单(尽管看起来有很多步骤),我们可以通过一条命令完成所有这些动作:
PUT /megacorp/employee/1 { "first_name" : "John", "last_name" : "Smith", "age" : 25, "about" : "I love to go rock climbing", "interests": [ "sports", "music" ] }
拷贝为 CURL在 SENSE 中查看
注意,路径 /megacorp/employee/1
包含了三部分的信息:
megacorp
索引名称
employee
类型名称
1
特定雇员的ID
请求体 —— JSON 文档 —— 包含了这位员工的所有详细信息,他的名字叫 John Smith ,今年 25 岁,喜欢攀岩。
很简单!无需进行执行管理任务,如创建一个索引或指定每个属性的数据类型之类的,可以直接只索引一个文档。Elasticsearch 默认地完成其他一切,因此所有必需的管理任务都在后台使用默认设置完成。
进行下一步前,让我们增加更多的员工信息到目录中:
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" ] }
-
检索文档
目前我们已经在 Elasticsearch 中存储了一些数据, 接下来就能专注于实现应用的业务需求了。第一个需求是可以检索到单个雇员的数据。
这在 Elasticsearch 中很简单。简单地执行 一个 HTTP GET
请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档:
GET /megacorp/employee/1
拷贝为 CURL在 SENSE 中查看
返回结果包含了文档的一些元数据,以及 _source
属性,内容是 John Smith 雇员的原始 JSON 文档:
{ "_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 命令由 PUT
改为 GET
可以用来检索文档,同样的,可以使用 DELETE
命令来删除文档,以及使用 HEAD
指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT
。
-
轻量搜索
一个 GET
是相当简单的,可以直接得到指定的文档。 现在尝试点儿稍微高级的功能,比如一个简单的搜索!
第一个尝试的几乎是最简单的搜索了。我们使用下列请求来搜索所有雇员:
GET /megacorp/employee/_search
拷贝为 CURL在 SENSE 中查看
可以看到,我们仍然使用索引库 megacorp
以及类型 employee`,但与指定一个文档 ID 不同,这次使用 `_search
。返回结果包括了所有三个文档,放在数组 hits
中。一个搜索默认返回十条结果。
{ "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
拷贝为 CURL在 SENSE 中查看
我们仍然在请求路径中使用 _search
端点,并将查询本身赋值给参数 q=
。返回结果给出了所有的 Smith:
{ ... "hits": { "total": 2, "max_score": 0.30685282, "hits": [ { ... "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } }, { ... "_source": { "first_name": "Jane", "last_name": "Smith", "age": 32, "about": "I like to collect rock albums", "interests": [ "music" ] } } ] } }
-
使用查询表达式搜索
Query-string 搜索通过命令非常方便地进行临时性的即席搜索 ,但它有自身的局限性(参见 轻量 搜索)。Elasticsearch 提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。
领域特定语言 (DSL), 指定了使用一个 JSON 请求。我们可以像这样重写之前的查询所有 Smith 的搜索 :
GET /megacorp/employee/_search { "query" : { "match" : { "last_name" : "Smith" } } }
拷贝为 CURL在 SENSE 中查看
返回结果与之前的查询一样,但还是可以看到有一些变化。其中之一是,不再使用 query-string 参数,而是一个请求体替代。这个请求使用 JSON 构造,并使用了一个 match
查询(属于查询类型之一,后续将会了解)。
-
更复杂的搜索
现在尝试下更复杂的搜索。 同样搜索姓氏为 Smith 的雇员,但这次我们只需要年龄大于 30 的。查询需要稍作调整,使用过滤器 filter ,它支持高效地执行一个结构化查询。
GET /megacorp/employee/_search { "query" : { "bool": { "must": { "match" : { "last_name" : "smith"
} }, "filter": { "range" : { "age" : { "gt" : 30 }
} } } } }
拷贝为 CURL在 SENSE 中查看
这部分与我们之前使用的 |
|
这部分是一个 |
目前无需太多担心语法问题,后续会更详细地介绍。只需明确我们添加了一个 过滤器 用于执行一个范围查询,并复用之前的 match
查询。现在结果只返回了一个雇员,叫 Jane Smith,32 岁。
{ ... "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/_sea