对象及Nested对象
内部对象边界搜索异常
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"properties" : {
"first_name" : {
"type" : "keyword"
},
"last_name" : {
"type" : "keyword"
}
}
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
POST my_movies/_doc/1
{
"title":"Speed",
"actors":[
{
"first_name":"Keanu",
"last_name":"Reeves"
},
{
"first_name":"Dennis",
"last_name":"Hopper"
}
]
}
POST my_movies/_search
{
"query": {
"bool": {
"must": [
{"match": {"actors.first_name": "Keanu"}},
{"match": {"actors.last_name": "Hopper"}}
]
}
}
}
为什么会搜索到不需要的结果?
- 存储时,内部对象的边界并没有考虑在内,JSON格式被处理成扁平式键值对的结构
- 当对多个字段进行查询时,导致了意外的搜索结果
-
Nested Dtat Type 解决方案
Nested 数据类型:允许对象数组中的对象被独立索引
- 使用 nested 和 properties 关键字,将所有 actors 索引到多个分隔的文档
在内部,Nested 文档会被保存在两个 Lucene 文档中,在查询时做 Join 处理
DELETE my_movies
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"type": "nested",
"properties" : {
"first_name" : {"type" : "keyword"},
"last_name" : {"type" : "keyword"}
}},
"title" : {
"type" : "text",
"fields" : {"keyword":{"type":"keyword","ignore_above":256}}
}
}
}
}
POST my_movies/_doc/1
{
"title":"Speed",
"actors":[
{
"first_name":"Keanu",
"last_name":"Reeves"
},
{
"first_name":"Dennis",
"last_name":"Hopper"
}
]
}
查询
POST my_movies/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "Speed"}},
{
"nested": {
"path": "actors",
"query": {
"bool": {
"must": [
{"match": {
"actors.first_name": "Keanu"
}},
{"match": {
"actors.last_name": "Hopper"
}}
]
}
}
}
}
]
}
}
}
聚合
POST my_movies/_search
{
"size": 0,
"aggs": {
"actors": {
"nested": {
"path": "actors"
},
"aggs": {
"actor_name": {
"terms": {
"field": "actors.first_name",
"size": 10
}
}
}
}
}
}
文档的父子关系
Elasticsearch 提供了类似关系型数据库中的 Join 的实现。使用 Join 数据类型实现,可以通过维护 Parent / Child 的关系。从而分离两个对象。父文档和子文档是两个独立的文档,更新父文档无需重新索引子文档。
父子关系 Mapping 声明
```json
my_blogs:索引名称
blog_comments_relation:关联关系名称
blog:父文档名称
comment:子文档名称
PUT my_blogs { “mappings”: { “properties”: { “blog_comments_relation”: { “type”: “join”, “relations”: { “blog”: “comment” } }, “content”: { “type”: “text” }, “title”: { “type”: “keyword” } } } }
<a name="BCXZP"></a>
## 索引父子文档
```json
# my_blogs:索引名称
# blog1、blog2:文档id
PUT my_blogs/_doc/blog1
{
"title":"Learning Elasticsearch",
"content":"learning ELK @ geektime",
"blog_comments_relation":{
"name":"blog"
}
}
PUT my_blogs/_doc/blog2
{
"title":"Learning Hadoop",
"content":"learning Hadoop",
"blog_comments_relation":{
"name":"blog"
}
}
# my_blogs:索引名称
# comment1、comment2、comment3:子文档id
# blog1、blog2:父文档id,通过routing确保文档与父文档存储同一分片
PUT my_blogs/_doc/comment1?routing=blog1
{
"comment":"I am learning ELK",
"username":"Jack",
"blog_comments_relation":{
"name":"comment",
"parent":"blog1"
}
}
PUT my_blogs/_doc/comment2?routing=blog2
{
"comment":"I like Hadoop!!!!!",
"username":"Jack",
"blog_comments_relation":{
"name":"comment",
"parent":"blog2"
}
}
PUT my_blogs/_doc/comment3?routing=blog2
{
"comment":"Hello Hadoop",
"username":"Bob",
"blog_comments_relation":{
"name":"comment",
"parent":"blog2"
}
}
搜索父子文档
#根据父文档ID查看
GET my_blogs/_doc/blog2
# Parent Id 查询
POST my_blogs/_search
{
"query": {
"parent_id": {
"type": "comment",
"id": "blog2"
}
}
}
# Has Child 查询,返回父文档
POST my_blogs/_search
{
"query": {
"has_child": {
"type": "comment",
"query" : {
"match": {
"username" : "Jack"
}
}
}
}
}
# Has Parent 查询,返回相关的子文档
POST my_blogs/_search
{
"query": {
"has_parent": {
"parent_type": "blog",
"query" : {
"match": {
"title" : "Learning Hadoop"
}
}
}
}
}
更新子文档
PUT my_blogs/_doc/comment3?routing=blog2
{
"comment": "Hello Hadoop??",
"blog_comments_relation": {
"name": "comment",
"parent": "blog2"
}
}
字段建模
如何进行字段建模
- Text
- 用于全文本字段,文本会被 Analyzer 分词
- 默认不支持聚合分析以及排序,需要设置 fielddata 为 true
- Keyword
- 用于 id,枚举以及不需要分词的文本。例如电话号码,email地址,手机号码,邮政编码,性别等
- 适用于 Filter (精准匹配),Sorting 和 Aggregations
设置多字段类型
数值类型:尽量选择贴近的类型。例如可以用 byte,就不要用 long。
- 枚举类型:设置为 keyword,即便是数字,也应该设置成 keyword,获得更好的性能。
-
检索
如不需要检索,排序和聚合分析:Enable 设置成 false。
- 如不需要检索,Index 设置成 false。
对需要检索的字段,可以通过如下配置,设定存储粒度:Index options / Norms:不需要归一化数据。
聚合以及排序
如不需要检索,排序和聚合分析:Enable 设置成 false。
- 如不需要排序或者聚合分析功能:Doc_values / fielddata 设置成 false。
更新频繁,聚合查询频繁的 keyword 类型的字段:推荐将 eager_global_ordinals 设置为 true。
额外的存储
是否需要专门存储当前字段数据
- Store 设置成 true,可以存储该字段的原始内容
- 一般结合 _source 的 enabled 为 false 的时候使用
- Disable _source:节约磁盘,适用于指标型数据
- 一般建议先考虑增加压缩比
- 无法看到 _source 字段,无法做 Relndex,无法做 Update。