赞
踩
String类型可以和java的string、mysql的varchar等同,但是为何会分为text、keyword呢?这两者又有什么区别?
ES作为全文检索引擎,它强大的地方就在于 分词和倒排序索引。而 text 和 keyword 的区别就 在于是否分词(ps:什么叫分词?举个简单例子,“中国我爱你”这句话,如果使用了分词,那么这句话在底层的储存可能就是“中国”、“我爱你”, 被拆分成了两个关键字)
就拿刚才的例子来说,“中国我爱你”这句话,如果使用text类型储存,我们不去特殊定义它的分词器,那么ES就会使用默认的分词器 standard 。
ES的分词器(可先有个概念)
下图就是“中国我爱你”的ES分词效果:
GET _analyze
{
"text": "我爱你中国",
"analyzer": "standard"
}
{ "tokens" : [ { "token" : "我", "start_offset" : 0, "end_offset" : 1, "type" : "<IDEOGRAPHIC>", "position" : 0 }, { "token" : "爱", "start_offset" : 1, "end_offset" : 2, "type" : "<IDEOGRAPHIC>", "position" : 1 }, { "token" : "你", "start_offset" : 2, "end_offset" : 3, "type" : "<IDEOGRAPHIC>", "position" : 2 }, { "token" : "中", "start_offset" : 3, "end_offset" : 4, "type" : "<IDEOGRAPHIC>", "position" : 3 }, { "token" : "国", "start_offset" : 4, "end_offset" : 5, "type" : "<IDEOGRAPHIC>", "position" : 4 } ] }
这意味着什么呢?如果你使用text类型去储存你本不想分词的string类型,你在查询的时候,查询结果将违背你的预期。
简单看个示例:
# 创建索引 PUT /toherotest { "mappings": { "_doc":{ "properties" : { "field1" : { "type" : "text" } } } } } # 存入数据 POST /toherotest/_doc/1 { "field1":"中国我爱你" }
# 查询 索引下的所有数据
GET /toherotest/_search
条件查询,等价于mysql的** where field1 = “中国我爱你”** 。发现居然查询不到
GET /toherotest/_search
{
"query": {
"term": {
"field1": {
"value": "中国我爱你"
}
}
}
}
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 0, "relation" : "eq" }, "max_score" : null, "hits" : [ ] } }
检索中国也搜不到
GET /toherotest/_search
{
"query": {
"term": {
"field1": {
"value": "中国"
}
}
}
}
再根据刚才的ES分词效果,我们检索其中一个字,居然神奇的检索到了
GET /toherotest/_search
{
"query": {
"term": {
"field1": {
"value": "中"
}
}
}
}
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.2876821, "hits" : [ { "_index" : "toherotest", "_type" : "_doc", "_id" : "1", "_score" : 0.2876821, "_source" : { "field1" : "中国我爱你" } } ] } }
这是为什么呢?我们发现在使用term查询(等价于mysql的=)时却查不到结果,其实就是因为text类型会分词,简单理解就是“中国我爱你”这句话在ES的倒排序索引中存储的是单个字,所以无法检索。
2)keywor——不会分词
我们新增一个keyword类型的字段field2,再来看看检索效果:
# 测试分词效果 GET /_analyze { "text": ["中国我爱你"], "analyzer": "keyword" } # 结果 { "tokens": [ { "token": "中国我爱你", "start_offset": 0, "end_offset": 5, "type": "word", "position": 0 } ] } # 新增 字段类型 keyword PUT toherotest/_mapping/_doc { "properties": { "field2": { "type": "keyword" } } } # 新增数据 PUT /toherotest/_doc/12 { "field2":"中国我爱你" } # 查询 GET /toherotest/_doc/_search { "query": { "term": { "field2": { "value": "中国我爱你" } } } }
可以发现,类型为keyword,通过term是可以查询到,说明ES对keyword是没有分词的。
PUT my_index { "mappings": { "properties": { "date": { "type": "date" } } } } PUT my_index/_doc/1 { "date": "2015-01-01" } PUT my_index/_doc/2 { "date": "2015-01-01T12:10:30Z" } PUT my_index/_doc/3 { "date": 1420070400001 } GET my_index/_search { "sort": { "date": "asc"} }
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "my_index", "_type" : "_doc", "_id" : "1", "_score" : null, "_source" : { "date" : "2015-01-01" }, "sort" : [ 1420070400000 ] }, { "_index" : "my_index", "_type" : "_doc", "_id" : "3", "_score" : null, "_source" : { "date" : 1420070400001 }, "sort" : [ 1420070400001 ] }, { "_index" : "my_index", "_type" : "_doc", "_id" : "2", "_score" : null, "_source" : { "date" : "2015-01-01T12:10:30Z" }, "sort" : [ 1420114230000 ] } ] } }
同时ES的date类型允许我们规定格式,可以使用的格式有:
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd
epoch_millis(毫秒值)
# 规定格式如下: || 表示或者
PUT my_index
{
"mappings": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
注意:一旦我们规定了格式,如果新增数据不符合这个格式,ES将会报错mapper_parsing_exception。
ES的复杂类型有3个,Array、object、nested。
1)Array:在Elasticsearch中,数组不需要专用的字段数据类型。默认情况下,任何字段都可以包含零个或多个值,但是,数组中的所有值都**必须具有相同的数据类型。
**举个简单例子理解下:比如上一个例子中的field1这个字段,可以只存储一个值“中国我爱你”,同时也可以存储一个数组:[“这是”,“一个”,“数组”]
# 新增数据
POST /toherotest/_doc/2
{
"field1":["这是","一个","数组"]
}
查询内容
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "toherotest", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "_source" : { "field1" : "中国我爱你" } }, { "_index" : "toherotest", "_type" : "_doc", "_id" : "12", "_score" : 1.0, "_source" : { "field2" : "中国我爱你" } }, { "_index" : "toherotest", "_type" : "_doc", "_id" : "2", "_score" : 1.0, "_source" : { "field1" : [ "这是", "一个", "数组" ] } } ] } }
2)object我相信大家都能理解;需要注意的是,object类型的字段,也可以有多个值,形成List的数据结构。
重点:List中的 object不允许彼此独立地索引查询。这是什么意思呢?
举个简单例子:我们现在有2条数据:数据结构都是一个List
如果此时我们的需求是,只要 name = “tohero1”and “age”= 1 的数据,根据我们常规的理解,只有第一条数据才能被检索出来,但是真的是这样么?我们写个例子看看:
# 添加 属性为object的字段 field3 PUT toherotest/_mapping/_doc { "properties": { "field3": { "type": "object" } } } # 新增数据 POST /toherotest/_doc/3 { "field3":[ { "name":"tohero1", "age":1 }, { "name":"tohero2", "age":2 } ] } POST /toherotest/_doc/4 { "field3": [ { "name":"tohero1", "age":2 }, { "name":"tohero2", "age":1 } ] } #执行查询语句 GET /toherotest/_doc/_search { "query": { "bool": { "must": [ { "term": { "field3.name": "tohero1" } }, { "term": { "field3.age": 1 } } ] } } }
查询结果如下:
{ "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1.287682, "hits": [ { "_index": "toherotest", "_type": "_doc", "_id": "4", "_score": 1.287682, "_source": { "field3": [ { "name": "tohero1", "age": 2 }, { "name": "tohero2", "age": 1 } ] } }, { "_index": "toherotest", "_type": "_doc", "_id": "3", "_score": 1.287682, "_source": { "field3": [ { "name": "tohero1", "age": 1 }, { "name": "tohero2", "age": 2 } ] } } ] } }
**可以看到两条数据都被我们检索到了。**所以,现在理解什么叫做“**object不允许彼此独立地索引查询”**了吧。(在数组对象中, 只要存在满足分别条件的的数据就行。而不能做到对针对每一个元素独立的检索)
#执行查询语句 GET /toherotest/_search { "query": { "bool": { "must": [ { "match": { "field3.name": "我ggg" } }, { "term": { "field3.age": 1 } } ] } } }
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.738981, "hits" : [ { "_index" : "toherotest", "_type" : "_doc", "_id" : "5", "_score" : 1.738981, "_source" : { "field3" : [ { "name" : "我爱你中国", "age" : 1 }, { "name" : "tohero2", "age" : 2 } ] } } ] } }
就像上面这个搜索,从分包检索数组中每个元素的角度看,我们认为应该是检索不到内容的
但是,我们在日常的使用过程中,常规的需求就是,**希望object能被独立的索引,**难道es满足不了这个需求么?那是不可能。下面就来看下nested类型。
官方定义:官方释义:这个nested类型是object一种数据类型,允许对象数组以相互独立的方式进行索引
nested属于object类型的一种,是Elasticsearch中用于复杂类型对象数组的索引操作。Elasticsearch没有内部对象的概念,因此,ES在存储复杂类型的时候会把对象的复杂层次结果扁平化为一个键值对列表。
字段值为复杂类型的情况,即字段值为非基本数据类型。
# 添加 属性为object的字段 field3
PUT toheronesttest/_mapping/_doc
{
"properties": {
"field3": {
"type": "nested",
"properties": {
"name": {
"type": "text"
}
}
}
}
#执行查询语句 GET /toheronesttest/_search { "query": { "nested": { "path": "field3", "query": { "bool": { "must": [ { "term": { "field3.name": "tohero2" } }, { "term": { "field3.age": 1 } } ] } } } } }
那么结果就是扁平化后的筛选结果了,也就是数组中的其中一个元素满足条件才能被检索出来
{ "took" : 2, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.6931472, "hits" : [ { "_index" : "toheronesttest", "_type" : "_doc", "_id" : "4", "_score" : 1.6931472, "_source" : { "field3" : [ { "name" : "tohero1", "age" : 2 }, { "name" : "tohero2", "age" : 1 } ] } } ] } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。