可以根据地理空间数据连接两个索引。在本教程中,我将向你展示如何通过混合邻里多边形和 GPS 犯罪事件坐标来创建纽约市的犯罪地图。
安装
如果你还没有安装好自己的 Elasticsearch 及 Kibana 的话,请参考如下的链接来进行安装。
-
如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch
-
Kibana:如何在 Linux,MacOS 及 Windows 上安装 Elastic 栈中的 Kibana
在第一次启动 Elasticsearch 时,我们需要记下超级用户 elastic 的密码:
这个密码将在下面进行使用。
装载测试数据
定义映射
我们在 Kibana 中打入如下的命令来定义索引 nyc_neighborhood 及 crime_events
PUT nyc_neighborhood
{
"mappings": {
"properties": {
"neighborhood": {
"type": "keyword"
},
"borough": {
"type": "keyword"
},
"location": {
"type": "geo_shape"
}
}
}
}
我们也可以在命令行中使用如下的命令来进行操作:
curl -k -XPUT -u elastic:<YourPassword> "https://localhost:9200/nyc_neighborhood" -H "Content-Type: application/json" -d'
{
"mappings": {
"properties": {
"neighborhood": {
"type": "keyword"
},
"borough": {
"type": "keyword"
},
"location": {
"type": "geo_shape"
}
}
}
}'
PUT crime_events
{
"mappings": {
"properties": {
"crime_type": {
"type": "keyword"
},
"crime_timestamp": {
"type": "date"
},
"crime_location": {
"type": "geo_point"
}
}
}
}
我们也可以在命令行中使用如下的命令来进行操作:
curl -k -u elastic:<YourPassword> -XPUT "https://localhost:9200/crime_events" -H "Content-Type: application/json" -d'
{
"mappings": {
"properties": {
"crime_type": { "type": "keyword" },
"crime_timestamp": { "type": "date" },
"crime_location": { "type": "geo_point" }
}
}
}'
如果你想删除上面的两个索引,你可以在命令行中进行如下的操作:
curl -k -u elastic:<YourPassword> -XDELETE "https://localhost:9200/nyc_neighborhood"
curl -k -u elastic:<YourPassword> -XDELETE "https://localhost:9200/crime_events"
注意:请注意 geo_shape 和 geo_point 字段类型用于位置。因为邻里是区域,所以应该通过多边形表示,而犯罪事件是地点,因此是单个点。
我们可以注意到上面的两个索引有两个位置字段:crime_location 是 geo_point 数据类型,而另外一个 location 是 geo_shape 类型。
如上所示,如果一个 geo_point 被一个 geo shape 所包含,那么这两个数据就是关联的。我们可以正对它们进行数据的丰富。我们可以从另外一个索引中得到额外的字段,比如,neighborhood。这样我们可以针对整个 neighborhood 进行数据的统计和可视化。
批量加载 - bulk load
我为邻域准备了详细数据,其中包含约 600 行,因此我不会在这里列出,而是请使用 bulk API 将其加载到 ELK。
从 IPFS 进行下载
我们使用如下的命令来进行下载:
docker run --rm -it \
-v "$PWD:/tmp" \
-e IPFS_GATEWAY="https://ipfs.filebase.io/" \
curlimages/curl:8.5.0 --parallel --output "/tmp/#1.json" "ipfs://{QmaZD1xzi1MFf2MhjrZv7U2BGKji9U1jRB9im1MbbPG446,QmNNaC9AquYsQfRu5nqZgWcCjFKEAqv2XS1XgHw3Tut8ck}"
$ docker run --rm -it \
> -v "$PWD:/tmp" \
> -e IPFS_GATEWAY="https://ipfs.filebase.io/" \
> curlimages/curl:8.5.0 --parallel --output "/tmp/#1.json" "ipfs://{QmaZD1xzi1MFf2MhjrZv7U2BGKji9U1jRB9im1MbbPG446,QmNNaC9AquYsQfRu5nqZgWcCjFKEAqv2XS1XgHw3Tut8ck}"
Unable to find image 'curlimages/curl:8.5.0' locally
8.5.0: Pulling from curlimages/curl
c30352492317: Pull complete
90f58e8ca393: Pull complete
4ca545ee6d5d: Pull complete
Digest: sha256:08e466006f0860e54fc299378de998935333e0e130a15f6f98482e9f8dab3058
Status: Downloaded newer image for curlimages/curl:8.5.0
DL% UL% Dled Uled Xfers Live Total Current Left Speed
100 -- 507k 0 2 0 0:00:02 0:00:02 --:--:-- 226k
$ ls
QmNNaC9AquYsQfRu5nqZgWcCjFKEAqv2XS1XgHw3Tut8ck.json QmaZD1xzi1MFf2MhjrZv7U2BGKji9U1jRB9im1MbbPG446.json
我们使用如下的命令来进行重新命名:
mv QmaZD1xzi1MFf2MhjrZv7U2BGKji9U1jRB9im1MbbPG446.json nyc_neighborhood_bulk.json
mv QmNNaC9AquYsQfRu5nqZgWcCjFKEAqv2XS1XgHw3Tut8ck.json crime_events.json
$ ls
crime_events.json nyc_neighborhood_bulk.json
crime_events.json 文件展示:
"index": {}}
{"crime_type": "theft", "timestamp": "2024-07-24T10:00:00Z", "crime_location": {"type": "point", "coordinates": [-74.0060, 40.7128]}}
{"index": {}}
{"crime_type": "assault", "timestamp": "2024-07-24T12:30:00Z", "crime_location": {"type": "point", "coordinates": [-73.9890, 40.6892]}}
{"index": {}}
{"crime_type": "vandalism", "timestamp": "2024-07-24T15:45:00Z", "crime_location": {"type": "point", "coordinates": [-73.9106, 40.7769]}}
{"index": {}}
{"crime_type": "robbery", "timestamp": "2024-07-25T09:15:00Z", "crime_location": {"type": "point", "coordinates": [-73.9865, 40.7306]}}
nyc_neighborhood_bulk 文件展示:
{"index": {}}
{"neighborhood": "Allerton", "borough": "Bronx", "location": {"type": "Polygon", "coordinates": [[[-73.86888180915341, 40.857223150158326], [-73.86831755272824, 40.85786206225831], [-73.86955371467232, 40.85778409560018], [-73.87102485762065, 40.857309948816905], [-73.87048054998716, 40.865413584098484], [-73.87055489856489, 40.86970279858986], [-73.86721594442561, 40.86968966363671], [-73.85745, 40.86953300000018], [-73.85555000000011, 40.871813000000145], [-73.85359796757658, 40.8732883686742], [-73.84859700000018, 40.871670000000115], [-73.84582253683678, 40.870239076236174], [-73.85455918463374, 40.85995383576425], [-73.85466543306826, 40.859585694988056], [-73.85638870335896, 40.85759363530448], [-73.86888180915341, 40.857223150158326]]]}}
{"index": {}}
上传文件至 Elasticsearch
curl -XPOST -u elastic:<YourPassword> "https://localhost:9200/nyc_neighborhood/_bulk" -H "Content-Type: application/json" -k --data-binary "@nyc_neighborhood_bulk.json" > /dev/null
curl -XPOST -u elastic:<YourPassword> "https://localhost:9200/crime_events/_bulk" -H "Content-Type: application/json" -k --data-binary "@crime_events.json" > /dev/null
curl -XPOST -u elastic:LX+LGtCWdSa9zn1d2Ebs "https://localhost:9200/nyc_neighborhood/_bulk" -H "Content-Type: application/json" -k --data-binary "@nyc_neighborhood_bulk.json" > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 561k 0 58846 100 504k 251k 2211k --:--:-- --:--:-- --:--:-- 2463k
我们可以到 Kibana 中进行查看:
我们看到有32个文档已经写入到 Elasticsearch 中。
curl -XPOST -u elastic:LX+LGtCWdSa9zn1d2Ebs "https://localhost:9200/crime_events/_bulk" -H "Content-Type: application/json" -k --data-binary "@crime_events.json" > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6809 0 3805 100 3004 27552 21752 --:--:-- --:--:-- --:--:-- 49340
我们到 Kibana 中进行查看:
我们可以看到有 20 个文档写入到 Elasticsearch 中。
我们也可以在 Kibana 中使用 ES|QL 来展示数据:
POST /_query?format=csv
{
"query": """
from nyc_neighborhood
"""
}
加入地理空间数据集
丰富策略
在 Elasticsearch 世界中,它被称为丰富。你将创建丰富策略,该策略将定义包含键值对的查找表
PUT /_enrich/policy/what-is-area-name
{
"geo_match": {
"indices": "nyc_neighborhood",
"match_field": "location",
"enrich_fields": [
"neighborhood" , "borough"
]
}
}
上面的意思表明,如果 location 字段包含另外一个索引中的 geo_point 点,那么 neighborhood 及 borough 将会被丰富。
它将从 nyc_neighborhood 索引中获取字段。match field 是关键,而 enrich_fields 将是附加到索引的值,你将来会通过 enrich 处理器或 ES|QL 命令来丰富这些值。
创建策略后,你必须执行它:
POST _enrich/policy/what-is-area-name/_execute
这是使用选定数据创建新的系统索引。你可以使用 ES|QL 显示其中的内容。
POST _query?format=csv
{
"query":"""
from .enrich-what-is-area-name*
| limit 1000
"""
}
这是你的查找表。从现在起,你可以使用它执行连接。
使用 ES|QL 连接数据
以下查询将汇总每个地区的犯罪事件。
POST /_query?format=txt
{
"query": """
from crime_events
| keep crime_type,timestamp,crime_location
| enrich what-is-area-name on crime_location
| where borough is not null
| limit 10
"""
}
POST /_query?format=txt
{
"query": """
from crime_events
| keep crime_type,timestamp,crime_location
| enrich what-is-area-name on crime_location
| where borough is not null
| stats howMany = count(*) by borough,crime_type
| limit 10
"""
}
上面真的每个 borough 地区进行了统计。
Kibana 中的可视化
请使用 Maps 创建图层并制作漂亮的仪表板。添加图层时,你可以使用 ES|QL 获取正确的数据。首先来创建 Data views:
我们下面来做可视化:
在上面,我们使用如下的查询:
from crime_events | keep crime_location | limit 10000
我们放大地图就可以看到显示的数据。
按照同样的方法,我们添加另外一个 layer,使用如下的查询:
from nyc_neighborhood | keep location | limit 10000
我们需要调整上下层(通过拖拽调整层的关系)。最终我们得到上面的可视化图。