对象及Nested对象

内部对象边界搜索异常

  1. PUT my_movies
  2. {
  3. "mappings" : {
  4. "properties" : {
  5. "actors" : {
  6. "properties" : {
  7. "first_name" : {
  8. "type" : "keyword"
  9. },
  10. "last_name" : {
  11. "type" : "keyword"
  12. }
  13. }
  14. },
  15. "title" : {
  16. "type" : "text",
  17. "fields" : {
  18. "keyword" : {
  19. "type" : "keyword",
  20. "ignore_above" : 256
  21. }
  22. }
  23. }
  24. }
  25. }
  26. }
  27. POST my_movies/_doc/1
  28. {
  29. "title":"Speed",
  30. "actors":[
  31. {
  32. "first_name":"Keanu",
  33. "last_name":"Reeves"
  34. },
  35. {
  36. "first_name":"Dennis",
  37. "last_name":"Hopper"
  38. }
  39. ]
  40. }
  1. POST my_movies/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {"match": {"actors.first_name": "Keanu"}},
  7. {"match": {"actors.last_name": "Hopper"}}
  8. ]
  9. }
  10. }
  11. }

为什么会搜索到不需要的结果?

  • 存储时,内部对象的边界并没有考虑在内,JSON格式被处理成扁平式键值对的结构
  • 当对多个字段进行查询时,导致了意外的搜索结果
  • 可以用 Nested Dtat Type 解决这个问题

    Nested Dtat Type 解决方案

  • Nested 数据类型:允许对象数组中的对象被独立索引

  • 使用 nested 和 properties 关键字,将所有 actors 索引到多个分隔的文档
  • 在内部,Nested 文档会被保存在两个 Lucene 文档中,在查询时做 Join 处理

    1. DELETE my_movies
    2. PUT my_movies
    3. {
    4. "mappings" : {
    5. "properties" : {
    6. "actors" : {
    7. "type": "nested",
    8. "properties" : {
    9. "first_name" : {"type" : "keyword"},
    10. "last_name" : {"type" : "keyword"}
    11. }},
    12. "title" : {
    13. "type" : "text",
    14. "fields" : {"keyword":{"type":"keyword","ignore_above":256}}
    15. }
    16. }
    17. }
    18. }
    1. POST my_movies/_doc/1
    2. {
    3. "title":"Speed",
    4. "actors":[
    5. {
    6. "first_name":"Keanu",
    7. "last_name":"Reeves"
    8. },
    9. {
    10. "first_name":"Dennis",
    11. "last_name":"Hopper"
    12. }
    13. ]
    14. }

    查询

    1. POST my_movies/_search
    2. {
    3. "query": {
    4. "bool": {
    5. "must": [
    6. {"match": {"title": "Speed"}},
    7. {
    8. "nested": {
    9. "path": "actors",
    10. "query": {
    11. "bool": {
    12. "must": [
    13. {"match": {
    14. "actors.first_name": "Keanu"
    15. }},
    16. {"match": {
    17. "actors.last_name": "Hopper"
    18. }}
    19. ]
    20. }
    21. }
    22. }
    23. }
    24. ]
    25. }
    26. }
    27. }

    聚合

    1. POST my_movies/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "actors": {
    6. "nested": {
    7. "path": "actors"
    8. },
    9. "aggs": {
    10. "actor_name": {
    11. "terms": {
    12. "field": "actors.first_name",
    13. "size": 10
    14. }
    15. }
    16. }
    17. }
    18. }
    19. }

    文档的父子关系

    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” } } } }

  1. <a name="BCXZP"></a>
  2. ## 索引父子文档
  3. ```json
  4. # my_blogs:索引名称
  5. # blog1、blog2:文档id
  6. PUT my_blogs/_doc/blog1
  7. {
  8. "title":"Learning Elasticsearch",
  9. "content":"learning ELK @ geektime",
  10. "blog_comments_relation":{
  11. "name":"blog"
  12. }
  13. }
  14. PUT my_blogs/_doc/blog2
  15. {
  16. "title":"Learning Hadoop",
  17. "content":"learning Hadoop",
  18. "blog_comments_relation":{
  19. "name":"blog"
  20. }
  21. }
  1. # my_blogs:索引名称
  2. # comment1comment2comment3:子文档id
  3. # blog1blog2:父文档id,通过routing确保文档与父文档存储同一分片
  4. PUT my_blogs/_doc/comment1?routing=blog1
  5. {
  6. "comment":"I am learning ELK",
  7. "username":"Jack",
  8. "blog_comments_relation":{
  9. "name":"comment",
  10. "parent":"blog1"
  11. }
  12. }
  13. PUT my_blogs/_doc/comment2?routing=blog2
  14. {
  15. "comment":"I like Hadoop!!!!!",
  16. "username":"Jack",
  17. "blog_comments_relation":{
  18. "name":"comment",
  19. "parent":"blog2"
  20. }
  21. }
  22. PUT my_blogs/_doc/comment3?routing=blog2
  23. {
  24. "comment":"Hello Hadoop",
  25. "username":"Bob",
  26. "blog_comments_relation":{
  27. "name":"comment",
  28. "parent":"blog2"
  29. }
  30. }

搜索父子文档

  1. #根据父文档ID查看
  2. GET my_blogs/_doc/blog2
  3. # Parent Id 查询
  4. POST my_blogs/_search
  5. {
  6. "query": {
  7. "parent_id": {
  8. "type": "comment",
  9. "id": "blog2"
  10. }
  11. }
  12. }
  13. # Has Child 查询,返回父文档
  14. POST my_blogs/_search
  15. {
  16. "query": {
  17. "has_child": {
  18. "type": "comment",
  19. "query" : {
  20. "match": {
  21. "username" : "Jack"
  22. }
  23. }
  24. }
  25. }
  26. }
  27. # Has Parent 查询,返回相关的子文档
  28. POST my_blogs/_search
  29. {
  30. "query": {
  31. "has_parent": {
  32. "parent_type": "blog",
  33. "query" : {
  34. "match": {
  35. "title" : "Learning Hadoop"
  36. }
  37. }
  38. }
  39. }
  40. }

更新子文档

  1. PUT my_blogs/_doc/comment3?routing=blog2
  2. {
  3. "comment": "Hello Hadoop??",
  4. "blog_comments_relation": {
  5. "name": "comment",
  6. "parent": "blog2"
  7. }
  8. }

字段建模

如何进行字段建模

  1. 确定字段类型
  2. 确定字段是否需要搜索以及分词
  3. 确定字段是否需要聚合以及排序
  4. 确实是否需要额外的存储

    字段类型

    Text v.s Keyword

  • Text
    • 用于全文本字段,文本会被 Analyzer 分词
    • 默认不支持聚合分析以及排序,需要设置 fielddata 为 true
  • Keyword
    • 用于 id,枚举以及不需要分词的文本。例如电话号码,email地址,手机号码,邮政编码,性别等
    • 适用于 Filter (精准匹配),Sorting 和 Aggregations
  • 设置多字段类型

    • 默认会为文档类型设置成 text,并且设置一个 keyword 的子字段
    • 在处理人类语言时,通过增加“英文”,“拼音”和“标准”分词器,提高搜索结构

      结构化数据

  • 数值类型:尽量选择贴近的类型。例如可以用 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。