在今天的這個教程中,我們來着重講解一下如何使用Elasticsearch中的copy來提高搜索的效率。比如在我們的搜索中,經常我們會遇到如下的文檔:
{
"user" : "雙榆樹-張三",
"message" : "今兒天氣不錯啊,出去轉轉去",
"uid" : 2,
"age" : 20,
"city" : "北京",
"province" : "北京",
"country" : "中國",
"address" : "中國北京市海淀區",
"location" : {
"lat" : "39.970718",
"lon" : "116.325747"
}
}
在這里,我們可以看到在這個文檔中,我們有這樣的幾個字段:
"city" : "北京",
"province" : "北京",
"country" : "中國",
它們是非常相關的。我們在想是不是可以把它們綜合成一個字段,這樣可以方便我們的搜索。假如我們要經常對這三個字段進行搜索,那么一種方法我們可以在must子句中使用should子句運行bool查詢。這種方法寫起來比較麻煩。有沒有一種更好的方法呢?
我們其實可以使用Elasticsearch所提供的copy_to來提高我們的搜索效率。我們可以首先把我們的index的mapping設置成如下的項(這里假設我們使用的是一個叫做twitter的index)。
PUT twitter
{
"mappings": {
"properties": {
"address": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"age": {
"type": "long"
},
"city": {
"type": "keyword",
"copy_to": "region"
},
"country": {
"type": "keyword",
"copy_to": "region"
},
"province": {
"type": "keyword",
"copy_to": "region"
},
"region": {
"type": "text",
"store": true
},
"location": {
"type": "geo_point"
},
"message": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"uid": {
"type": "long"
},
"user": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
在這里,我們特別注意如下的這個部分:
"city": {
"type": "keyword",
"copy_to": "region"
},
"country": {
"type": "keyword",
"copy_to": "region"
},
"province": {
"type": "keyword",
"copy_to": "region"
},
"region": {
"type": "text"
}
我們把city, country及province三個項合並成為一個項region,但是這個region並不存在於我們文檔的source里。當我們這么定義我們的mapping的話,在文檔被索引之后,有一個新的region項可以供我們進行搜索。
我們可以采用如下的數據來進行展示:
POST _bulk
{ "index" : { "_index" : "twitter", "_id": 1} }
{"user":"雙榆樹-張三","message":"今兒天氣不錯啊,出去轉轉去","uid":2,"age":20,"city":"北京","province":"北京","country":"中國","address":"中國北京市海淀區","location":{"lat":"39.970718","lon":"116.325747"}}
{ "index" : { "_index" : "twitter", "_id": 2 }}
{"user":"東城區-老劉","message":"出發,下一站雲南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中國","address":"中國北京市東城區台基廠三條3號","location":{"lat":"39.904313","lon":"116.412754"}}
{ "index" : { "_index" : "twitter", "_id": 3} }
{"user":"東城區-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中國","address":"中國北京市東城區","location":{"lat":"39.893801","lon":"116.408986"}}
{ "index" : { "_index" : "twitter", "_id": 4} }
{"user":"朝陽區-老賈","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中國","address":"中國北京市朝陽區建國門","location":{"lat":"39.718256","lon":"116.367910"}}
{ "index" : { "_index" : "twitter", "_id": 5} }
{"user":"朝陽區-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中國","address":"中國北京市朝陽區國貿","location":{"lat":"39.918256","lon":"116.467910"}}
{ "index" : { "_index" : "twitter", "_id": 6} }
{"user":"虹橋-老吳","message":"好友來了都今天我生日,好友來了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中國","address":"中國上海市閔行區","location":{"lat":"31.175927","lon":"121.383328"}}
在Kibnana中執行上面的語句,它將為我們生產我們的twitter索引。同時我們可以通過如下的語句來查詢我們的mapping:
我們可以看到twitter的mapping中有一個新的被稱作為region的項。它將為我們的搜索帶來方便。
那么假如我們想搜索country:中國,province:北京 這樣的記錄的話,我們可以只寫如下的一條語句就可以了:
GET twitter/_search
{
"query": {
"match": {
"region": {
"query": "中國 北京",
"minimum_should_match": 4
}
}
}
}
下面顯示的是搜索的結果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : 0.8114117,
"hits" : [
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.8114117,
"_source" : {
"user" : "雙榆樹-張三",
"message" : "今兒天氣不錯啊,出去轉轉去",
"uid" : 2,
"age" : 20,
"city" : "北京",
"province" : "北京",
"country" : "中國",
"address" : "中國北京市海淀區",
"location" : {
"lat" : "39.970718",
"lon" : "116.325747"
}
}
},
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.8114117,
"_source" : {
"user" : "東城區-老劉",
"message" : "出發,下一站雲南!",
"uid" : 3,
"age" : 30,
"city" : "北京",
"province" : "北京",
"country" : "中國",
"address" : "中國北京市東城區台基廠三條3號",
"location" : {
"lat" : "39.904313",
"lon" : "116.412754"
}
}
},
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.8114117,
"_source" : {
"user" : "東城區-李四",
"message" : "happy birthday!",
"uid" : 4,
"age" : 30,
"city" : "北京",
"province" : "北京",
"country" : "中國",
"address" : "中國北京市東城區",
"location" : {
"lat" : "39.893801",
"lon" : "116.408986"
}
}
},
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.8114117,
"_source" : {
"user" : "朝陽區-老賈",
"message" : "123,gogogo",
"uid" : 5,
"age" : 35,
"city" : "北京",
"province" : "北京",
"country" : "中國",
"address" : "中國北京市朝陽區建國門",
"location" : {
"lat" : "39.718256",
"lon" : "116.367910"
}
}
},
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "5",
"_score" : 0.8114117,
"_source" : {
"user" : "朝陽區-老王",
"message" : "Happy BirthDay My Friend!",
"uid" : 6,
"age" : 50,
"city" : "北京",
"province" : "北京",
"country" : "中國",
"address" : "中國北京市朝陽區國貿",
"location" : {
"lat" : "39.918256",
"lon" : "116.467910"
}
}
}
]
}
}
這樣我們只對一個region進行操作就可以了,否則我們需要針對country, city及province分別進行搜索。
如何查看copy_to的內容
在之前的mapping中,我們對region字段加入了如下的一個屬性:
"region": {
"type": "text",
"store": true
}
這里的store屬性為true,那么我們可以通過如下的命令來查看文檔的region的內容:
GET twitter/_doc/1?stored_fields=region
那么它顯示的內容如下:
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"fields" : {
"region" : [
"北京",
"北京",
"中國"
]
}
}
如果你想了解更多關於Elastic Stack,請參閱文章“Elasticsearch簡介”