一 序
本文屬於極客時間Elasticsearch核心技術與實戰學習筆記系列。
二 數據的關聯關係
真實世界中有很多關係,軟件也是對此一種抽象。
2.1關係型數據庫的範式化設計
範式化設計(Normalization)的主要目標是 “減少不必要的更新”
副作用:一個完全範式化設計的數據庫經常面臨 “查詢緩慢” 的問題
- 數據庫餘額範式化,就需要 Join 越多的表
範式化節省了儲存空間,但是儲存空間越來越便宜
範式化簡化了更新,但是數據 “讀” 取操作可能越多
2.2Denormalization
反範式化設計
- 數據 “Flattening”, 不使用關聯關係,而是在文檔中保存冗餘的數據拷貝
優點:無需處理 Joins 操作,數據讀取性能好
- Elasticsearch 通過壓縮_source 字段,減少磁盤空間的開銷
缺點:不適合在數據頻繁修改的場景
- 一條數據(用戶名)的改動,可能會引起很多數據的更新
2.3 在 Elasticsearch 中處理關聯關係
關係型數據庫,一般會考慮 Normalize 數據;在 Elasticsearch,往往考慮 Denormalize 數據
- Denormalize 的好處:讀的速度變快、無需表連接、無需行鎖
Elasticsearch 並不擅長處理關聯關係,我們一般採用以下四種方法處理關聯
- 對象類型
- 嵌套對象(Nested Object)
- 父子關聯關係(Parent 、Child)
- 應用端關聯
案例 1:博客和其作者信息
數據準備:
DELETE blog
# 設置blog的 Mapping
PUT /blog
{
"mappings": {
"properties": {
"content": {
"type": "text"
},
"time": {
"type": "date"
},
"user": {
"properties": {
"city": {
"type": "text"
},
"userid": {
"type": "long"
},
"username": {
"type": "keyword"
}
}
}
}
}
}
# 插入一條 Blog 信息
PUT blog/_doc/1
{
"content":"I like Elasticsearch",
"time":"2019-01-01T00:00:00",
"user":{
"userid":1,
"username":"Jack",
"city":"Shanghai"
}
}
這是bool的query,es滿足搜索需求。
案例 2:包含對象數組的文檔
數據準備:
DELETE my_movies
# 電影的Mapping信息
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"properties" : {
"first_name" : {
"type" : "keyword"
},
"last_name" : {
"type" : "keyword"
}
}
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
# 寫入一條電影信息
POST my_movies/_doc/1
{
"title":"Speed",
"actors":[
{
"first_name":"Keanu",
"last_name":"Reeves"
},
{
"first_name":"Dennis",
"last_name":"Hopper"
}
]
}
查詢到數據,但是不存在滿足查詢條件的數據。
爲啥搜到不需要的數據?
- 儲存時,內部對象的邊界沒有在考慮在內,JSON 格式被處理成扁平鍵值對的結構
- 當對多個字段進行查詢時,導致了意外的搜索結果
- 可以用 Nested Data Type 解決這個問題
Nested Data Type
- Nested 數據類型:允許對象數組中的對象被獨立索引
- 使用 Nested 和 Properties 關鍵詞,將所有 actors 索引到對個分隔的文檔
- 在內部,Nested 文檔會被保存在兩個 Lucene 文檔中,查詢時做 join 處理
數據準備:
DELETE my_movies
# 創建 Nested 對象 Mapping
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"type": "nested",
"properties" : {
"first_name" : {"type" : "keyword"},
"last_name" : {"type" : "keyword"}
}},
"title" : {
"type" : "text",
"fields" : {"keyword":{"type":"keyword","ignore_above":256}}
}
}
}
}
POST my_movies/_doc/1
{
"title":"Speed",
"actors":[
{
"first_name":"Keanu",
"last_name":"Reeves"
},
{
"first_name":"Dennis",
"last_name":"Hopper"
}
]
}
這個索引增加了 "type": "nested",指定爲嵌套的對象。
3 嵌套查詢
在內部,Nested 文檔被保存在兩個 Lucene 文檔中
上面指定嵌套關鍵字:nested,path指定屬性。下面增加actor_name做子聚合。就可以實現嵌套對象的term分桶。
# 普通 aggregation不工作
POST my_movies/_search
{
"size": 0,
"aggs": {
"NAME": {
"terms": {
"field": "actors.first_name",
"size": 10
}
}
}
}