关系

以下四种常用的方法,用来在 Elasticsearch 中进行关系型数据的管理:

  1. join 类型父子文档可以独立更新,适合子文档频繁变更的情况;
  2. join 类型需要维护联接关系,使用 has_childhas_parent 查询性能差,显著增加查询时间;
  3. join 类型方式通过父文档字段排序子文档,支持较差;

    nested

    join(父子文档)

    说明

    join 类型唯一有意义的使用情况是,数据包含一对多关系,其中一个实体明显多于另一个实体,可以多层级;

  4. 子文档与父文档存储于一个索引中,正常查询会返回所有的文档(父子文档都包括);

  5. 父子文档必须索引在一个分片上,使用 routing 参数实现;
  6. 子文档按父文档字段排序较困难,只支持父文档字段类型为数字的排序;
  7. 尽量少的使用父子关系,仅在子文档远多于父文档时使用;
  8. 避免在一个查询中使用多个父子联合语句;
  9. 在 has_child 查询中使用 filter 上下文,或者设置 score_mode 为 none 来避免计算文档得分;
  10. 保证父文档 IDs 尽量短,以便在 doc values 中更好地压缩,被临时载入时占用更少的内存。

    多代文档使用

  • 联合越多,性能越差;
  • 每一代父文档都要将其字符串类型的 _id 字段存储到内存里,这会占用大量内存。

    示例

    字段映射
    1. PUT my_test
    2. {
    3. "mappings": {
    4. "_doc": {
    5. "dynamic": "strict",
    6. "properties": {
    7. "id": {
    8. "type": "keyword"
    9. },
    10. "ver": {
    11. "type": "keyword"
    12. },
    13. "title": {
    14. "type": "keyword"
    15. },
    16. "text": {
    17. "type": "keyword"
    18. },
    19. "reads": {
    20. "type": "integer"
    21. },
    22. "comments": {
    23. "type": "integer"
    24. },
    25. "article_created_at": {
    26. "type": "date",
    27. "format": "epoch_second"
    28. },
    29. "connection": {
    30. "type": "join", // join 类型
    31. "relations": {
    32. "article": "version" // 父子关系 父/子
    33. }
    34. }
    35. }
    36. }
    37. }
    38. }

    测试数据

  • 父文档

    1. PUT my_test/_doc/1?routing=1&refresh
    2. {
    3. "text": "article a1",
    4. "connection": {
    5. "name": "article"
    6. },
    7. "id": 1,
    8. "article_created_at": 1583731094,
    9. "reads": 132
    10. }
    11. PUT my_test/_doc/2?routing=1&refresh
    12. {
    13. "text": "article a2",
    14. "connection": {
    15. "name": "article"
    16. },
    17. "id": 2,
    18. "article_created_at": 1583731095,
    19. "reads": 89
    20. }
    21. PUT my_test/_doc/3?routing=1&refresh
    22. {
    23. "text": "article a3",
    24. "connection": {
    25. "name": "article"
    26. },
    27. "id": 3,
    28. "article_created_at": 1583731096,
    29. "reads": 32
    30. }
  • 子文档

    1. // 为父文档为 1 的文章增加版本
    2. PUT my_test/_doc/11?routing=1&refresh
    3. {
    4. "text": "ver v1 belong to a1",
    5. "connection": {
    6. "name": "version",
    7. "parent": "1"
    8. },
    9. "ver": 111,
    10. "comments": 100
    11. }
    12. // 为父文档为 1 的文章增加版本
    13. PUT my_test/_doc/12?routing=1&refresh
    14. {
    15. "text": "ver v2 belong to a1",
    16. "connection": {
    17. "name": "version",
    18. "parent": "1"
    19. },
    20. "ver": 222,
    21. "comments": 200
    22. }
    23. // 为父文档为 2 的文章增加版本
    24. PUT my_test/_doc/13?routing=1&refresh
    25. {
    26. "text": "ver vv1 belong to a2",
    27. "connection": {
    28. "name": "version",
    29. "parent": "2"
    30. },
    31. "ver": 1111,
    32. "comments": 200
    33. }

    查询

  • 查询某个父文档下的子文档

    1. GET my_test/_search
    2. {
    3. "query": {
    4. "parent_id": {
    5. "type": "version",
    6. "id": "1"
    7. }
    8. }
    9. }

    返回相应的子文档,不包括父文档;

  • 按父文档的阅读量评分排序(返回子文档)

    1. GET my_test/_search
    2. {
    3. "query": {
    4. "has_parent": {
    5. "parent_type": "article",
    6. "score": true, //开启对父文档评分
    7. "query": {
    8. "function_score": {
    9. "script_score": {
    10. "script": "_score * doc['reads'].value"
    11. }
    12. }
    13. }
    14. }
    15. }
    16. }
  • 查询子文档中 comments 等于 200(返回父文档)

    1. {
    2. "query": {
    3. "has_child" : {
    4. "type" : "version",
    5. "query" : {
    6. "term" : {
    7. "comments" : 200
    8. }
    9. }
    10. }
    11. }
    12. }

    nested 类型使用

    因为 es 会将数组中的子文档进行扁平化存储,建立索引,如果不使用 nested 类型会造成匹配判断混乱。示例:

    1. {
    2. "questions": [
    3. {
    4. "title": "aaa",
    5. "comments": 10
    6. },
    7. {
    8. "title": "bbb",
    9. "comments": 13
    10. }
    11. ]
    12. }

    不使用 nested 类型会造成查询 title = ‘aaa’ 且 comments = 13 匹配到该文档。

    说明

  1. 子文档与父文档存储与同一个文档中;

    示例

    1. PUT my_test2/_doc/1
    2. {
    3. "title": "what kind of animals",
    4. "text": "animals",
    5. "questions": [
    6. {
    7. "content": "dog",
    8. "created_at": 1583742663
    9. },
    10. {
    11. "content": "cat",
    12. "created_at": 1583742664
    13. },
    14. {
    15. "content": "cock",
    16. "created_at": 1583742665
    17. },
    18. {
    19. "content": "bird",
    20. "created_at": 1583742666
    21. }
    22. ]
    23. }
  • 查询子文档内容为 XX,且 创建时间为 AA 的稿件(一个子文档必须同时满足多个条件)
    1. GET my_test2/_search
    2. {
    3. "query": {
    4. "nested": {
    5. "path": "questions",
    6. "query": {
    7. "bool": {
    8. "must": [
    9. {
    10. "term": {
    11. "questions.content": {
    12. "value": "dog"
    13. }
    14. }
    15. },
    16. {
    17. "term": {
    18. "questions.created_at": {
    19. "value": "1583742663"
    20. }
    21. }
    22. }
    23. ]
    24. }
    25. }
    26. }
    27. }
    28. }
    参考:
    es 6.3 官方文档