天天看點

[Elasticsearch] 索引管理 索引管理

索引管理

本文翻譯自Elasticsearch官方指南的索引管理(Index Management)一章

我們已經了解了ES是如何在不需要任何複雜的計劃和安裝就能讓我們很容易地開始開發一個新的應用的。但是,用不了多久你就會想要仔細調整索引和搜尋過程來更好的适配你的用例。

幾乎所有的定制都和索引(Index)以及其中的類型(Type)相關。本章我們就來讨論用于管理索引和類型映射的API,以及最重要的設定。

建立索引

到現在為止,我們已經通過索引一份文檔來完成了新索引的建立。這個索引是使用預設的設定建立的,新的域通過動态映射(Dynamic Mapping)的方式被添加到了類型映射(Type Mapping)中。

現在我們對這個過程擁有更多的控制:我們需要確定索引被建立時擁有合适數量的主分片(Primary Shard),并且在索引任何資料之前,我們需要設定好解析器(Analyzers)以及映射(Mappings)。

是以我們需要手動地去建立索引,将任何需要的設定和類型映射傳入到請求正文中,就像下面這樣:

PUT /my_index
{
    "settings": { ... },
    "mappings": {
        "type_one": { ... },
        "type_two": { ... },
        ...
    }
}      

事實上,如果你想阻止索引被自動建立,可以通過添加下面的設定到每個節點的

config/elasticsearch.yml

檔案中:

action.auto_create_index: false
           
将來,我們會讨論如何使用索引模闆(Index Template)來預先定義自動生成的索引。這個功能在索引日志資料的時候有用武之地:索引的名字中會包含日期,每天都有一個有着合适配置的索引被自動地生成。

删除索引

使用下面的請求完成索引的删除:

DELETE /my_index      

你也可以删除多個索引:

DELETE /index_one,index_two
DELETE /index_*
           

你甚至還可以删除所有的索引:

DELETE /_all
           

索引設定

雖然索引的種種行為可以通過索引子產品的參考文檔介紹的那樣進行配置,但是……

TIP

ES中提供了一些很好的預設值,隻有當你知道它是幹什麼的,以及為什麼要去修改它的時候再去修改。

兩個最重要的設定:

number_of_shards

一個索引中含有的主分片(Primary Shard)的數量,預設值是5。在索引建立後這個值是不能被更改的。

number_of_replicas

每一個主分片關聯的副本分片(Replica Shard)的數量,預設值是1。這個設定在任何時候都可以被修改。

比如,我們可以通過下面的請求建立一個小的索引 - 隻有一個主分片 - 同時沒有副本分片:

PUT /my_temp_index
{
    "settings": {
        "number_of_shards" :   1,
        "number_of_replicas" : 0
    }
}      

将來,我們可以動态地通過update-index-settings API完成對副本分片數量的修改:

PUT /my_temp_index/_settings
{
    "number_of_replicas": 1
}      

配置解析器

第三個重要的索引設定就是解析(Analysis),可以利用已經存在的解析器(Analyzer)進行配置,或者是為你的索引定制新的解析器。

在解析和解析器中,我們介紹了一些内置的解析器,它們用來将全文字元串轉換成适合搜尋的反向索引(Inverted Index)。

對于全文字元串字段預設使用的是

standard

解析器,它對于多數西方語言而言是一個不錯的選擇。它包括:

  • standard

    分詞器。它根據詞語的邊界進行分詞。
  • standard

     token過濾器。用來整理上一步分詞器得到的tokens,但是目前是一個空操作(no-op)。
  • lowercase

     token過濾器。将所有tokens轉換為小寫。
  • stop

     token過濾器。移除所有的stopwords,比如a,the,and,is等

預設下stopwords過濾器沒有被使用。可以通過建立一個基于

standard

解析器的解析器并設定

stopwords

參數來啟用。要麼提供一個stopwords的清單或者告訴它使用針對某種語言預先定義的stopwords清單。

在下面的例子中,我們建立了一個名為

es_std

的解析器,它使用了預先定義的西班牙語中的stopwords清單:

PUT /spanish_docs
{
    "settings": {
        "analysis": {
            "analyzer": {
                "es_std": {
                    "type":      "standard",
                    "stopwords": "_spanish_"
                }
            }
        }
    }
}      

es_std

解析器不是全局的 - 它隻作用于

spanish_docs

索引。可以通過制定索引名,使用

analyze

 API進行測試:

GET /spanish_docs/_analyze?analyzer=es_std
{
    El veloz zorro marrón
}      

下面的部分結果顯示了西班牙語中的stopword 

El

已經被正确地移除了:

{
  "tokens" : [
    { "token" :    "veloz",   "position" : 2 },
    { "token" :    "zorro",   "position" : 3 },
    { "token" :    "marrón",  "position" : 4 }
  ]
}      

自定義解析器(Custom Analyzers)

雖然ES本身已經提供了一些解析器,但是通過組合字元過濾器(Character Filter),分詞器(Tokenizer)以及詞條過濾器(Token Filter)來建立你自己的解析器才會顯示出其威力。

在解析和解析器中,我們提到過解析器(Analyzer)就是将3種功能打包得到的,它會按照下面的順序執行:

  • 字元過濾器(Character Filter) 字元過濾器用來在分詞前将字元串進行"整理"。比如,如果文本是HTML格式,那麼它會含有類似

    <p>

    或者

    <div>

    這樣的HTML标簽,但是這些标簽我們是不需要索引的。我們可以使用

    html_strip

    字元過濾器移除所有的HTML标簽,并将所有的像Á這樣的HTML實體(HTML Entity)轉換為對應的Unicode字元:Á。
  • 分詞器(Tokenizers) 一個解析器必須有一個分詞器。分詞器将字元串分解成一個個單獨的詞條(Term or Token)。在

    standard

    解析器中使用的

    standard

    分詞器,通過單詞邊界對字元串進行劃分來得到詞條,同時會移除大部分的标點符号。另外還有其他的分詞器擁有着不同的行為。

    比如

    keyword

    分詞器,它不會進行任何分詞,直接原樣輸出。

    whitespace

    分詞器則隻通過對空白字元進行劃分來得到詞條。而

    pattern

    分詞器則根據正規表達式來進行分詞。
  • 詞條過濾器(Token Filter) 在分詞後,得到的詞條流(Token Stream)會按照順序被傳入到指定的詞條過濾器中。

    詞條過濾器能夠修改,增加或者删除詞條。我們已經提到了

    lowercase

    詞條過濾器和

    stop

    詞條過濾器,但是ES中還有許多其它可用的詞條過濾器。

    stemming

    詞條過濾器會對單詞進行詞幹提取來得到其詞根形态(Root Form)。

    ascii_folding

    詞條過濾器則會移除變音符号(Diacritics),将類似于

    très

    的詞條轉換成

    tres

    ngram

    詞條過濾器和

    edge_ngram

    詞條過濾器會産生适用于部分比對(Partial Matching)或者自動完成(Autocomplete)的詞條。

在深入搜尋中,我們會通過例子來讨論這些分詞器和過濾器的使用場景和使用方法。但是首先,我們需要解釋如何來建立一個自定義的解析器。

建立一個自定義的解析器

和上面我們配置

es_std

解析器的方式相同,我們可以在

analysis

下對字元過濾器,分詞器和詞條過濾器進行配置:

PUT /my_index
{
    "settings": {
        "analysis": {
            "char_filter": { ... custom character filters ... },
            "tokenizer":   { ...    custom tokenizers     ... },
            "filter":      { ...   custom token filters   ... },
            "analyzer":    { ...    custom analyzers      ... }
        }
    }
}      

比如,要建立擁有如下功能的解析器:

  1. 使用

    html_strip

    字元過濾器完成HTML标簽的移除。
  2. 将&字元替換成" and ",使用一個自定義的

    mapping

    字元過濾器。
"char_filter": {
    "&_to_and": {
        "type":       "mapping",
        "mappings": [ "&=> and "]
    }
}      
  1. 使用

    standard

    分詞器對文本進行分詞。
  2. 使用

    lowercase

    詞條過濾器将所有詞條轉換為小寫。
  3. 使用一個自定義的stopword清單,并通過自定義的stop詞條過濾器将它們移除:
"filter": {
    "my_stopwords": {
        "type":        "stop",
        "stopwords": [ "the", "a" ]
    }
}      

我們的解析器将預先定義的分詞器和過濾器和自定義的過濾器進行了結合:

"analyzer": {
    "my_analyzer": {
        "type":           "custom",
        "char_filter":  [ "html_strip", "&_to_and" ],
        "tokenizer":      "standard",
        "filter":       [ "lowercase", "my_stopwords" ]
    }
}      

是以,整個

create-index

請求就像下面這樣:

PUT /my_index
{
    "settings": {
        "analysis": {
            "char_filter": {
                "&_to_and": {
                    "type":       "mapping",
                    "mappings": [ "&=> and "]
            }},
            "filter": {
                "my_stopwords": {
                    "type":       "stop",
                    "stopwords": [ "the", "a" ]
            }},
            "analyzer": {
                "my_analyzer": {
                    "type":         "custom",
                    "char_filter":  [ "html_strip", "&_to_and" ],
                    "tokenizer":    "standard",
                    "filter":       [ "lowercase", "my_stopwords" ]
            }}
}}}      

建立索引之後,使用

analyze

 API對新的解析器進行測試:

GET /my_index/_analyze?analyzer=my_analyzer
The quick & brown fox      

得到的部分結果如下,表明我們的解析器能夠正常工作:

{
  "tokens" : [
      { "token" :   "quick",    "position" : 2 },
      { "token" :   "and",      "position" : 3 },
      { "token" :   "brown",    "position" : 4 },
      { "token" :   "fox",      "position" : 5 }
    ]
}      

我們需要告訴ES這個解析器應該在什麼地方使用。我們可以将它應用在

string

字段的映射中:

PUT /my_index/_mapping/my_type
{
    "properties": {
        "title": {
            "type":      "string",
            "analyzer":  "my_analyzer"
        }
    }
}      

類型和映射(Types and Mappings)

在ES中的類型(Type)代表的是一類相似的文檔。一個類型包含了一個名字(Name) - 比如

user

或者

blogpost

 - 以及一個映射(Mapping)。映射就像資料庫的模式那樣,描述了文檔中的字段或者屬性,和每個字段的資料類型 -

string

integer

date

等 - 這些字段是如何被Lucene索引和存儲的。

在什麼是文檔中,我們說一個類型就好比關系資料庫中的一張表。盡管一開始這樣思考有助于了解,但是對類型本身進行更細緻的解釋 - 它們到底是什麼,它們是如何在Lucene的基礎之上實作的 - 仍然是有價值的。

Lucene是如何看待文檔的

Lucene中的文檔包含的是一個簡單field-value對的清單。一個字段至少要有一個值,但是任何字段都可以擁有多個值。類似的,一個字元串值也可以通過解析階段而被轉換為多個值。Lucene不管值是字元串類型,還是數值類型或者什麼别的類型 - 所有的值都會被同等看做一些不透明的位元組(Opaque bytes)。

當我們使用Lucene對文檔進行索引時,每個字段的值都會被添加到反向索引(Inverted Index)的對應字段中。原始值也可以被選擇是否會不作修改的被儲存到索引中,以此來友善将來的擷取。

類型是如何實作的

ES中的type是基于以下簡單的基礎進行實作的。一個索引中可以有若幹個類型,每個類型又有它自己的mapping,然後類型下的任何文檔可以存儲在同一個索引中。

可是Lucene中并沒有文檔類型這一概念。是以在具體實作中,類型資訊通過一個中繼資料字段

_type

記錄在文檔中。當我們需要搜尋某個特定類型的文檔時,ES會自動地加上一個針對_type字段的過濾器來保證傳回的結果都是目标類型上的文檔。

同時,Lucene中也沒有映射的概念。映射是ES為了對複雜JSON文檔進行扁平化(可以被Lucene索引)而設計的一個中間層。

比如,

user

類型的

name

字段可以定義成一個

string

類型的字段,而它的值則應該被

whitespace

解析器進行解析,然後再被索引到名為

name

的反向索引中。

"name": {
    "type":     "string",
    "analyzer": "whitespace"
}      

避免類型中的陷阱

由于不同類型的文檔能夠被添加到相同的索引中,産生了一些意想不到的問題。

比如在我們的索引中,存在兩個類型:

blog_en

用來儲存英文的博文,

blog_es

用來儲存西班牙文的博文。這兩種類型中都有一個

title

字段,隻不過它們使用的解析器分别是

english

spanish

問題可以通過下面的查詢反映:

GET /_search
{
    "query": {
        "match": {
            "title": "The quick brown fox"
        }
    }
}      

我們在兩個類型中搜尋

title

字段。查詢字元串(Query String)需要被解析,但是應該使用哪個解析器:是

spanish

還是

english

?答案是會利用首先找到的

title

字段對應的解析器,是以對于部分文檔這樣做是正确的,對于另一部分則不然。

我們可以通過将字段命名地不同 - 比如

title_en

title_es

 - 或者通過顯式地将類型名包含在字段名中,然後對每個字段獨立查詢來避免這個問題:

GET /_search
{
    "query": {
        "multi_match": { 
            "query":    "The quick brown fox",
            "fields": [ "blog_en.title", "blog_es.title" ]
        }
    }
}      

multi_match

查詢會對指定的多個字段運作

match

查詢,然後合并它們的結果。

以上的查詢中對

blog_en.title

字段使用

english

解析器,對

blog_es.title

字段使用

spanish

解析器,然後對兩個字段的搜尋結果按照相關度分值進行合并。

這個解決方案能夠在兩個域是相同資料類型時起作用,但是考慮下面的場景,當向相同索引中添加兩份文檔時會發生什麼:

類型user

{ "login": "john_smith" }      

類型event

{ "login": "2014-06-01" }      

Lucene本身不在意類型一個字段是字元串類型,而另一個字段是日期類型 - 它隻是愉快地将它們當做位元組資料進行索引。

但是當我們試圖去針對

event.login

字段進行排序的時候,ES需要将

login

字段的值讀入到記憶體中。根據Fielddata提到的,ES會将索引中的所有文檔都讀入,無論其類型是什麼。

取決于ES首先發現的

login

字段的類型,它會試圖将這些值當做字元串或者日期類型讀入。是以,這會産生意料外的結果或者直接失敗。

Tip 為了避免發生這些沖突,建議索引中,每個類型的同名字段都使用相同的映射方式。

根對象(Root Object)

映射的最頂層被稱為根對象。它包含了:

  • 屬性區域(Properties Section),列舉了文檔中包含的每個字段的映射資訊。
  • 各種中繼資料(Metadata)字段,它們都以_開頭,比如

    _type

    _id

    _source

  • 控制用于新字段的動态探測(Dynamic Detection)的設定,如

    analyzer

    dynamic_date_formats

    dynamic_templates

  • 其它的可以用在根對象和

    object

    類型中的字段上的設定,如

    enabled

    dynamic

    include_in_all

屬性(Properties)

我們已經在核心簡單字段類型(Core Simple Field Type)和複雜核心字段類型(Complex Core Field Type)中讨論了對于文檔字段或屬性最為重要的三個設定:

  • type

    :字段的資料類型,比如

    string

    或者

    date

  • index

    :一個字段是否需要被當做全文(Full text)進行搜尋(

    analyzed

    ),被當做精确值(Exact value)進行搜尋('not_analyzed'),或者不能被搜尋(

    no

    )。
  • analyzer

    :全文字段在索引時(Index time)和搜尋時(Search time)使用的

    analyzer

我們會在後續章節中合适的地方讨論諸如

ip

geo_point

geo_shape

等其它字段類型。

中繼資料:

_source

字段

預設,ES會将表示文檔正文的JSON字元串儲存為

_source

字段。和其它存儲的字段一樣,

_source

字段也會在儲存到磁盤上之前被壓縮。

這個功能幾乎是總被需要的,因為它意味着:

  • 完整的文檔在搜尋結果中直接就是可用的 - 不需要額外的請求來得到完整文檔
  • _source

    字段讓部分更新請求(Partial Update Request)成為可能
  • 當映射發生變化而需要對資料進行重索引(Reindex)時,你可以直接在ES中完成,而不需要從另外一個資料存儲(Datastore)(通常較慢)中擷取所有文檔
  • 在你不需要檢視整個文檔時,可以從

    _source

    直接抽取出個别字段,通過

    get

    或者

    search

    請求傳回
  • 調試查詢更容易,因為可以清楚地看到每個文檔包含的内容,而不需要根據一個ID清單來對它們的内容進行猜測

即便如此,存儲

_store

字段确實會占用磁盤空間。如果以上的任何好處對你都不重要,你可以使用以下的映射來禁用

_source

字段:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "_source": {
                "enabled":  false
            }
        }
    }
}      

在一個搜尋請求中,你可以隻要求傳回部分字段,通過在請求正文(Request body)中指定

_source

參數:

GET /_search
{
    "query":   { "match_all": {}},
    "_source": [ "title", "created" ]
}      

這些字段的值會從

_source

字段中被抽取出來并傳回,而不是完整的

_source

存儲字段(Stored fields)

除了将一個字段的值索引外,你還可以選擇将字段的原始值(Original field value)進行

store

來友善将來的擷取。有過使用Lucene經驗的使用者會使用存儲字段來選擇在搜尋結果中能夠被傳回的字段。實際上,

_source

字段就是一個存儲字段。

在ES中,設定個别的文檔字段為存儲字段通常都是一個錯誤的優化。整個文檔已經通過

_source

字段被儲存了。使用

_source

參數來指定需要抽取的字段幾乎總是更好的方案。

中繼資料:

_all

字段

在簡化搜尋(Search Lite)中我們介紹了

_all

字段:它是一個特殊的字段,将其它所有字段的值當做一個大的字元串進行索引。

query_string

查詢語句(以及

?q=john

這種形式的查詢)在沒有指定具體字段的時候,預設搜尋的就是

_all

字段。

_all

字段在一個新應用的探索階段有用處,此時你對文檔的最終結構還不太确定。你可以直接使用任何搜尋字元串,并且也能夠得到需要的結果:

GET /_search
{
    "match": {
        "_all": "john smith marketing"
    }
}      

随着你的應用逐漸成熟,對搜尋要求也變的更加精确,你就會越來越少地使用

_all

字段。 

_all

字段是一種搜尋的霰彈槍政策(Shotgun approach)。通過查詢個别字段,你可以對搜尋結果有更靈活,強大和細粒度的控制,來保證結果是最相關的。

在相關度算法(Relevance Algorithm)中一個重要的考量因素是字段的長度:字段越短,那麼它就越重要。一個出現在較短的

title

字段中的詞條會比它出現在較長的

content

字段中時要更重要。而這個關于字段長度的差别在

_all

字段中時不存在的。

如果你決定不再需要

_all

字段了,那麼可以通過下面的映射設定來禁用它:

PUT /my_index/_mapping/my_type
{
    "my_type": {
        "_all": { "enabled": false }
    }
}      

可以使用

include_in_all

設定來對每個字段進行設定,是否需要将它包含到

_all

字段中,預設值是

true

。在一個對象(或者在根對象上)設定

include_in_all

會改變其中所有字段的預設設定。

如果你隻需要将部分字段添加到

_all

字段中,比如

title

overview

summary

tags

等,用來友善地進行全文搜尋。那麼相比完全禁用

_all

,你可以将

include_in_all

預設設定為對所有字段禁用,然後對你選擇的字段啟用:

PUT /my_index/my_type/_mapping
{
    "my_type": {
        "include_in_all": false,
        "properties": {
            "title": {
                "type":           "string",
                "include_in_all": true
            },
            ...
        }
    }
}      

需要記住的是,

_all

字段也隻不過是一個被解析過的

string

字段。它使用預設的解析器來解析其值,無論來源字段中設定的是什麼解析器。和任何

string

字段一樣,你也可以配置

_all

字段應該使用的解析器:

PUT /my_index/my_type/_mapping
{
    "my_type": {
        "_all": { "analyzer": "whitespace" }
    }
}      

中繼資料:文檔ID(Document Identity)

和文檔ID相關的有四個中繼資料字段:

  • _id

    :文檔的字元串ID
  • _type

    :文檔的類型
  • _index

    :文檔屬于的索引
  • _uid

    _type

    _id

    的結合,

    type#id

預設情況下,

_uid

字段會被儲存和索引。意味着它可以被擷取,也可以被搜尋。

_type

字段會被索引但不會被儲存。

_id

_index

既不會被索引也不會被儲存,也就是說它們實際上是不存在的。

盡管如此,你還是能夠查詢

_id

字段,就好像它是一個實實在在的字段一樣。ES使用

_uid

字段來得到

_id

。盡管你可以為這些字段修改

index

store

設定,但是你幾乎不需要這麼做。

_id

字段有一個你也許會用到的設定:

path

,它用來告訴ES:文檔應該從某個字段中抽取一個值來作為它自身的

_id

PUT /my_index
{
    "mappings": {
        "my_type": {
            "_id": {
                "path": "doc_id" 
            },
            "properties": {
                "doc_id": {
                    "type":   "string",
                    "index":  "not_analyzed"
                }
            }
        }
    }
}      

以上請求設定

_id

來源于

doc_id

字段。

然後,當你索引一份文檔:

POST /my_index/my_type
{
    "doc_id": "123"
}      

得到的結果是這樣的:

{
    "_index":   "my_index",
    "_type":    "my_type",
    "_id":      "123", 
    "_version": 1,
    "created":  true
}      

警告

這樣做雖然很友善,但是它對于

bulk

請求(參考為什麼選擇有趣的格式)有一些性能影響。處理請求的節點不能夠利用優化的批處理格式:僅通過解析中繼資料行來得知哪個分片(Shard)應該接受該請求。相反,它需要解析文檔正文部分。

動态映射(Dynamic Mapping)

當ES在文檔中碰到一個以前沒見過的字段時,它會利用動态映射來決定該字段的類型,并自動地對該字段添加映射。

有時這正是需要的行為,但有時不是。你或許不知道在以後你的文檔中會添加哪些字段,但是你想要它們能夠被自動地索引。或許你隻是想要忽略它們。或者 - 尤其當你将ES當做主要的資料存儲使用時 - 大概你會希望這些未知的字段會抛出異常來提醒你注意這一問題。

幸運的是,你可以通過

dynamic

設定來控制這一行為,它能夠接受以下的選項:

  • true

    :預設值。動态添加字段
  • false

    :忽略新字段
  • strict

    :如果碰到陌生字段,抛出異常

dynamic

設定可以适用在根對象上或者

object

類型的任意字段上。你應該預設地将

dynamic

設定為

strict

,但是為某個特定的内部對象啟用它:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "dynamic":      "strict", 
            "properties": {
                "title":  { "type": "string"},
                "stash":  {
                    "type":     "object",
                    "dynamic":  true 
                }
            }
        }
    }
}      

my_type

對象上如果碰到了未知字段則會抛出一個異常。 在

stash

對象上會動态添加新字段。

通過以上的映射,你可以向

stash

添加新的可搜尋的字段:

PUT /my_index/my_type/1
{
  "title": "This doc adds a new field",
  "stash": {
    "new_field": "Success!"
  }
}      

但是,如果在頂層對象上試圖添加新字段則會失敗:

PUT /my_index/my_type/1
{
    "title":     "This throws a StrictDynamicMappingException",
    "new_field": "Fail!"
}      

NOTE

dynamic

設定為

false

并不會改變

_source

字段的内容 - 

_source

字段仍然會儲存你索引的整個JSON文檔。隻不過是陌生的字段将不會被添加到映射中,以至于它不能被搜尋到。

自定義動态映射

如果你知道你需要動态的添加的新字段,那麼你也許會啟用動态映射。然而有時動态映射的規則又有些不夠靈活。幸運的是,你可以調整某些設定來讓動态映射的規則更加适合你的資料。

date_detection

當ES碰到一個新的字元串字段時,它會檢查該字串是否含有一個可被識别的日期,比如

2014-01-01

。如果存在,那麼它會被識别為一個

date

類型的字段。否則會将它作為

string

進行添加。

有時這種行為會導緻一些問題。如果你想要索引一份這樣的文檔:

{ "note": "2014-01-01" }      

假設這是

note

字段第一次被發現,那麼根據規則它會被作為

date

字段添加。但是如果下一份文檔是這樣的:

{ "note": "Logged out" }      

這時該字段顯然不是日期,但是已經太遲了。該字段的類型已經是日期類型的字段了,是以這會導緻一個異常被抛出。

可以通過在根對象上将

date_detection

設定為

false

來關閉日期檢測:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "date_detection": false
        }
    }
}      

有了以上的映射,一個字元串總是會被當做

string

類型。如果你需要一個

date

字段,你需要手動地添加它。

NOTE

ES中識别日期的方法可以通過

dynamic_date_formats

設定改變。

dynamic_templates

通過

dynamic_templates

,你可以擁有對新字段的動态映射規則擁有完全的控制。你設定可以根據字段名稱或者類型來使用一個不同的映射規則。

每個模闆都有一個名字,可以用來描述這個模闆做了什麼。同時它有一個

mapping

用來指定具體的映射資訊,和至少一個參數(比如

match

)用來規定對于什麼字段需要使用該模闆。

模闆的比對是有順序的 - 第一個比對的模闆會被使用。比如我們可以為

string

字段指定兩個模闆:

  • es

    :以

    _es

    結尾的字段應該使用

    spanish

    解析器
  • en

    :其它所有字段使用

    english

    解析器

我們需要将

es

模闆放在第一個,因為它相比能夠比對所有字元串字段的

en

模闆更加具體:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "dynamic_templates": [
                { "es": {
                      "match":              "*_es", 
                      "match_mapping_type": "string",
                      "mapping": {
                          "type":           "string",
                          "analyzer":       "spanish"
                      }
                }},
                { "en": {
                      "match":              "*", 
                      "match_mapping_type": "string",
                      "mapping": {
                          "type":           "string",
                          "analyzer":       "english"
                      }
                }}
            ]
}}}      

match_mapping_type

允許你隻對特定類型的字段使用模闆,正如标準動态映射規則那樣,比如

string

long

等。

match

參數隻會比對字段名,

path_match

參數用于比對對象中字段的完整路徑,比如

address.*.name

可以比對如下字段:

{
    "address":
        "city":
            "name": "New York"
        }
    }
}      

unmatch

path_unmatch

模式能夠用來排除某些字段,沒有被排除的字段則會被比對。

更多的配置選項可以在根對象的參考文檔中找到。

預設映射(Default Mapping)

一般情況下,索引中的所有類型都會有相似的字段和設定。是以将這些常用設定在

_default

映射中指定會更加友善,這樣就不需要在每次建立新類型的時候都重複設定。

_default

映射的角色是新類型的模闆。所有在

_default

映射之後建立的類型都會包含所有的預設設定,除非顯式地在類型映射中進行覆寫。

比如,我們使用

_default

映射對所有類型禁用

_all

字段,唯獨對

blog

類型啟用它。可以這樣實作:

PUT /my_index
{
    "mappings": {
        "_default_": {
            "_all": { "enabled":  false }
        },
        "blog": {
            "_all": { "enabled":  true  }
        }
    }
}      

_default_

映射同時也是一個聲明作用于整個索引的動态模闆(Dynamic Templates)的好地方。

資料重索引

雖然你可以向索引中添加新的類型,或者像類型中添加新的字段,但是你不能添加新的解析器或者對現有字段進行修改。如果你這麼做了,就會讓已經索引的資料變的不正确,導緻搜尋不能正常的進行。

為已經存在的資料适用這些更改的最簡單的方法就是重索引(Reindex):建立一個擁有最新配置的索引,然後将所有舊索引中的資料拷貝到新的索引中。

_source

字段的一個優勢是在ES中你已經擁有了整個文檔。你不需要通過資料庫來重建你的索引,這種方法通常會更慢。

為了從舊索引中高效地對所有文檔進行重索引,可以使用scan和scroll來批量地從舊索引中擷取文檔,然後使用bulk API将它們添加到新索引中。

批量重索引

你可以同時執行多個重索引任務,但是你顯然不想要它們的結果有任何重疊。可以根據

date

或者

timestamp

字段對重索引的任務進行劃分,成為規模較小的任務:
GET /old_index/_search?search_type=scan&scroll=1m
{
    "query": {
        "range": {
            "date": {
                "gte":  "2014-01-01",
                "lt":   "2014-02-01"
            }
        }
    },
    "size":  1000
}      
如果你正在持續地更改舊索引中的資料,想必你也希望這些更改也會被反映到新索引中。這可以通過再次運作重索引任務來完成,但是還是可以通過對日期字段進行過濾來得到在上次重索引開始後才被添加的文檔。

索引别名和零停機時間(Index Alias and Zero Downtime)

重索引的問題在于你需要更新你的應用讓它使用新的索引名。而索引别名可以解決這個問題。

一個索引别名就好比一個快捷方式(Shortcut)或一個符号連結(Symbolic Link),索引别名可以指向一個或者多個索引,可以在任何需要索引名的API中使用。使用别名可以給我們非常多的靈活性。它能夠讓我們:

  • 在一個運作的叢集中透明地從一個索引切換到另一個索引
  • 讓多個索引形成一個組,比如

    last_three_months

  • 為一個索引中的一部分文檔建立一個視圖(View)

我們會在本書的後面讨論更多關于别名的其它用途。現在我們要解釋的是如何在零停機時間的前提下,使用别名來完成從舊索引切換到新索引。

有兩個用來管理别名的端點(Endpoint):

_alias

用來完成單一操作,

_aliases

用來原子地完成多個操作。

在這個場景中,我們假設你的應用正在使用一個名為

my_index

的索引。實際上,

my_index

是一個别名,它指向了目前正在使用的真實索引。我們會在真實索引的名字中包含一個版本号碼:

my_index_v1

my_index_v2

等。

首先,建立索引

my_index_v1

,然後讓别名

my_index

指向它:

PUT /my_index_v1 
PUT /my_index_v1/_alias/my_index       

可以通過下面的請求得到别名指向的索引:

GET /*/_alias/my_index      

或者查詢指向真實索引的有哪些别名:

GET /my_index_v1/_alias/*      

它們都會傳回:

{
    "my_index_v1" : {
        "aliases" : {
            "my_index" : { }
        }
    }
}      

然後,我們決定要為索引中的一個字段更改其映射。當然,我們是不能修改目前的映射的,是以我們隻好對資料進行重索引。此時我們建立了擁有新的映射的索引

my_index_v2

PUT /my_index_v2
{
    "mappings": {
        "my_type": {
            "properties": {
                "tags": {
                    "type":   "string",
                    "index":  "not_analyzed"
                }
            }
        }
    }
}      

緊接着,我們會根據資料重索引中的流程将

my_index_v1

中的資料重索引到

my_index_v2

。一旦我們确定了文檔已經被正确地索引,我們就能夠将别名切換到新的索引上了。

一個别名能夠指向多個索引,是以當我們将别名指向新的索引時,我們還需要删除别名原來到舊索引的指向。這個改變需要是原子的,即意味着我們需要使用

_aliases

端點:

POST /_aliases
{
    "actions": [
        { "remove": { "index": "my_index_v1", "alias": "my_index" }},
        { "add":    { "index": "my_index_v2", "alias": "my_index" }}
    ]
}      

現在你的應用就在零停機時間的前提下,實作了舊索引到新索引的透明切換。

TIP

即使你認為目前的索引設計是完美的,将來你也會發現有一些部分需要被改變,而那個時候你的索引已經在生産環境中被使用了。

在應用中使用索引别名而不是索引的真實名稱,這樣你就能夠在任何需要的時候執行重索引操作。應該充分地使用别名。

原文來自:

http://blog.csdn.net/dm_vincent/article/details/41440539

http://blog.csdn.net/dm_vincent/article/details/41478321

http://blog.csdn.net/dm_vincent/article/details/41511821

http://blog.csdn.net/dm_vincent/article/details/41593987

http://blog.csdn.net/dm_vincent/article/details/41643793

原文翻譯自:

http://blog.csdn.net/dm_vincent/article/details/41643793

繼續閱讀