当前位置:   article > 正文

Elasticsearch处理数据关联关系_elasticsearch 反范式的关联能力

elasticsearch 反范式的关联能力

关系型数据库的范式化设计:范式化设计(Normalization)的主要目的是减少不必要的更新,但是一个完全范式化设计的数据会经常面临查询缓慢的问题(数据库越范式化,需要Join的表就越多)

反范式化设计(Denormalization):数据扁平,不使用关联关系,而是在文档中保存冗余的数据拷贝

  • 优点:无需处理Join操作,数据读取性能好(Elasticsearch通过压缩_source字段,减少磁盘的开销)
  • 缺点:不适合在数据频繁修改的场景

关系型数据库一般会考虑Normalize数据,在Elasticsearch,往往考虑Denormalize数据(Denormalize的好处:读的速度快/无需表连接/无需行锁)

Elasticsearch并不擅长处理关联关系,一般采取以下四种方式处理

  • 对象类型
  • 嵌套对象(Nested Object)
  • 父子关联关系(Parent/Child)
  • 应用端关联

对比

Nested ObjectParent/Child
优点文档存储在一起,读取性能高父子文档可以独立更新
缺点更新嵌套子文档时,需要更新整个文档需要额外的内存维护关系,读取性能相对差

对象类型

案例一:文章和作者的信息(1:1关系)

DELETE articles
#设置articles的mappings信息
PUT /articles  
{  
  "mappings": {  
    "properties": {  
      "content": {  
        "type": "text"  
      },  
      "time": {  
        "type": "date"  
      },  
      "author": {  
        "properties": {
          "userid": {  
            "type": "long"  
          },  
          "username": {  
            "type": "keyword"  
          }  
        }  
      }  
    }  
  }  
} 
#插入一条测试数据
PUT articles/_doc/1  
{  
  "content":"Elasticsearch Helloworld!",  
  "time":"2020-01-01T00:00:00",  
  "author":{  
    "userid":1001,  
    "username":"liu"
  }  
} 
#查询
POST articles/_search  
{  
  "query": {  
    "bool": {  
      "must": [  
        {"match": {  
          "content": "Elasticsearch"  
        }},  
        {"match": {  
          "author.username": "liu"  
        }}  
      ]  
    }  
  }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

案例二:文章和作者的信息(1:n关系)(有问题!

DELETE articles
#设置articles的mappings信息
PUT /articles  
{  
  "mappings": {  
    "properties": {  
      "content": {  
        "type": "text"  
      },  
      "time": {  
        "type": "date"  
      },  
      "author": {  
        "properties": {
          "userid": {  
            "type": "long"  
          },  
          "username": {  
            "type": "keyword"  
          }  
        }  
      }  
    }  
  }  
} 
POST articles/_search
#插入一条测试数据
PUT articles/_doc/1  
{  
  "content":"Elasticsearch Helloworld!",  
  "time":"2020-01-01T00:00:00",  
  "author":[{  
    "userid":1001,  
    "username":"liu"
  },{
    "userid":1002,  
    "username":"jia"
  }]
} 
#查询(这样也能查到!为什么出现这种结果呢?)
POST articles/_search  
{  
  "query": {  
    "bool": {  
      "must": [  
        {"match": {  
          "author.userid": "1001"  
        }},  
        {"match": {  
          "author.username": "jia"  
        }}  
      ]  
    }  
  }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

当使用对象保存有数组的文档时,我们发现会查询到不需要的结果,原因是什么呢?

存储时,内部对象的边界并没有考虑在内,JSON格式被处理成扁平式键值对的结构,当对多个字段进行查询时,导致了意外的搜索结果

"content":"Elasticsearch Helloworld!"
"time":"2020-01-01T00:00:00"
"author.userid":["1001","1002"]
"author.username":["liu","jia"]
  • 1
  • 2
  • 3
  • 4

使用嵌套对象(Nested Object)可以解决这个问题

嵌套对象

允许对象数组中的对象被独立索引,使用Nested和properties关键字将所有author索引到多个分隔的文档 ,在内部,Nested文档会被保存在两个Lucene文档中,在查询时做Join处理

案例一:文章和作者的信息(1:n关系)

DELETE articles
#设置articles的mappings信息
PUT /articles  
{  
  "mappings": {  
    "properties": {  
      "content": {  
        "type": "text"  
      },  
      "time": {  
        "type": "date"  
      },  
      "author": {  
        "type": "nested", 
        "properties": {
          "userid": {  
            "type": "long"  
          },  
          "username": {  
            "type": "keyword"  
          }  
        }  
      }  
    }  
  }  
} 
POST articles/_search
#插入一条测试数据
PUT articles/_doc/1  
{  
  "content":"Elasticsearch Helloworld!",  
  "time":"2020-01-01T00:00:00",  
  "author":[{  
    "userid":1001,  
    "username":"liu"
  },{
    "userid":1002,  
    "username":"jia"
  }]
} 
#查询(这样也能查到!为什么出现这种结果呢?)
POST articles/_search  
{  
  "query": {  
    "bool": {  
      "must": [  
        {"nested": {
          "path": "author",
          "query": {  
            "bool": {  
              "must": [  
                {"match": {  
                  "author.userid": "1001"  
                }},  
                {"match": {  
                  "author.username": "jia"  
                }}  
              ]  
            }  
          }
        }}
      ]  
    }  
  }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

父子关联关系

对象和Nested对象都存在一定的局限性,每次更新需要重新索引整个对象,Elasticsearch提供了类似关系型数据库中Join的实现,可以通过维护Parent/Child的关系,从而分离两个对象,父文档和子文档是两个独立的文档,更新父文档无需重新索引子文档,子文档被添加,更新或删除也不会影响到父文档和其他的子文档

案例:文章和作者的信息(1:n关系)

DELETE articles
#设置articles的mappings信息
PUT /articles  
{  
  "mappings": {  
    "properties": {  
      "article_author_relation": {  
        "type": "join",  
        "relations": {  
          "article": "author"  
        }
      },
      "content": {  
        "type": "text"  
      },  
      "time": {  
        "type": "date"  
      }
    }  
  }  
} 
#索引父文档
PUT articles/_doc/article1
{  
  "article_author_relation":{
    "name":"article"
  },
  "content":"Elasticsearch Helloworld!",  
  "time":"2020-01-01T00:00:00"
} 
#索引子文档
PUT articles/_doc/author1?routing=article1
{  
  "article_author_relation":{
    "name":"author",
    "parent":"article1"
  },
  "userid":"1001",  
  "username":"jia"
} 
PUT articles/_doc/author2?routing=article1
{  
  "article_author_relation":{
    "name":"author",
    "parent":"article1"
  },
  "userid":"1002",  
  "username":"liu"
} 
GET articles/_doc/article1
POST articles/_search
#根据parent_id父文档id查询子文档
POST articles/_search  
{  
  "query": {
    "parent_id":{
      "type":"author",
      "id":"article1"
    }
  }  
}
#has_child返回父文档
POST articles/_search  
{  
  "query": {
    "has_child":{
      "type":"author",
      "query": {
        "match": {
          "username": "liu"
        }
      }
    }
  }  
}
#has_parent返回子文档
POST articles/_search  
{  
  "query": {
    "has_parent":{
      "parent_type":"article",
      "query": {
        "match": {
          "content": "elasticsearch"
        }
      }
    }
  }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/1007912
推荐阅读
相关标签
  

闽ICP备14008679号