head插件可以用来快速查看elasticsearch中的数据概况以及非全量的数据,也支持控件化查询和rest请求,但是体验都不是很好。一般就用它来看各个索引的数据量以及分片的状态。
直接访问下面的地址即可:
http://localhost:9200/_plugin/head/
sense插件可以方便的执行rest请求,但是中文输入的体验不是很好。
安装sense只需要在Kibana端安装插件即可,插件会自动安装到kibana的应用菜单中
http://localhost:5601/app/sense
marvel
marvel工具可以帮助使用者监控elasticsearch的运行状态,不过这个插件需要license。安装完license后可以安装marvel的agent,agent会收集elasticsearch的运行状态。
然后在Kibana段安装marvel插件,这个插件与sense类似,都集成在kibana的导航列表面
es提供了两种形式的java客户端(要将es提供的客户端支持的代码引入自己的java工程或者项目中去),这两种客户端与es的交互均是9300端口。集群中的节点本身也是通过9300端口彼此通信,使用es原生的传输协议。
所有其他语言可以使用RESTful API 通过端口9200与es进行通信。如 JavaScript、Python、PHP、Ruby、Perl
es请求格式:
crul -XVERB "protocal://host:port/path?queryStr' -d 'body'
示例:
curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query" : { "match_all" : { } }
}'
缩略格式(sense控制台也使用此种缩略格式):
GET /_count
{"query" : { "match_all" : { } }}
应用程序中的对象一般有复杂的数据结构,当存储到关系型数据库中时,往往需要对其进行扁平化处理,而在查询数据时,又要重新构造对象,这其中需要大量的工作进行设计。
而es是直接存储对象或者文档,并对整个文档进行索引,使之可以被检索。在es中是对文档进行索引、检索、排序和过滤,而不是对行列数据。
一个es集群可以包含多个索引(库),每个索引又可以包含多种类型(类/表),每个类型可以包含多个文档(对象/行-一条数据),每个文档可以包含多个属性(字段/列)
无需事先进行创建索引库、创建类型、指定类型的属性等操作,只需直接执行如下的索引一个文档的操作,es会在后台使用默认设置完成其它的一切管理任务。
PUT /magecorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
PUT /megacorp/employee/2
{
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests": [ "music" ]
}
PUT /megacorp/employee/3
{
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about": "I like to build cabinets",
"interests": [ "forestry" ]
}
路径 /megacorp/employee/1 包含了三部分的信息:
megacorp 索引名称
employee 类型名称
1 特定雇员的ID
请求体 —— JSON 文档 —— 包含了这位员工的所有详细信息,他的名字叫 John Smith ,今年 25 岁,喜欢攀岩。
GET /megacorp/employee/1
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
适用于通过命令行或者交互式客户端做即席查询或一次性查询,比较简洁,在开发阶段非常方便,但也晦涩、易出错。
相反的在生产环境中更多地使用功能全面的请求体 request body方式的 查询API,除了能完成以上所有功能,还有一些附加功能。
例:
+name:(jane kitty) join_date:>2014-05-01 -age:<20 +(aggregations geo)
/_search?q=mary
,则是查询文档中包含“mary”这个词的所有文档。GET /megacorp/employee/_search?q=last_name:Smith #检索某类型下的文档,指定查询条件
GET /megacorp/employee/_search #检索某类型下的所有文档,无查询条件
{
"took": 6, #耗时?
"timed_out": false,
"_shards": { ... },
"hits": { #检索出的结果的,汇总信息?
"total": 3,
"max_score": 1, #检索出的结果中相关度最高分
"hits": [
{
"_index": "megacorp", #索引库
"_type": "employee", #类型
"_id": "3",
"_score": 1, #相关度得分
"_source": {
"first_name": "Douglas",
"last_name": "Fir",
"age": 35,
"about": "I like to build cabinets",
"interests": [ "forestry" ]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 1,
"_source": {
"first_name": "John"
...
}
}...
]
}
}
轻量搜索:
GET /matecorp/employee/_search?q=last_name:Smith
用DSL查询表达式如下:
GET /matecorp/employee/_search
{
"query":{
"match":{
"last_name" : "Smith"
}
}
}
#查询last_name=Smith,年龄大于30的
GET /magecorp/employee/_search
{
"query":{
"bool":{
"must":{ #必须匹配
"match" : {
"last_name" : "Smith"
}
},
"filter":{ #过滤器
"range" : { #取值范围,相对于between
"age" : {
"gt" : 30
}
}
}
}
}
}
#检索所有喜欢攀岩(rock climbing)的员工
GET /magecorp/employee/_search
{
"query":{
"match":{ #若要完全匹配整个短语,用match_phrase
"about": "rock climbing"
}
}
}
#结果如下:名为John的雇员的相关性得分_score要高于名为Jane的,因为Jane的about为"I like to collect rock albums",只匹配到了单词rock
{
...
"hits": {
"total": 2,
"max_score": 0.16273327,
"hits": [
{
...
"_score": 0.16273327,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_score": 0.016878016,
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
GET /magecorp/employee/_search
{
"query" : {
"match_phrase" : { #匹配整个短语
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
#挖掘所有兴趣爱好的受欢迎度
GET /megacorp/employee/_search
{
"aggs":{ #指定查询为聚合查询
"popularity_interests":{ #给查询结果取个名
"terms":{ #汇总方式:terms:列出所有结果项
"field" : "interests" #依据的是一个属性,属性名是interests
}
}
}
}
{
...
"hits": { ... },
"aggregations": {
"popularity_interests": { #名为popularity_interests的查询的结果
"buckets": [
{
"key": "music",
"doc_count": 2
},
{
"key": "forestry",
"doc_count": 1
},
{
"key": "sports",
"doc_count": 1
}
]
}
}
}
#指定查询条件的聚合:叫 Smith 的兴趣的受欢迎程度,直接添加适当的查询来组合查询:
GET /magecorp/employee/_search
{
"query" : { "match" : { "first_name" : "Smith" } },
"aggs" : { "popularity_interests" : { "terms" : { "field" : "interests" } } }
}
#分级汇总(嵌套聚合):某兴趣爱好的人气的平均年龄段
GET /magecorp/employee/_search
{
"aggs" : {
"popularity_interests" : {
"terms" : { "field" : "interests" } , #汇总方式:terms:列出所有结果项
"aggs" : { #嵌套聚合汇总
"avg_age" : { #查询结果名称
"avg" : { #汇总方式:avg:求平均
"field" : "age"
}
}
}
}
}
}
cluster.name: escluster
node.name: node-0
network.host: 192.168.174.20
http.port: 9200
discovery.zen.ping.multicast.enabled: false
discovery.zen.ping.unicast.hosts: ["192.168.174.20","192.168.174.21","192.168.174.22"]
node : number_of_nodes , number_of_data_nodes
shard :
PUT /myIndex
{
"settings" : {
"number_of_shards" : "3", #3个主分片
"number_of_replicas" : "2", #每个分片有两个副本,共6个副本分片
}
}
PUT /my_index/settings
{
"number_of_replicas":5
}
当然,如果只是在相同节点数目的集群上增加更多的副本分片并不能提高性能,因为每个分片从节点上获得的资源会变少。 你需要增加更多的硬件资源来提升吞吐量。
但是更多的副本分片数提高了数据冗余量:按照上面的节点配置,我们可以在失去2个节点的情况下不丢失任何数据
id可以是自己指定的(如果文档本身有一个可以作唯一标识的字段,如学号等),那么应该在将文档索引到es中时直接提供:
PUT /{index}/{type}/{id}
{ "field1":"value"}
#如果不自己指定id,那么可以用autogenerating IDS,注意语法改成用POST(存储文档在这个url命名空间下),而不是PUT(使用这个URL存储文档):
POST /{index}/{type}
{ "field1":"value"}
#自动生成的 ID 是 URL-safe、 基于 Base64 编码且长度为20个字符的 GUID 字符串。 这些 GUID 字符串由可修改的 FlakeID 模式生成,这种模式允许多个节点并行生成唯一 ID ,且互相之间的冲突概率几乎为零
#返回头部head信息(-i)
GET /magecorp/employee/1
{
...
"_version": 1,
"found": true,
"_source": {
"first_name": "John",
"last_name": "Smith",
...
}
}
curl -i -XGET http://hadoop01:9200/megacorp/employee/10
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=UTF-8
Content-Length: 65
{"_index":"megacorp","_type":"employee","_id":"10",
"found":false}
#返回文档的一个或者几个字段(?_source=字段名1,字段名2)
GET /megacorp/employee/1?_source=first_name,age
{
"_index": "megacorp",
...
"found": true,
"_source": {
"first_name": "John",
"age": "25"
}
}
#只想获得_source字段,不想获得元数据,则设置端点为_source
GET /megacorp/employee/1/_source
{
"first_name": "John",
"last_name": "Smith",
...
}
#status为200时为存在,status为404时,为不存在
curl -i -XHEAD http://hadoop01:9200/megacorp/employee/10
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
#如果直接用 PUT /{index}/{type}/{id} {body}
#那么可能是更新,也可能是创建新文档(当id已经存在时,更新文档;否则创建新文档)
#如果需要确保创建一个新的文档,有以下三种请求格式
POST /{index}/{type}/ {body}
PUT /{index}/{type}/{id}/_create
PUT /{index}/{type}/{id}?op_type=create
当指定的id已经存在时,会返回status=409,document_already_exists
DELETE /megacorp/employee/1
#如果要删除的id存在,则status为200,且返回的_version为加1后的
#如果id不存在,则status为404
Elasticsearch 是分布式的。当文档创建、更新或删除时, 新版本的文档必须复制到集群中的其他节点。
Elasticsearch 也是异步和并发的,这意味着这些复制请求被并行发送,并且到达目的地时也许 顺序是乱的 。 Elasticsearch 需要一种方法确保文档的旧版本不会覆盖新的版本。
es通过检查版本号来检测是否发生了并发冲突(conflict),如果冲突,则返回status:409。
{
"error": {
"root_cause": [
...
],
"type": "version_conflict_engine_exception",
"reason": "[blog][1]: version conflict, current [2], provided [1]",
"index": "website",
"shard": "3"
},
"status": 409
}
?version=源版本号
来检测是否发生了并发的冲突 PUT /megacorp/employee/2?version=1
?version=新版本号&version_type=external
来检测是否发生了冲突。 #创建一个ID=4的雇员,指定版本号=5
PUT /megacorp/employee/4?version=5&version_type=external
{ "first_name":"hellen",...}
#更新ID=4的雇员信息,指定版本号=10
PUT /megacorp/employee/4?version=10&version_type=external
{ "first_name":"Helen",...}
POST /megacorp/employee/1/_update
{
"doc" : { "interests" : ["swiming" , "pingpang" ] }
}
#或者用groovy脚本更新,注意要配置一下允许脚本
POST /megacorp/employee/1/_update
{
"script" : "ctx._source.interests+=newInterest",
"params" :{
"newInterest" : "swiming"
}
}
GET /_mget
{
"docs":[
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1"
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_source": "interests" #指定取回的字段
}
]
}
GET /megacorp/employee/_mget
{
"ids" : ["1","2"]
}
bulk API提供在一个步骤中进行多个create\index\update\delete的不同action的操作。
批量请求避免了单独处理每个请求的网络延时和开销,因此可以提高性能。但同时整个批量请求都要由接收到请求的节点加载到内存中,则其它后来的请求所能获得的内存就会相应缩减。因此批量操作并不是越多越好,超过某个量,性能将不再提升,反而可能降低,一般占用内存5M~15M比较合适。最好的办法是逐渐增加批的大小,进行尝试。一般以1000-5000条为一个批。
格式如下:
POST /_bulk 或者 POST /{index}/{type}/_bulk
{action1 : {metadata1}}
{request body1}
{action2 : {metadata2}}
{request body2}
其中action必须是以下选项之一:
- create :如果文档不存在,则创建文档(PUT /{index}/{type}/{id}/_create)
- index:创建一个新文档或者对现有文档进行全部更新(PUT /{index}/{type}/{id})
- update:对一个文档进行部分更新(POST /{index}/{type}/{id}/_update)
- delete:删除一个文档(DELETE /{index}/{type}/{id})
POST /_bulk
{"create":{"_index": "megacorp","_type":"employee","_id": "4"}} #create,不存在则创建,已存在则报错
{"first_name": "helilio",...} #注意body的两个花括号{}必须是在同一行
{"index":{"_index": "megacorp","_type":"employee","_id": "1"}} #不存在在创建,一存在则替换整个文档
{"first_name": "John",...}
{"update":{"_index": "megacorp", "_type": "employee", "_id": "2"}} #部分更新
{"doc":{"age": "11"}
{"delete":{"_index": "megacorp", "_type": "employee", "_id": "1"}}
shard = hash(_id) % number_of_primary_shards
因此索引库一旦创建后,则不能再修改主分片数,否则就无法正确的找到文档在索引库的哪个分片上
GET /_search
查询所有索引库下的所有文档,默认返回前10个文档/idx1,idx2/_search #索引库idx1,idx2下的所有文档
/_all/user,tweet/_search #所有索引库下类型为user,tweet的所有文档
/idx1,idx2/user,tweet/_search
/idx*,db*/_search #所有名称以idx或者db为前缀的索引库下的所有文档
/_search?size=5&from=10
对文本分析的过程
1. 将文本分成一个个的词条
2. 将每个词条统一化为标准格式
分析器就是执行上面的工作,分三个步骤:
1. 字符过滤器:在分词前先整理下字符串,如将’&’转成 and
2. 分词器:字符串被分成一个个的词条。例如空格分词器、
3. Token过滤器:每个词条按顺序通过Token过滤器,如将词条转为小写,提取词根,删除无意义词条如the 、a、的、了等,增加同义词如jump则加leap
es自带的分词器:
- 标准分析器(默认,最常用)
根据 Unicode 联盟 定义的 单词边界 划分文本;删除绝大部分标点;将词条小写。
- 简单分析器
在任何不是字母的地方分隔文本;将词条小写。
- 空格分析器
在空格的地方划分文本。(不进行小写处理)
- 语言分析器
特定语言分析器有许多可选语言,会考虑指定语言的特定。例如 ‘英语’:删除英语无意义词(and the a);提取词根;将词条小写。
GET /_analyze
{
"analyzer":"standard", #指定要测试的分析器
"text":" to test The Standard analyzer" #要分析的文本
}
简单核心域类型:
- 字符串:string
- 日期:date
- 整形:byte, short, integer, long
- 浮点型:float, double
- 布尔型:boolean
动态映射:
当索引一个文档,此文档中有一个新的域(属性),即在文档所属类型的映射中未出现过的域,es会对其进行动态映射(根据一些默认的规则,自动设置新域的映射)
新域的值类型 | 新域映射的类型 |
---|---|
整数:123 | long |
浮点数:123 | double |
字符串形式的有效日期:2017-01-01 | date |
普通字符串:foo bar | string |
布尔型:true 或 false | boolean |
查看映射:GET /{index}/_mapping/{type}
自定义域映射:
"index" : "analyzed" #全文索引
"index" : "not_analyzed" #精确值索引
"index" : "no" #不索引:永远检索不到
"analyzer" : "english"
复杂核心域类型:
"null_value": null,
"empty_array": [],
"array_with_null_value": [ null ]
#如:
{
"age": 23
"name":{
"first" : "li",
"last" : "ling",
"full" : "li ling",
}
}
将被索引为:
{
"age":[23],
"name.first":[li],
"name.last":[ling],
"name.full":[li,ling]
}
}
叶子语句(leaf clauses) 与 复合语句(bool)
重要叶子语句:
multi_match:可以作用在多个字段上执行相同的match查询
{
"muti_match":{
"query" : "elastic", #共同的要查询的信息
"fields":[ "title" , "body"]
}
}
range:范围查询(gt, gte , lt , lte)
{
"range":{
"age" : {
"lt" : "30",
"gt": 20
}
}
}
term:精确值查询
{ "term": { "age" : 25 } }
{ "term": { "join_date" : "2017-01-01"} }
terms:精确值查询,允许匹配多个值
{ "terms": { "age" : [25,27,29] } }
exists 和 missing:查询某个字段是否有值
{ "exists": { "field " : "title" } }
复合语句bool的可接收的参数:
{
"bool":{
"must":{"match":{"skills":["java","python"]}}, #最好会java或者python,会影响相关度得分_score
"must_not":{"range":{"exception_salary":{"gt":25000}}}, #期望薪资最好不超过25K,会影响相关度得分_score
"should" :{"match":{"skills":["hadoop","elastic"]}}, #会hadoop和elastic的优先,会影响相关度得分_score
"filter" :{"range":{"age":["lt","30"]}} # 年龄必须不能超过30岁,过滤掉30岁以上的,不参与相关度评分
}
}
{
"constant_score":{
"filter" :{"range":{"age":["lt","30"]}} # 年龄必须不能超过30岁,过滤掉30岁以上的,不参与相关度评分
}
}
默认是按照相关性排序,即按_score字段降序,但是也可以自己指定某个字段进行排序,也可以指定多个字段进行多级排序
{
"query":{...},
"sort" : {
"join_date" : { "order" : "desc"}, #先按照入党日期降序排序
"_score" : { "order" : "desc"} #再按照相关性得分降序排序
}
}
,可以设置"mode" : "min" | "max" | "avg" | "sum"
{
"query":{...},
"sort" : {
"dates" : {
"mode" : "min",
"order" : "desc"
}, #先按照入党日期降序排序
}
}
字符串被分析器分析后也是一个多值字段,但是因为多个值的顺序总是不固定的,而且用设置 “` “mode”为 “min” 或者 “max” 也并不能达到我们想要的效果。
例如,我们原本有两个文档的content分别为“i love marry” 和 “i love lily”,我们想要的效果是按照字母顺序,含 lily的文档 应该 排在 含marry的文档前。
这时,我们可以对content的域映射做如下设置:
"content": {
"type": "string",
"analyzer": "english",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed" #设置此字段不被分析,不被分词,
}
}
}
{"sort":{"content.raw":{"order":"desc"}}}
es的相似度算法 :检索词频率/反向文档频率= TF/IDF ,包括以下内容:
单个查询可以联合使用 TF/IDF 和其他方式,比如短语查询中检索词的距离或模糊查询里的检索词相似度。
相关性并不只是全文本检索的专利。也适用于 yes|no 的子句,匹配的子句越多,相关性评分越高。
如果多条查询子句被合并为一条复合查询语句 ,比如 bool 查询,则每个查询子句计算得出的评分会被合并到总的相关性评分中。
GET /megacorp/employee/_search?explain&format=yaml
{
"query": {
"match": {
"about": "rock"
}
}
}
"_explanation": {
"description": "weight(tweet:honeymoon in 0)
[PerFieldSimilarity], result of:",
"value": 0.076713204,
"details": [
{
"description": "fieldWeight in 0, product of:",
"value": 0.076713204,
"details": [
{
"description": "tf(freq=1.0), with freq of:",
"value": 1,
"details": [
{
"description": "termFreq=1.0", #检索词 `honeymoon` 在这个文档的 `tweet` 字段中的出现次数。
"value": 1
}
]
},
{
"description": "idf(docFreq=1, maxDocs=1)", #检索词 `honeymoon` 在索引上所有文档的 `tweet` 字段中出现的次数。
"value": 0.30685282
},
{
"description": "fieldNorm(doc=0)",
"value": 0.25, #在这个文档中, `tweet` 字段内容的长度 -- 内容越长,值越小。
}
]
}
]
}
分页查询:
取回:
- 协调节点从所有其它节点返回的数据中挑选出需要被取回的_id,再向相关节点发出_mget请求,
- 相关节点根据协调节点的_mget请求,获取丰富文档,返回给协调节点
- 协调节点返回文档给客户端
注意:深度分页本身并不符合人的行为,一般很少人翻到第50页,除非是爬虫spider或者机器人。因此最好直接做一个全局设置,比如_score小于某个值或者是数据量少于1000等等。
游标查询用_doc字段来排序,避免深度分页需要将多个分片上的结果集合并的资源消耗。
游标查询分两个阶段:
开启游标需设置scroll(过期时间),游标查询首先要初始化,会保存当前时间点的数据快照,并在到了过期时间后,丢弃或刷新快照,因此批量的拉取结果需要在过期时间内完成。过期时间设置的足够批量拉取结果就可以了,因为保存的快照也很消耗资源,所以应该及时释放游标窗口。
#游标查询初始化
/_search?scroll=1m #保持游标查询窗口1分钟,过期时间为1分钟
{
"query":{..}
"sort": "_doc", #用_doc字段来排序,而不是_score
"size" :1000
}
#初始化会返回一个_scroll_id
{
"_scroll_id": "cXVlcnlUaGVuRm...",
"took": 7,
"timed_out": false,
...
}
#将初始化返回的_scroll_id作为参数,直接获取下一批结果
/_search/scroll
{
"scroll" : "1m",
"scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NT..."
}
PUT /index_name
{
"settings" : { ... }
"mappings" : {
"type1" : {},
"type2" : {}
}
}
禁止通配符和_all方式,以避免误删索引:
es.yml : action.destructive_requires_name: true
DELETE /index_name
DELETE /index_a,index_b
DELETE /index*
DELETE /_all
DELETE /*
分析器是用于将全文字符串转换成适合搜索的倒排索引。
在索引的设置中,用名称为analysis 的配置项来处理分析器相关的设置。
#创建一个分析器
PUT /my_index
{
"analysis": {
analyzer: {
"es_std" : { #创建一个名称为es_std的分析器
"type" : "standard",
"stop" : "_spanish_" # 用西班牙语的停用词列表
}
}
}
}
#以上只是创建了一个分析器,但是没有将它设置为任何字段的分析器
自定义分析器的几个项
- char_filter(字符过滤器):html_strip(html 清除),
- analyzer(分词器):stadard分词器,whitespace分词器
- filter(token filter 词条过滤器 ):lowercase,stop_words(停用词),stemmer(词干提取),ngram(部分匹配),edge_ngram(自动补全)
#自定义字符过滤器、词条过滤器
PUT /my_index
{
"settings":{
"analysis" : {
"char_filter":{ #创建自定义的字符过滤器:将&转成and
"&_to_and" : {
"type" : { "mapping" },
"mappings" : ["&=> and "]
}
},
"filter": { #创建自定义词条过滤器
"my_stop_words" : {
"type" : "stop",
"stop_words" : [ "the" , "a"]
}
},
"analyzer": { #创建自定义分析器
"my_analyzer" : {
"type" : "custom",
"char_filter" : [ "the" , "a"],
"tokennizer" : "standard",
"filter" : ["lowercase","my_stop_words"]
}
}
}
}
}
在同一个索引下,即便是不同类型下的属性(字段),如果名称相同,那么其映射属性也必须完全相同。不能出现字段名称相同,但是字段type不同或者idnex方式不同。
为什么?
因为在一个lucene索引中,字段是单一扁平的模式,lucene中并不存在类型的概念。在es中的不同类型下的同名属性,在lucene中其实会是同一个字段。
在es中,所有的类型最终其实共享相同的映射。
所以es中的类型,和关系型数据库中的表,的地位和性质完全不同。在关系型数据库中的表是一个完整的逻辑概念,但是在es中的类型,其实只是对多个字段的映射信息的一个命名而已。
比如有个文档: {"name":"liyl","age":33,"address":"shanghai"}
可以在es中可以映射出多种类型:
{"type1":{"properties":{"name":{"type":"string"},"age":{"type":"string"}}}}
{"type2":{"properties":{"age":{"type":"string"},"address":{"type":"string"}}}}
重要的一点是: 类型可以很好的区分同一个集合(索引的文档)中的不同细分。在不同的细分中数据的整体模式是相同的(或相似的)
技术上讲,同一索引下可以有多个类型,且他们的字段都不尽相同。
但是属性完全不同的类型不适合存储在同一索引中,因为这将意味着索引中将有一半的数据是空的(字段将是稀疏的),最终将导致性能问题,因此这种情况下最好放在两个单独的索引里。
许多人容易把字段 _source:{name:"lily",age:25}
的值对象理解为根对象,实际上在es中,或者说在Lucene中,_source:{}只是根对象的一个字段,是根对象的元数据。而_type和_id也是根对象的一个字段,是其元数据。
根对象的组成:
"address" : {
"type" : "string | long | double | date | bool " ,
"index" : "analyzed | not_analyzed | no" , # 字段是否可以被当成全文来搜索,还是作为一个精准的值,还是完全不能被检索到
"analyzer" : "standard | simple | whitespace | english | spanish " #如果index设置为analyzed,即字段被当做全文来搜索,那么在对字段建索引和被搜索时,使用什么分析器
}
各种元数据:都是以下划线_开头的,如:_index、_type、_id、_all、_source
_source:是文档体,会被存储,当然也会占用存储空间。
可以设置为不被存储,但这并不会影响其被索引和检索。方式如下:
PUT /my_index
{
"mappings" : {
"my_type" : {
"_source" :{
"enabled" : false
}
}
}
}
但是选择存储_source字段,则有更多好处:
可以直观的每个文档体的内容,更方便于调试。例如:检索时为什么没有检索到,相关性得分的分值等。
当映射改变,需要重新索引数据时,不需要从其它数据仓库中取数据
当需要看文档体的内容时,不需要再从其它数据仓库(如结构化数据库)中取。
PUT /my_index/_mapping/my_type
{
"_all":{ "enabled" : "false" }
}
include_in_all可以控制每个文档体中的属性,是否要被索引到_all字段中:
#首先在映射信息中,设置include_in_all:false
#然后在某些属性的映射信息中,设置include_in_all:true
PUT /my_index/my_type/_mapping
{
"my_type" : {
"include_in_all" : false,
"properties" : {
"age" : {
"type" : "byte"
"include_in_all" : true
}
}
}
}
_all字段被索引时,仅经过分词器,默认用standard分词器,也可以设置为其它分词器:
PUT /my_index/_mapping
{
"my_type" : {
"_all" : {
"analyzer" : "simple"
}
}
}
_uid :type#id拼接成的字符串,会被存储和索引。
其它设置,可以同时应用在根对象和其它object类型的字段上,如:enabled、dynamic、include_in_all
dynamic 动态映射:true(自动映射新字段)、false(忽略新字段、不对其做映射)、strict(遇到新字段报错。提示管理员事先手动映射)
es对于文档体中出现的新属性会做动态映射,但这可能会出现非预期结果,比如文档体中出现字段”logging” : “2014-01-01”时,es会将logging字段映射为date类型,但实际上我们想将其作为一个字符串类型。
可以对某个类型的属性设置禁用动态映射,但有选择性的对其子文档的属性设置启用动态映射,:
PUT /my_index
{
"mappings" : {
"my_type" : {
"dynamic" : "strict ",
"properties": {
"age" : { "type" : "byte "},
"address" : {
"dynamic" : "true",
"properties": {
"state" : { "type" : "string"},
"province" : { "type" : "string"},
...
}
}
}
}
}
}
date_detection : true 若字段未映射过,且为字符串,则自动检测其是否为符合日期类型规则,若是则将新字段动态映射为date类型。
可以设置不自动检测是否符合日期类型规则:
PUT /my_index
{
"mappings" : {
"my_type" : {
"date_detection" : false,
"properties" : {...}
}
}
}
dynamic_date_formats settings 自定义日期类型字符串检测规则:
# The default value for dynamic_date_formats is:
# [ "strict_date_optional_time","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"]
PUT /my_index
{
"mappings" : {
"my_type" : {
"dynamic_date_formats" : ["yyyy/MM/dd","yyyy-MM-dd"]
}
}
}
dynamic_templates,自定义动态映射规则
如,所有以_es结尾的字段都用spanish解析器,所有以_en结尾的字段都用english解析器:
PUT /my_index
{
"mappings" : {
"my_type" : {
"dynamic_templates" : [
"es" : {
"match" : "*_es", #如果字段以_es结尾
"match_mapping_type" : "string", #并且字段是string了下
"mapping" : { #那么将字段其做如下动态映射
"type" : "string",
"analyzer" : "spanish"
}
},
"en" : {
"match" : "*_en", #如果字段以_en结尾
"match_mapping_type" : "string", #并且字段是string了下
"mapping" : { #那么将字段其做如下动态映射
"type" : "string",
"analyzer" : "english"
}
},
]
}
}
}
_default_ 所有类型的缺省映射
设置了_default_后,不必再为每个新类型重复设置个性化的默认映射配置
PUT /my_index
{
"mappings" : {
"_default_" : {
"_all" : { "enabled" : false}, #默认每个类型都不索引_all字段,
"date_detection" : false, #默认不检测日期字段
"dynamic_date_formats" : ["MM/dd/yyyy"],
"dynamic" : "strict" #如果有未映射的新字段出现,报错
}
}
}
es支持添加新的字段映射,但不支持改动某字段的映射信息,若有如此需求,则需重新索引。重新索引数据,需要以下步骤
并行执行重建索引的任务更有效率,但是注意并行执行时的任务分割方式,一般可以用时间来分割,如下:
GET /old_index/_search?scorll=1m
{
"query" : {
"range" : {
"date" : {
"gt" : "2014-01-01",
"lt" : "2015-01-01"
}
}
},
"sort" : ["_doc"],
"size" : 1000
}
{"product_name":"圆珠笔","product_no":"XHDK-A-1293-#fJ3"}
{"term" : { "product_no" : "XHDK-A-1293-#fJ3" } }
是将所有可能词加载到内存。然后直接给出用户提示,而非实时查询索引。