這是ElasticSearch 2.4 版本系列的第三篇:
- ElasticSearch入門 第一篇:Windows下安裝ElasticSearch
- ElasticSearch入門 第二篇:集群配置
- ElasticSearch入門 第三篇:索引
- ElasticSearch入門 第四篇:使用C#添加和更新文檔
- ElasticSearch入門 第五篇:使用C#查詢文檔
- ElasticSearch入門 第六篇:復合數據類型——數組,對象和嵌套
- ElasticSearch入門 第七篇:分析器
- ElasticSearch入門 第八篇:存儲
- ElasticSearch入門 第九篇:實現正則表達式查詢的思路
ElasticSearch是文檔型數據庫,索引(Index)定義了文檔的邏輯存儲和字段類型,每個索引可以包含多個文檔類型,文檔類型是文檔的集合,文檔以索引定義的邏輯存儲模型,比如,指定分片和副本的數量,配置刷新頻率,分配分析器等,存儲在索引中的海量文檔分布式存儲在ElasticSearch集群中。
ElasticSearch是基於Lucene框架的全文搜索引擎,將所有文檔的信息寫入到倒排索引(Inverted Index)的數據結構中,倒排索引建立的是索引中詞和文檔之間的映射關系,在倒排索引中,數據是面向詞(Term)而不是面向文檔的。
ElasticSearch的對象模型,跟關系型數據庫模型相比:
- 索引(Index):相當於數據庫,用於定義文檔類型的存儲;在同一個索引中,同一個字段只能定義一個數據類型;
- 文檔類型(Type):相當於關系表,用於描述文檔中的各個字段的定義;不同的文檔類型,能夠存儲不同的字段,服務於不同的查詢請求;
- 文檔(Document):相當於關系表的數據行,存儲數據的載體,包含一個或多個存有數據的字段;
- 字段(Field):文檔的一個Key/Value對;
- 詞(Term):表示文本中的一個單詞;
- 標記(Token):表示在字段中出現的詞,由該詞的文本、偏移量(開始和結束)以及類型組成;
索引是由段(Segment)組成的,段存儲在硬盤(Disk)文件中,段不是實時更新的,這意味着,段在寫入磁盤后,就不再被更新。ElasticSearch引擎把被刪除的文檔的信息存儲在一個單獨的文件中,在搜索數據時,ElasticSearch引擎首先從段中查詢,再從查詢結果中過濾被刪除的文檔,這意味着,段中存儲着“被刪除”的文檔,這使得段中含有”正常文檔“的密度降低。多個段可以通過段合並(Segment Merge)操作把“已刪除”的文檔將從段中物理刪除,把未刪除的文檔合並到一個新段中,新段中沒有”已刪除文檔“,因此,段合並操作能夠提高索引的查找速度,但段合並是IO密集型的操作,需要消耗大量的硬盤IO。
一,創建索引
在創建索引之前,首先了解RESTful API的調用風格,在管理和使用ElasticSearch服務時,常用的HTTP動詞有下面五個:
- GET 請求:獲取服務器中的對象
- 相當於SQL的Select命令
- GET /blogs:列出所有博客
- POST 請求:在服務器上更新對象
- 相當於SQL的Update命令
- POST /blogs/ID:更新指定的博客
- PUT 請求:在服務器上創建對象
- 相當於SQL的Create命令
- PUT /blogs/ID:新建一個博客
- DELETE 請求:刪除服務器中的對象
- 相當於SQL的Delete命令
- DELETE /blogs/ID:刪除指定的博客
- HEAD 請求:僅僅用於獲取對象的基礎信息
1,禁用自動創建索引
推薦設置:在全局配置文件 elasticsearch.yml 中,禁用自動創建索引:
action.auto_create_index:false
2,手動創建索引
創建索引的語法是:PUT http://host:port/index_name/ + index_configuration
其中,index_name是創建的索引的名字,indiex_configuration 是向ElasticSearch服務器傳遞的請求負載的主體,數據格式是json,用於定義索引的配置信息:映射節(mappings)和配置節(settings)。
在創建索引時,需要精心設計索引的映射節(mappings)和配置節(settings),本例創建blog索引和articles文檔類型,創建索引的語法是:
PUT http://localhost:9200/blog/
下文詳細介紹ElasticSearch索引的映射(Mapping)配置,詳細信息請參考《Elasticsearch Reference [2.4] » Mapping》。注意,ElasticSearch引擎是大小寫敏感的,強制性要求索引名和文檔類型小寫,對於字段名,ElasticSearch引擎會將首字母小寫,建議在配置索引,文檔類型和字段名時,都使用小寫字母。
二,索引映射節(mappings)
1,索引結構
索引是由文檔類型構成的,在mappings字段中定義索引的文檔類型,示例代碼中為blog索引定義了三個文檔類型:articles,followers和comments
{ "mappings":{ "articles":{ }, "followers":{ }, "comments":{ } } }
2,文檔屬性
文檔屬性定義了文檔類型的共用屬性,適用於文檔的所有字段:
- dynamic_date_formats屬性:該屬性定義可以識別的日期格式列表;
- dynamic屬性:默認值為true,允許動態地向文檔類型中加入新的字段。推薦設置為false,禁止向文檔中添加字段,這樣,文檔類型的所有字段必須在索引映射的properties屬性中顯式定義,在properties字段中未定義的字段都將會ElasticSearch忽略。
- dynamic設置為ture:默認值,新增加的字段被添加到索引映射中;
- dynamic設置為false:新增加的字段會被忽略;
- dynamic設置為strict:當向文檔中新增字段時,ElasticSearch引擎拋出異常;
{ "mappings":{ "articles":{ "dynamic":false, "dynamic_date_formats":["yyyy-MM-dd hh:mm:ss", "yyyy-MM-dd" ], "properties":{ "id":{}, "title":{}, "author":{}, "content":{}, "postedat":{} } } } }
三,文檔的字段屬性
1,字段的數據類型
字段的數據類型由字段的屬性type指定,ElasticSearch支持的基礎數據類型主要有:
- 字符串類型:string;
- 數值類型:字節(byte)、2字節(short)、4字節(integer)、8字節(long)、float、double;
- 布爾類型:boolean,值是true或false;
- 時間/日期類型:date,用於存儲日期和時間;
- 二進制類型:binary;
- IP地址類型:ip,以字符串形式存儲IPv4地址;
- 特殊數據類型:token_count,用於存儲索引的字數信息
在文檔類型的properties屬性中,定義字段的type屬性,指定字段的數據類型,屬性properties 用於定義文檔類型的字段屬性,或字段對象的屬性:
"properties":{
"id":{"type":"long"},
2,字段的公共屬性
- index:該屬性控制字段是否編入索引被搜索,該屬性共有三個有效值:analyzed、no和not_analyzed:
- analyzed:表示該字段被分析,編入索引,產生的token能被搜索到;
- not_analyzed:表示該字段不會被分析,使用原始值編入索引,在索引中作為單個詞;
- no:不編入索引,無法搜索該字段;
- 其中analyzed是分析,分解的意思,默認值是analyzed,表示將該字段編入索引,以供搜索。
- store:指定是否將字段的原始值寫入索引,默認值是no,字段值被分析,能夠被搜索,但是,字段值不會存儲,這意味着,該字段能夠被查詢,但是不會存儲字段的原始值。
- boost:字段級別的助推,默認值是1,定義了字段在文檔中的重要性/權重;
- include_in_all:該屬性指定當前字段是否包括在_all字段中,默認值是ture,所有的字段都會包含_all字段中;如果index=no,那么屬性include_in_all無效,這意味着當前字段無法包含在_all字段中。
- copy_to:該屬性指定一個字段名稱,ElasticSearch引擎將當前字段的值復制到該屬性指定的字段中;
- doc_values:文檔值是存儲在硬盤上的索引時(indexing time)數據結構,對於not_analyzed字段,默認值是true,analyzed string字段不支持文檔值;
- fielddata:字段數據是存儲在內存中的查詢時(querying time)數據結構,只支持analyzed string字段;
- null_value:該屬性指定一個值,當字段的值為NULL時,該字段使用null_value代替NULL值;在ElasticSearch中,NULL 值不能被索引和搜索,當一個字段設置為NULL值,ElasticSearch引擎認為該字段沒有任何值,使用該屬性為NULL字段設置一個指定的值,使該字段能夠被索引和搜索。
3,字符串類型常用的其他屬性
- analyzer:該屬性定義用於建立索引和搜索的分析器名稱,默認值是全局定義的分析器名稱,該屬性可以引用在配置結點(settings)中自定義的分析器;
- search_analyzer:該屬性定義的分析器,用於處理發送到特定字段的查詢字符串;
- ignore_above:該屬性指定一個整數值,當字符串字段(analyzed string field)的字節數量大於該數值之后,超過長度的部分字符數據將不能被analyzer處理,不能被編入索引;對於 not analyzed string字段,超過長度的部分字符將被忽略,不會被編入索引。默認值是0,禁用該屬性;
- position_increment_gap:該屬性指定在相同詞的位置上增加的gap,默認值是100;
- index_options:索引選項控制添加到倒排索引(Inverted Index)的信息,這些信息用於搜索(Search)和高亮顯示:
- docs:只索引文檔編號(Doc Number)
- freqs:索引文檔編號和詞頻率(term frequency)
- positions:索引文檔編號,詞頻率和詞位置(序號)
- offsets:索引文檔編號,詞頻率,詞偏移量(開始和結束位置)和詞位置(序號)
- 默認情況下,被分析的字符串(analyzed string)字段使用positions,其他字段使用docs;
- docs:只索引文檔編號(Doc Number)
分析器(analyzer)把analyzed string 字段的值,轉換成標記流(Token stream),例如,字符串"The quick Brown Foxes",可能被分解成的標記(Token)是:quick,brown,fox。這些詞(term)是該字段的索引值,這使用對索引文本的查找更有效率。字段的屬性 analyzer 用於指定在index-time和search-time時,ElasticSearch引擎分解字段值的分析器名稱。
4,數值類型的其他屬性
- precision_step:該屬性指定為數值字段每個值生成的term數量,值越低,產生的term數量越高,范圍查詢越快,索引越大,默認值是4;
- ignore_malformed:忽略格式錯誤的數值,默認值是false,不忽略錯誤格式,對整個文檔不處理,並且拋出異常;
- coerce:默認值是true,嘗試將字符串轉換為數值,如果字段類型是整數,那么將小數取整;
5,日期類型的其他屬性
- format:指定日期的格式,例如:“yyyy-MM-dd hh:mm:ss”
- precision_step:該屬性指定數值字段每隔多少數值,生成一個詞(term);step值越低,產生的詞數量越高,范圍查詢越快,索引越大,占用存儲空間越大;
- ignore_malformed:忽略錯誤格式,默認值是false,不忽略錯誤格式;
6,多字段(fields)
在fields屬性中定義一個或多個字段,該字段的值和當前字段值相同,可以設置一個字段用於搜索,一個字段用於排序等。
"properties": { "id":{ "type":"long", "fields":{ "id2":{"type":"long","index":"not_analyzed"} } },
7,文檔值(doc_values)
默認情況下,多數字段都被一起編入索引,用戶使用倒排索引(Inverted Index)可以搜索到相應的詞(Term),倒排索引支持在唯一的有序詞列表中查找特定詞,或檢查文檔中是否包含某個詞,但是,對於排序(Sort),聚合和在腳本中訪問特定字段的值(Field value),這三個操作需要執行不同的數據訪問模式,即單字段數據訪問:在文檔中查找特定的字段,檢查該字段是否包含指定的詞。
文檔值(doc_values)屬性指定將字段的值寫入到硬盤上的列式結構,實現了單個字段的數據訪問模式,能夠高效執行排序和聚合搜索。使用文檔值的字段將有專屬的字段數據緩存實例,無需像普通字段一樣倒排。是存儲在硬盤上的數據結構,在文檔索引時創建。文檔值數據存在硬盤上,在文檔索引時創建,存儲的數據和字段存儲在_source 字段的數據相同,文檔值支持所有的字段類型,除了analyzed string 字段之外。
默認情況下,所有的字段都支持文檔值,默認是啟用的(enabled),如果不需要在單個字段上執行排序或聚合操作,或者從腳本中訪問指定字段的值,那么,可以禁用文檔值,字段的值將不會存儲在硬盤空間中。
"properties": { "status_code": { "type": "string", "index": "not_analyzed" "doc_values": true }, "session_id": { "type": "string", "index": "not_analyzed", "doc_values": false } }
8,字段數據(Fielddata)
字段數據(Fielddata)是存儲在內存中的查詢時數據結構,只支持analyzed string字段。該數據結構在字段第一次執行聚合,排序或被腳本訪問時創建。創建的過程是:在讀取整個倒排索引(Inverted Index)時,ElasticSearch從硬盤上加載倒排索引的每個段(Segment),倒轉詞(Term)和文檔的關系,並將其存儲在JVM堆內存中。加載字段數據的過程是非常消耗IO資源的,一旦被加載,就被存儲在內存中,直到段的生命周期結束。
對於analyzed string字段,fielddata字段是默認啟用的,
"text":{ "type":"string", "fielddata":{ "loading":"lazy" } }
詳細信息,請參考Mapping parameters » fielddata
Analyzed strings use a query-time data structure called fielddata. This data structure is built on demand the first time that a field is used for aggregations, sorting, or is accessed in a script. It is built by reading the entire inverted index for each segment from disk, inverting the term ↔︎ document relationship, and storing the result in memory, in the JVM heap.
9,存儲(store)
存儲(store)屬性指定是否將字段的原始值寫入索引,默認值是no,字段值被分析,能夠被搜索,但是,字段的原始值不會存儲,這意味着,該字段能夠被查詢,但是無法獲取字段的原始值。默認情況下,該字段的值會被存儲到_source字段中,如果想要獲取單個或多個字段的值,而不是整個_source字段,可以使用 source filtering 來實現;但是在特定的條件下,只存儲一個字段的值是有意義的(make sense),例如,一個article文檔包含:title,postdate和content字段,從文檔中只獲取title和postdate字段,並且使_source 字段包含content字段,必須通過store屬性來控制:
"mappings": { "my_type": { "properties": { "title": { "type": "string", "store": true }, "date": { "type": "date", "store": true }, "content": { "type": "string",
"store": false } } } }
10,位置增加間隔(position_increment_gap)
對於analyzed string字段,都會考慮把詞的位置信息,用於支持位置和短語匹配查詢(proximity or phrase queries),例如,有一個字符串字段,該字段中存在多個詞“fake”,ElasticSearch引擎會在每個值之間增加一個gap,以防止短語匹配或位置匹配查詢出現跨越多個詞的異常,這個gap的值就是屬性position_increment_gap,默認值是100;
四,元字段
在索引的映射中,元字段(Meta-field)是以下划線開頭的字段,部分元字段可以配置,部分元字段不可配置,只能用於返回信息。
1,_all 字段,可以配置
ElasticSearch使用_all字段存儲其他字段的數據以便搜索,默認情況下,_all字段是啟用的,包含了索引中所有字段的數據,然而這一字段使索引變大,如果不需要,請禁用該字段,或排除某些字段。為了在_all字段中不包括某個特定字段,在字段中設置“include_in_all”屬性為false。
禁用_all字段,需要修改映射配置:
{ "articles":{ "_all":{ "enabled":false } } }
2,_source 字段,可以配置
_source字段表示在生成索引的過程中,存儲發送到ElasticSearch的原始JSON文檔,默認情況下,該字段會被啟用,因為索引的局部更新功能依賴該字段。
{ "articles":{ "_source":{ "enabled":true } } } { "articles":{ "_source":{ "excludes":["Content","Comments"], "includes":["author"] } } }
3,_routing 字段,可以配置
路由字段,將一個文檔值進行哈希映射,並將該文檔路由到指定的分片,路由的公式是:
shard_num = hash(_routing) % num_primary_shards
在ElasticSearch 2.4 版本中,path參數被廢棄,使用的默認字段是_id,設置required為true,表示路由字段在進行索引的CRUD操作時必需顯式賦值。
{ "articles":{ "_routing":{ "required":true } } }
在put 命令中,使用自定義的路由字段,以下示例使用 user1字段作為路由字段更新和查詢文檔:
PUT my_index/my_type/1?routing=user1 { "title": "This is a document" } GET my_index/my_type/1?routing=user1
4,不可配置的元字段
- _index:返回文檔所屬的索引
- _uid:返回文檔的type和id
- _type:返回文檔類型(type)
- _id:返回文檔的ID;
- _size:返回文檔的_source字段中函數的字節數量;
- _field_names:返回文檔中不包含null值的字段名稱;
詳細信息,請參考:Mapping » Meta-Fields
五,索引配置節(settings)
1,配置索引的分片和副本數量
ElasticSearch索引是有一個或多個分片組成的,每個分片是索引的一個水平分區,包含了文檔數據的一部分;每個分片有0,1或多個副本,分片的副本和分片存儲相同的數據。
示例代碼,為索引創建5個分片,分片沒有副本:
"settings":{ "number_of_shards":5, "number_of_replicas":0,
2,配置分析器(analyzer)
在配置結點的analysis屬性中配置分析器,參考官方文檔了解更多,
分詞器(tokenizer)是系統預定義的,常用的分詞器是:
- standard:默認值,用於大多數歐洲語言的標准分詞器
- simple:基於非字母字符來分詞,並將其轉化為小寫形式
- whitespace:基於空格來分詞
- stop:除了simple的所有功能,還能基於停用詞(stop words)過濾數據;
- pattern:使用正則表達式分詞;
- snowball:除了standard提供的分詞功能之外,還提供詞干提取功能;
過濾器是系統預定義的,常用的過濾器是:
- asciifolding
- lowercase
- kstem
在配置結點中,自定義分析器(analyzer)示例代碼:

{ "settings":{ "index":{ "analysis":{ "analyzer":{ "myanalyzer_name":{ "tokenizer":"standard", "filter":[ "asciifolding", "lowercase", "ourEnglishFilter" ] } }, "filter":{ "ourEnglishFilter":{ "type":"kstem" } } } } } }
六,刪除索引
刪除索引的語法是: DELETE http://localhost:9200/blog
七,更新索引
索引的更新分為逐個文檔的更新和批量文檔更新:
1,單個文檔(Individual Document)的更新
單個文檔更新的語法是:POST http://localhost:9200/blog/articles/1 +文檔對象的JSON數據
POST http://localhost:9200/blog/articles/1
文檔對象的JSON數據示例如下:
{ "id":1, "title":"Elasticsearch index", "Author":"悅光陰", "content":"xxxxxxxxxxx", "postedat":"2017-03-14" }
2,批量文檔的更新(Bluk)
批量文檔更新的語法是:POST http://localhost:9200/_bulk + 批量文檔對象的JSON數據,在_bulk 端進行批量更新操作。
在傳遞的請求主體中,每一個請求分為兩個JSON數據,第一個JSON數據包含操作說明的描述信息,第二個JSON數據包含文檔對象:

{ "index":{ "_index":"blog", "_type":"ariticles", "_id":1 } } { "id":1, "title":"Elasticsearch index", "Author":"悅光陰", "content":"xxxxxxxxxxx", "postedat":"2017-03-14" } { "index":{ "_index":"blog", "_type":"ariticles", "_id":2 } } { "id":2, "title":"Elasticsearch index", "Author":"悅光陰", "content":"xxxxxxxxxxx", "postedat":"2017-03-14" }
八,搜索索引
在_search端對索引數據進行搜索,ES查詢的語法非常復雜,總體來說,ElasticSearch支持聚合查詢和簡單查詢。
1,按照路由搜索
路由可以控制文檔和查詢轉發的目的分片,ElasticSearch計算路由字段的哈希值,對於相同的路由值,將產生相同的哈希值,分配到特定的分片上;如果在查詢時,指定路由值,那么只需要搜索單個分片而不是整個索引,就能獲取查詢結果。
路由字段由文檔類型的_routing屬性定義,在查詢時,使用routing參數來查找特定路由的文檔:
GET http://localhost:9200/blog/_search?routing=1235&q=article_id=100
2,聚合和簡單查詢
請閱讀《ElasticSearch查詢 第一篇:搜索API》
附:索引的配置文檔

{ "settings":{ "number_of_shards":5, "number_of_replicas":0 }, "mappings":{ "articles":{ "_routing":{ "required":false }, "_all":{ "enabled":false }, "_source":{ "enabled":true }, "dynamic_date_formats":[ "yyyy-MM-dd", "yyyyMMdd" ], "dynamic":"false", "properties":{ "articleid":{ "type":"long", "store":true, "index":"not_analyzed", "doc_values":true, "ignore_malformed":true, "include_in_all":true, "null_value":0, "precision_step":16 }, "title":{ "type":"string", "store":true, "index":"analyzed", "doc_values":false, "ignore_above":0, "include_in_all":true, "index_options":"positions", "position_increment_gap":100, "fields":{ "title":{ "type":"string", "store":true, "index":"not_analyzed", "doc_values":true, "ignore_above":0, "include_in_all":false, "index_options":"docs", "position_increment_gap":100 } } }, "author":{ "type":"string", "store":true, "index":"analyzed", "doc_values":false, "ignore_above":0, "include_in_all":true, "index_options":"positions", "position_increment_gap":100, "fields":{ "author":{ "type":"string", "index":"not_analyzed", "include_in_all":false, "doc_values":true } } }, "content":{ "type":"string", "store":true, "index":"analyzed", "doc_values":false, "ignore_above":0, "include_in_all":false, "index_options":"positions", "position_increment_gap":100 }, "postat":{ "type":"date", "store":true, "doc_values":true, "format":[ "yyyy-MM-dd", "yyyyMMdd" ], "index":"not_analyzed", "ignore_malformed":true, "include_in_all":true, "null_value":"2000-01-01", "precision_step":16 } } } } }
在head插件中,打開"Any Request"窗體,輸入索引名稱:blog;在操作列表中選擇PUT,並將配置文檔作為請求body,點擊下方的“Request”按鈕,向Elasticsearch引擎發起請求,當右邊面板中出現"acknowledged":true 時,說明索引blog創建成功。
在測試階段,可以禁用路由(_routing)和_all字段,啟用源(_source)字段,以便更好的觀察索引的行為。
當啟用dynamic屬性時,推薦所有字段的名稱都使用小寫,
參考文檔:
Elasticsearch Reference [2.4] » Mapping » Mapping parameters