淺析ES的_source、_all、store、index

 

Elasticsearch中有大量關鍵概念容易混淆,對於初學者來講是噩夢:前端

  • _source字段裏存儲了什麼?
  • index屬性的做用是什麼?
  • 什麼時候應該開啓_all字段?
  • store屬性和_source字段有什麼關係?
  • store屬性和_all字段有什麼關係?
  • 什麼狀況下不用保留_source字段?

本文經過問答及展開描述的方式,深刻理解Elasticsearch中的_source_all字段和storeindex屬性。shell

概念解釋

Q:_source字段裏存儲了什麼?
A:原始文檔的內容。數據結構


圖1 Elasticsearch中的_source、_all、store和index屬性解析

如圖1所示, 第二象限是一份原始文檔,有titlecontent2個字段,字段取值分別爲"我是中國人"和"熱愛gongchangdang",這一點沒什麼可解釋的。咱們把原始文檔寫入Elasticsearch,默認狀況下,Elasticsearch裏面有2分內容,一份是原始文檔,也就是_source字段裏的內容,咱們在Elasticsearch中搜索文檔,查看的文檔內容就是_source中的內容,如圖2,相信你們必定很是熟悉這個界面。架構

圖2 _source字段舉例
圖2 _source字段舉例

另外一份是倒排索引,倒排索引中的數據結構是倒排記錄表,記錄了詞項和文檔之間的對應關係,好比關鍵詞"中國人"包含在文檔ID爲1的文檔中,倒排記錄表中存儲的就是這種對應關係,固然也包括詞頻等更多信息。Elasticsearch底層用的是Lucene的API,Elasticsearch之因此能完成全文搜索的功能就是由於存儲有倒排索引。若是把倒排索引拿掉,Elasticsearch是否是和mongoDB很像?app

Q:index屬性的做用是什麼?
A:控制field是否生成倒排索引以及生成索引時是否作分詞。大數據

那麼文檔索引到Elasticsearch的時候,默認狀況下是對全部字段建立倒排索引的(動態mapping解析出來爲數字類型、布爾類型的字段除外),某個字段是否生成倒排索引是由字段的index屬性控制的,在Elasticsearch 5以前,index屬性的取值有三個:搜索引擎

  • analyzed:字段被索引,會作分詞,可搜索。反過來,若是須要根據某個字段進行搜索,index屬性就應該設置爲analyzed
  • not_analyzed:字段值不分詞,會被原樣寫入索引。反過來,若是某些字段須要徹底匹配,好比人名、地名,index屬性設置爲not_analyzed爲佳。
  • no:字段不寫入索引,固然也就不能搜索。反過來,有些業務要求某些字段不能被搜索,那麼index屬性設置爲no便可。

Q:什麼時候應該開啓_all字段?
A:_all字段開啓適用於不指定搜索某一個字段,根據關鍵詞,搜索整個文檔內容。.net

再說_all字段,顧名思義,_all字段是把全部其它字段中的值,以空格爲分隔符組成一個大字符串,而後被分析和索引,可是不存儲,也就是說它能被查詢,但不能被取回顯示,是一個超級字段。以圖中的文檔爲例,若是開啓_all字段,那麼title和content的值會組成一個超級字段,固然也能夠設置只存儲某幾個字段到_all屬性裏面或者排除某些字段。_all字段在查詢時佔用更多的CPU和佔用更多的磁盤空間,若是確實不須要它能夠徹底的關閉它或者基於字段定製。code

_all能讓你在不知道要查找的內容是屬於哪一個具體字段的狀況下進行搜索,例如:blog

PUT my_index/user/1 
{
  "first_name":    "John",
  "last_name":     "Smith",
  "date_of_birth": "1970-10-24"
}
GET my_index/_search
{
  "query": {
    "match": {
      "_all": "john smith 1970"
    }
  }
}

獲得的結果是:

{
  "took": 20,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.84748024,
    "hits": [
      {
        "_index": "my_index",
        "_type": "user",
        "_id": "1",
        "_score": 0.84748024,
        "_source": {
          "first_name": "John",
          "last_name": "Smith",
          "date_of_birth": "1970-10-24"
        }
      }
    ]
  }
}

Q:store屬性和_source字段有什麼關係?
A:_source是字段,store是字段的屬性之一,Elasticsearch默認會存儲一份_source字段做爲原始文檔。store屬性和_source字段配合進行設置能夠決定字段的檢索方式和是否高亮展現。

回到圖一的第一象限,用戶輸入關鍵詞"中國人",分詞之後,Elasticsearch從倒排記錄表中查找哪些文檔包含詞項"中國人 ",注意變化,分詞以前" 中國人"是用戶查詢(query),分詞以後在倒排索引中" 中國人"是詞項(term)。Elasticsearch根據文檔ID(一般是文檔ID的集合)返回文檔內容給用戶,如圖一第四象限所示。

一般狀況下,對於用戶查詢的關鍵字要作高亮處理,如圖3所示:

圖3 搜索引擎中的關鍵字高亮
圖3 搜索引擎中的關鍵字高亮

關鍵字高亮實質上是根據倒排記錄中的詞項偏移位置,找到關鍵詞,加上前端的高亮代碼。這裏就要說到store屬性,store屬性用於指定是否將原始字段寫入索引,默認取值爲false。若是在Lucene中,高亮功能和store屬性是否存儲息息相關,由於須要根據偏移位置到原始文檔中找到關鍵字才能加上高亮的片斷。二者不一樣取值時對字段檢索和高亮的影響以下表:

enabled store爲true的字段從倒排索引裏檢索,浪費IO次數; 全部字段根據Client類解析存儲的 _source JSON串進行檢索,僅需一次IO;
disabled store爲true的字段從倒排索引裏檢索,其餘字段能檢索不能高亮展現; 全部字段只能檢索,不能高亮展現;
_source\store
true
false

在Elasticsearch,由於_source中已經存儲了一份原始文檔,能夠根據_source中的原始文檔實現高亮,在索引中再存儲原始文檔就多餘了,因此Elasticsearch默認是把store屬性設置爲false

若是想要對某個字段實現高亮功能,_sourcestore至少保留一個。下面給出一個例子,設置test索引不保存_source,title字段索引但不分析,字段原始值寫入索引,content字段爲默認屬性,代碼以下:

  • 新建test index:
PUT test/test/_mapping
{
   "test": {
      "_source": {
         "enabled": false
      },
      "properties": {
         "title": {
            "type": "string",
            "index": "not_analyzed",
            "store": "true"
         },
         "content": {
            "type": "string"
         }
      }
   }
}
  • 對title字段進行搜索並高亮:
GET test/_search
{
    "query": {
        "match": {
           "title": "我是中國人"
        }
    },
   "highlight": {
      "fields": {
         "title": {}
      }
   }
}
  • 返回結果:
{
   "took": 6,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 0.30685282,
      "hits": [
         {
            "_index": "test",
            "_type": "test",
            "_id": "1",
            "_score": 0.30685282,
            "highlight": {
               "title": [
                  "<em>我是中國人</em>"
               ]
            }
         }
      ]
   }
}

從返回結果中能夠看到,雖然沒有保存title字段到_source, 可是依然能夠實現搜索高亮。

{"store": true}既然這麼費力不討好,可是仍然有兩個應用場景:

  • 文檔很長,檢索全部文檔或者存儲全部文檔、獲取全部field的代價比較大;
  • 僅僅針對某幾個字段進行re-index的時候;

Q:store屬性和_all字段有什麼關係?
A:_all字段擁有store屬性,是從屬關係。store屬性決定了_all字段是否能夠被高亮展現。

首先store_all字段的一個屬性,_all字段默認不屬於_source,而且store屬性默認爲false,因此不能被高亮展現。若是但願被高亮展現,則有兩種方式:

  • 設置_all字段的store爲true,代價是大量的冗餘存儲;
  • 對原始字段進行高亮;

Q:什麼狀況下不用保留_source字段?
A:須要存儲海量文檔,Elasticsearch在架構中只作索引,存儲選型爲相似HBase的NoSQL,不存儲_source能夠極大減小存儲開銷(Elasticsearch每每用的是SSD)。

_source字段默認是存儲的, 什麼狀況下不用保留_source字段?若是某個字段內容很是多,業務只須要能對該字段進行搜索,最後返回文檔id,查看文檔內容會再次到HBase中取數據,把大字段的內容存在Elasticsearch中只會增大索引,這一點文檔數量越大結果越明顯,若是一條文檔節省幾KB,放大到億萬級的量結果也是很是可觀的。 若是想要關閉_source字段,在mapping中的設置以下:

{
    "yourtype":{
        "_source":{
            "enabled":false
        },
        "properties": {
            ... 
        }
    }
}

若是隻想存儲某幾個字段的原始值到Elasticsearch,能夠經過incudes參數來設置,在mapping中的設置以下:

{
    "yourtype":{
        "_source":{
            "includes":["field1","field2"]
        },
        "properties": {
            ... 
        }
    }
}

一樣,能夠經過excludes參數排除某些字段:

{
    "yourtype":{
        "_source":{
            "excludes":["field1","field2"]
        },
        "properties": {
            ... 
        }
    }
}

小結

至此,文章開頭提出的幾個問題都給出了答案。特別參考了這篇文章:圖解Elasticsearch中的_source、_all、store和index屬性

做者:大數據之心連接:http://www.jianshu.com/p/0908b9ee65fc來源:簡書著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索