在今天的文章中,我们来做一个简单的 Logstash 体验,并体验 Geo Search。我们的数据是中国区里的所有的 zipcode,也就是邮政编码。
安装
如果你还没有安装自己的 Elastic Stack:
- 请参阅文章 “Elastic:菜鸟上手指南” 来安装 Elasticsearch 及 Kibana。
- 请参阅文章 “如何安装Elastic栈中的Logstash” 安装 Logstash
在上面的安装中,我们都是安装默认缺省的方式来进行安装的。Elasticsearch 运行于 localhost:9200,而Kibana运行于 localhost:5601 上。
下载数据
我们通过如下的命令来下载我们数据:
git clone https://github.com/liu-xiao-guo/elasticzipcodes
通过上面的下载,我们可以得到一个叫做 zipcodes.csv 的文件。它的文件的内容如下:
从上面的表格中,我们可以看到:在这个 zipcodes.csv 的文件中,它含有 Id, Code, AreaCode, Name, ShortName, Logitude, Latitude, Sort, Memo, Disabled 字段。我们把这个下载的文件置于我们喜欢的一个文件目录中。
导入数据
为了把这数据导入到 Elasticsearch 中,我们使用 Logstash。为此,我们写一个我们自己的 logstash 配置文件:
logstash_zipcodes.conf
input {
file {
path => "/Users/liuxg/data/zipcodes/zipcodes.csv"
start_position => "beginning"
sincedb_path => "/dev/null"
}
}
filter {
csv {
separator => ","
columns => ["Id", "Code", "AreaCode", "Name", "ShortName", "Longitude", "Latitude", "Sort", "Memo", "Disabled"]
}
mutate {
convert => {"Longitude" => "float"}
convert => {"Latitude" => "float"}
add_field => ["location", "%{Latitude},%{Longitude}"]
rename => ["Code", "zipcode"]
}
}
output {
stdout {codec => rubydebug}
elasticsearch {
index => "zipcodes"
hosts => ["http://localhost:9200"]
}
}
我们知道 Logstash 的管道由三个部分组成:inputs,filters 及 outputs。
在上面的 input 部分,我们使用file来输入我们指定路径的文件 zipcodes.csv。根据你自己的实际的这个文件的路径,我们需要做相应的调整。
在 filters 部分,我们通过csv过滤器来提前相应的字段。同时我们使用 mutate 过滤器来对我们的数据进行类型的转换及重新命名。我们通过 add_fields 来创建一个新的字段叫做location,它由经纬度组成。
在我们的输出部分,我们通过 stdout 输出到我们的标准输出。这在很多的情况下,可以帮助我们调试我们的 Logstash 管道很用用。同时,我们把输出导入到我们的本地的 Elasticsearch 中。我们的数据将会保存于一个叫 zipcodes 的索引中。
为了能够使 Elasticsearch 帮我们生产的的 zipcodes 能适合我们的需求,我们在 Kibana 中打入如下的命令:
PUT _template/zipcodes
{
"order": 10,
"index_patterns": [
"zipcodes*"
],
"settings": {
"number_of_replicas": 0,
"number_of_shards": 1
},
"mappings": {
"properties": {
"zipcode": {
"type": "text"
},
"location": {
"type": "geo_point"
}
}
},
"aliases": {}
}
上面索引 template 表明:但凡 index pattern 为 zipcodes* 的索引将具有以上所定义的 settings,mappings 及 alias。在上面,我们把我们的 location 定义为一个 geo_point 的数据类型。
我们可以通过如下的命令来启动 Logstash:
sudo ./bin/logstash -f ~/data/zipcodes/logstash_zipcodes.conf
等我们的 Logstash 启动后,我们可以在 terminal 中看到如下的输出
从上面我们看出 Logstash 帮我们生产的各种字段。
我们可以打开我们的 Kibana,并打入如下的指令:
GET zipcodes/_count
我们可以看到如下的结果:
{
"count" : 42358,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
在上面,我们可以看到我们有 42358 个文档。
搜索数据 - Geo search
在上面我们已经看到我们导入的 Geo 数据,我们可以通过如下的命令来对我们的数据进行查询:
GET zipcodes/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
}
],
"filter": {
"geo_distance": {
"distance": "1km",
"location": {
"lat": 39.920086,
"lon": 116.454182
}
}
}
}
}
}
在上面,我们查询在以坐标(116.454182, 39.920086)我中心方圆一公里的所有文档。显示的结果是:
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "zipcodes",
"_type" : "_doc",
"_id" : "rvG12HABqA-NWEvj7Dj0",
"_score" : 1.0,
"_source" : {
"Memo" : null,
"Latitude" : 39.920929,
"Name" : "呼家楼街道",
"message" : "35,110105003,110105,呼家楼街道,呼家楼街道,116.464325,39.920929,16,,false",
"Sort" : "16",
"host" : "liuxg",
"AreaCode" : "110105",
"location" : "39.920929,116.464325",
"path" : "/Users/liuxg/data/zipcodes/zipcodes.csv",
"ShortName" : "呼家楼街道",
"Id" : "35",
"Longitude" : 116.464325,
"@version" : "1",
"@timestamp" : "2020-03-14T11:02:43.681Z",
"zipcode" : "110105003",
"Disabled" : "false"
}
}
]
}
当然,我们也可以修改上面的 distance 来调整,从而得到更多的结果。
同样的,我们也可以针对定义好的一个长方形区域来进行搜索:
GET zipcodes/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
}
],
"filter": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 39.94086,
"lon": 116.454182
},
"bottom_right": {
"lat": 39.930086,
"lon": 116.464182
}
}
}
}
}
}
}
在上面,我们定义了两个坐标位置。这两个位置定义了一个矩形的区域。上面的搜索将显示在这个区域里的所有的文档:
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "zipcodes",
"_type" : "_doc",
"_id" : "MfG12HABqA-NWEvj7Diy",
"_score" : 1.0,
"_source" : {
"Memo" : null,
"Latitude" : 39.931461,
"Name" : "团结湖街道",
"message" : "45,110105013,110105,团结湖街道,团结湖街道,116.462578,39.931461,35,,false",
"Sort" : "35",
"host" : "liuxg",
"AreaCode" : "110105",
"location" : "39.931461,116.462578",
"path" : "/Users/liuxg/data/zipcodes/zipcodes.csv",
"ShortName" : "团结湖街道",
"Id" : "45",
"Longitude" : 116.462578,
"@version" : "1",
"@timestamp" : "2020-03-14T11:02:43.684Z",
"zipcode" : "110105013",
"Disabled" : "false"
}
}
]
}
上面显示的结果是在我们定义的区域里的所有的文档。
展示数据
在上面我们可以很方便地搜索我们的数据。我们同时也可以使用Kibana提供的强大的图像化工具来展示我们所有的文档。为此,我们首先来创建一个叫做 zipcodes 的 index pattern:
点击上面的 “Create index pattern”:
点击上面的 “Next step” 按钮:
因为这个不是一个时序的文档,所以我们选择 “I don't want to use the Timer Filter”。点击 “Create index pattern”:
这样我们就创建了我们的 zipcodes* index pattern。
接下来我们为我们的 zipcodes 创建一个 Visualization:
点击上面的 “Create visualization”按钮:
我们选择 “Maps”:
点击上面的 “Add layer”按钮:
点击上面的 “Documents”:
点击上面的“Add layer”:
我们添加 tooltips 为 zipcode 及 ShortName:
点击上面的 “Save & close” 按钮:
我们从上面可以看到所有的文档都显示在地图的中国的位置。我们放大我们的地图,可以看到如下的画面:
当我们的鼠标放置于一个文档上后,它就可以显示出当前位置的名字及 zipcode。
我们可以同时利用地图上面的工具来定义一个我们想要的区域来进行搜索:
我们通过运用鼠标来进行定义一个区域:
选择 Draw shape:
我们可以在我们喜欢的区域用鼠标来标出我们想搜索的区域,最终我们形成一个 filter,从而只显示出该区域里的文档:
选择上面的filter:location in shape:
选择上面的 Edit filter,我们可以看到:
在上面我们可以看到用 DSL 搜索所需要的 geo_polygon 用到的点:
GET zipcodes/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
}
],
"filter": {
"geo_polygon": {
"ignore_unmapped": true,
"location": {
"points": [
{
"lat": 40.05915,
"lon": 115.95216
},
{
"lat": 39.86638,
"lon": 116.22583
},
{
"lat": 40.08309,
"lon": 116.31184
},
{
"lat": 40.05915,
"lon": 115.95216
}
]
}
}
}
}
}
}
上面的查询的结果将显示13个文档:
"hits" : {
"total" : {
"value" : 13,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "zipcodes",
"_type" : "_doc",
"_id" : "svG12HABqA-NWEvj7Dj0",
"_score" : 1.0,
"_source" : {
"Memo" : null,
"Latitude" : 39.916122,
"Name" : "八角街道",
"message" : "99,110107003,110107,八角街道,八角街道,116.195648,39.916122,2,,false",
"Sort" : "2",
"host" : "liuxg",
"AreaCode" : "110107",
"location" : "39.916122,116.195648",
"path" : "/Users/liuxg/data/zipcodes/zipcodes.csv",
"ShortName" : "八角街道",
"Id" : "99",
"Longitude" : 116.195648,
"@version" : "1",
"@timestamp" : "2020-03-14T11:02:43.694Z",
"zipcode" : "110107003",
"Disabled" : "false"
}
},
{
"_index" : "zipcodes",
"_type" : "_doc",
"_id" : "MfG12HABqA-NWEvj7Tk5",
"_score" : 1.0,
"_source" : {
"Memo" : null,
"Latitude" : 40.003696,
"Name" : "军庄镇",
"message" : "143,110109104,110109,军庄镇,军庄镇,116.103455,40.003696,5,,false",
"Sort" : "5",
"host" : "liuxg",
"AreaCode" : "110109",
"location" : "40.003696,116.103455",
"path" : "/Users/liuxg/data/zipcodes/zipcodes.csv",
"ShortName" : "军庄镇",
"Id" : "143",
"Longitude" : 116.103455,
"@version" : "1",
"@timestamp" : "2020-03-14T11:02:43.704Z",
"zipcode" : "110109104",
"Disabled" : "false"
}
},
{
"_index" : "zipcodes",
"_type" : "_doc",
"_id" : "KfG12HABqA-NWEvj7Tqx",
"_score" : 1.0,
"_source" : {
"Memo" : null,
"Latitude" : 39.898903,
"Name" : "八宝山街道",
"message" : "97,110107001,110107,八宝山街道,八宝山街道,116.231972,39.898903,1,,false",
"Sort" : "1",
"host" : "liuxg",
"AreaCode" : "110107",
"location" : "39.898903,116.231972",
"path" : "/Users/liuxg/data/zipcodes/zipcodes.csv",
"ShortName" : "八宝山街道",
"Id" : "97",
"Longitude" : 116.231972,
"@version" : "1",
"@timestamp" : "2020-03-14T11:02:43.694Z",
"zipcode" : "110107001",
"Disabled" : "false"
}
},
...
]
...
我们可以把当前的 Visualization 保存下来以在 Dashboard 里进行使用:
选择 Save,最终,我们形成自己的 Dashboard: