Elasticsearch: nested对象
在处理大量数据时,关系数据库存在很多问题。 无论是速度,高效处理,有效并行化,可扩展性还是成本,当数据量开始增长时,关系数据库都会失败。该关系数据库的另一个挑战是必须预先定义关系和模式。Elasticsearch也是一个NoSQL文档数据存储。 但是,尽管是一个NoSQL数据存储,Elasticsearch在一定程度上提供了很多帮助来管理关系数据。 它支持类似SQL的连接,并且在嵌套和相关的数据实体上工作得非常棒。
比如,对于一个像下面的blog形式的文档:

一个blog可能对应于很多的comments,或者一个员工对应于很多的经验。这种数据就是关系数据。使用Elasticsearch,您可以通过保留轻松地工作与不同实体的关联以及强大的全文搜索和分析。 Elasticsearch通过引入两种类型的文档关系模型使这成为可能:
- nested 关系: 在这种关系中,这种一对多的关系存在于同一个文档之中
- parent-child 关系:在这种关系中,它们存在于不同的文档之中。
这两种关系在同一个模式下工作,即一对多个的关系。一个root或parent可以有一个及多个子object。
如上图所示,在嵌套关系中,有一个根对象,它是我们拥有的主文档,它包含一个称为嵌套文档的子文档数组。 根对象内的文档嵌套级别没有限制。 例如,查看以下JSON以进行多级嵌套:
     {
         "location_id": "axdbyu",
         "location_name": "gurgaon",
         "company": [
           {
             "name": "honda",
             "modelName": [
               { "name": "honda cr-v", "price": "2 million" }
             ]
    }, {
             "name": "bmw",
             "modelName": [
               { "name": "BMW 3 Series", "price": "2 million"},
               { "name": "BMW 1 Series", "price": "3 million" }
             ]
      } ]
    }
下面,我们来做一个例子来展示一下为什么nested对象可以解决我们的问题。
Object数据类型
我们首先创建一个叫做developer的index,并输入如下的两个数据:
    POST developer/_doc/101
    {
      "name": "zhang san",
      "skills": [
        {
          "language": "ruby",
          "level": "expert"
        },
        {
          "language": "javascript",
          "level": "beginner"
        }
       ]
    }
    POST developer/_doc/102
    {
      "name": "li si",
      "skills": [
        {
          "language": "ruby",
          "level": "beginner"
        }
       ]
    }
上面显示是一对多的一个index。
Object Query
这个时候我们想搜一个skills: language是ruby,并且level是biginner的文档。我们可能想到的方法是:
    GET developer/_search
    {
      "query": {
        "bool": {
          "filter": [
            {
              "match": {
                "skills.language": "ruby"
              }
            },
            {
              "match": {
                "skills.level": "beginner"
              }
            }
          ]
        }
      }
    }
通过上面的搜寻,我们得到的结果是:
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 0.0,
        "hits" : [
          {
            "_index" : "developer",
            "_type" : "_doc",
            "_id" : "101",
            "_score" : 0.0,
            "_source" : {
              "name" : "zhang san",
              "skills" : [
                {
                  "language" : "ruby",
                  "level" : "expert"
                },
                {
                  "language" : "javascript",
                  "level" : "beginner"
                }
              ]
            }
          },
          {
            "_index" : "developer",
            "_type" : "_doc",
            "_id" : "102",
            "_score" : 0.0,
            "_source" : {
              "name" : "li si",
              "skills" : [
                {
                  "language" : "ruby",
                  "level" : "beginner"
                }
              ]
            }
          }
        ]
      }
    }
我们可以看到,我们得到两个结果。但是我们仔细查看一下发现得到的结果并不是我们想得到的。从我们的原意来说,我们想得到的是li si,因为只有li si这个人的language是ruby,并且他的level是biginner。zhang san这个文档,应该不在搜寻之列。这是为什么呢?
原来,langauge及level是skills的JSON内部数组项。当JSON对象被Lucene扁平化后,我们失去了language和level之间的对应关系。取而代之的是如下的这种关系:
    {
      "name": "zhang san",
      "skills.language" :["ruby", "javascript"],
      "skills.level": ["expert", "beginner"]
    }
如上所示,我们看到的是一个扁平化的数组。之前的那种language和level之间的那种对应关系已经不存在了。
Object aggregation
同样的问题也存在于aggregation中,比如我们想做一下的aggregation:
    GET developer/_search
    {
      "size": 0,
      "aggs": {
        "languages": {
          "terms": {
            "field": "skills.language.keyword"
          },
          "aggs": {
            "level": {
              "terms": {"field": "skills.level.keyword"}
            }
          }
        }
      }
    }
显示的结果是:
    {
      "took" : 2,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : null,
        "hits" : [ ]
      },
      "aggregations" : {
        "languages" : {
          "doc_count_error_upper_bound" : 0,
          "sum_other_doc_count" : 0,
          "buckets" : [
            {
              "key" : "ruby",
              "doc_count" : 2,
              "level" : {
                "doc_count_error_upper_bound" : 0,
                "sum_other_doc_count" : 0,
                "buckets" : [
                  {
                    "key" : "beginner",
                    "doc_count" : 2
                  },
                  {
                    "key" : "expert",
                    "doc_count" : 1
                  }
                ]
              }
            },
            {
              "key" : "javascript",
              "doc_count" : 1,
              "level" : {
                "doc_count_error_upper_bound" : 0,
                "sum_other_doc_count" : 0,
                "buckets" : [
                  {
                    "key" : "beginner",
                    "doc_count" : 1
                  },
                  {
                    "key" : "expert",
                    "doc_count" : 1
                  }
                ]
              }
            }
          ]
        }
      }
    }
显然,对于key javascript来说,它并没有expert对应的level,但是在我们的aggregation里显示出来了。这个结果显然是错误的。
nested 数据类型
nested数据类型能够让我们对object数组建立索引,并且分别进行查询。
如果需要维护数组中每个对象的关系,请使用nested数据类型
为了能够把我们的数据定义为nested,我们必须修改之前的索引mapping为:
    DELETE developer
    PUT developer
    {
      "mappings": {
        "properties": {
          "name": {
            "type": "text"
          },
          "skills": {
            "type": "nested",
            "properties": {
              "language": {
                "type": "keyword"
              },
              "level": {
                "type": "keyword"
              }
            }
          }
        }
      }
    }
经过这样的改造之后,重新把我们之前的数据输入到index里:
    POST developer/_doc/101
    {
      "name": "zhang san",
      "skills": [
        {
          "language": "ruby",
          "level": "expert"
        },
        {
          "language": "javascript",
          "level": "beginner"
        }
       ]
    }
    POST developer/_doc/102
    {
      "name": "li si",
      "skills": [
        {
          "language": "ruby",
          "level": "beginner"
        }
       ]
    }
针对101,在Lucence中的数据结构变为:
    {
      "name": "zhang san",
      {
        "skills.language": "ruby",
        "skills.level": "expert"
      },
      {
        "skills.language": "javascript",
        "skills.level", "beginner"
      }
    }
nested query
我们来重新做我们之前的搜索:
    GET developer/_search
    {
      "query": {
        "nested": {
          "path": "skills",
          "query": {
            "bool": {
              "filter": [
                {
                  "match": {
                    "skills.language": "ruby"
                  }
                },
                {
                  "match": {
                    "skills.level": "beginner"
                  }
                }
              ]
            }
          }
        }
      }
    }
注意上面的“nested”字段。显示的结果是:
    {
      "took" : 5,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 0.0,
        "hits" : [
          {
            "_index" : "developer",
            "_type" : "_doc",
            "_id" : "102",
            "_score" : 0.0,
            "_source" : {
              "name" : "li si",
              "skills" : [
                {
                  "language" : "ruby",
                  "level" : "beginner"
                }
              ]
            }
          }
        ]
      }
    }
显然,我们只得到了一个我们想要的结果。
nested aggregation
同样,我们可以对我们的index来做一个aggregation:
    GET developer/_search
    {
      "size": 0,
      "aggs": {
        "nested_skills": {
          "nested": {
            "path": "skills"
          },
          "aggs": {
            "languages": {
              "terms": {
                "field": "skills.language"
              },
              "aggs": {
                "levels": {
                  "terms": {
                    "field": "skills.level"
                  }
                }
              }
            }
          }
        }
      }
    }
显示的结果是:
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : null,
        "hits" : [ ]
      },
      "aggregations" : {
        "nested_skills" : {
          "doc_count" : 3,
          "languages" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "ruby",
                "doc_count" : 2,
                "levels" : {
                  "doc_count_error_upper_bound" : 0,
                  "sum_other_doc_count" : 0,
                  "buckets" : [
                    {
                      "key" : "beginner",
                      "doc_count" : 1
                    },
                    {
                      "key" : "expert",
                      "doc_count" : 1
                    }
                  ]
                }
              },
              {
                "key" : "javascript",
                "doc_count" : 1,
                "levels" : {
                  "doc_count_error_upper_bound" : 0,
                  "sum_other_doc_count" : 0,
                  "buckets" : [
                    {
                      "key" : "beginner",
                      "doc_count" : 1
                    }
                  ]
                }
              }
            ]
          }
        }
      }
    }
从上面显示的结果,可以看出来对于ruby来说,它分别对应于一个bigginer及一个expert。这个和我们之前的数据是一样的。对于javascript来说,它只有一个beginner的level。
Elasticsearch: nested对象的更多相关文章
- elasticsearch 嵌套对象之嵌套类型
		nested类型是一种特殊的对象object数据类型(specialised version of the object datatype ),允许对象数组彼此独立地进行索引和查询. 1. 对象数组如 ... 
- elasticSearch nested exist与missing查询
		elasticSearch nested查询,简单意义上,你可以理解为,它不会被索引,只是被暂时隐藏起来,而查询的时候,开关就是使用nested query/filter去查询 下面我有一个例子,是查 ... 
- 干货 | Elasticsearch Nested类型深入详解(转)
		https://blog.csdn.net/laoyang360/article/details/82950393 0.概要在Elasticsearch实战场景中,我们或多或少会遇到嵌套文档的组合形式 ... 
- 干货 | Elasticsearch Nested类型深入详解
		在Elasticsearch实战场景中,我们或多或少会遇到嵌套文档的组合形式,反映在ES中称为父子文档. 父子文档的实现,至少包含以下两种方式: 1)父子文档 父子文档在5.X版本中通过parent- ... 
- elasticsearch 嵌套对象使用Multi Match Query、query_string全文检索设置
		参考: https://www.elastic.co/guide/en/elasticsearch/reference/1.7/mapping-nested-type.html https://sta ... 
- elasticsearch嵌套对象的映射
		在es中,我们有时候可能需要映射,{ "field" : "xx" , "field01" : [] }这样格式的嵌套对象,默认情况下es会 ... 
- elasticsearch nested查询
		项目里可能会遇到多级嵌套的情况,实际上最多两级,三级及以上,我测试不通过. 一级索引时,我插入数据,会自动创建索引映射:然二级时,索引映射必须手动创建. 映射: PUT test999 { " ... 
- Elastic search中使用nested类型的内嵌对象
		在大数据的应用环境中,往往使用反范式设计来提高读写性能. 假设我们有个类似简书的系统,系统里有文章,用户也可以对文章进行赞赏.在关系型数据库中,如果按照数据库范式设计,需要两张表:一张文章表和一张赞赏 ... 
- 第三百六十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索的自动补全功能
		第三百六十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—用Django实现搜索的自动补全功能 elasticsearch(搜索引擎)提供了自动补全接口 官方说明:https://www.e ... 
随机推荐
- php 大数组 foreach 循环嵌套的性能优化
			前提:最近在做后台的时候,页面加载太慢,故第一时间想到的自然是优化SQL, 优化后sql查询速度从 2秒变成了零点几秒, 以为就这麽完事了,然并卵,加载竟然花费30秒! 这麽慢,然后在代码中分块记录它 ... 
- Alt+~,Alt+Tab
			Alt+~可以很方便的切换同一个软件中的内容. Alt+Tab可以很方便的切换不同软件之间的内容. 熟练使用,很方便. 
- PCL学习(五)如何在mesh模型上sample更多点及三维物体姿态估计
			---恢复内容开始--- 最近在做关于物体姿态估计的项目 基本思路就是 我们在估计物体的pose的时候,需要用分割得到的点云与模型库中的模型做匹配 1.通过基于RANSANC的SAC-IA将点云和模型 ... 
- Xamarin.Forms FlyoutIcon 不显示(not shown)
			升级了VS2019到16.4版本,然后默认创建了一个Xamarin Shell程序 结果运行后是这个样子 难道不应该是这个样子吗? 百了个度一晚上没解决,资料本身就少,再就是提示设置FlyoutIco ... 
- K8S从入门到放弃系列-(4)kubernetes集群之kubectl命令行工具部署
			摘要:随着版本的不断迭代,k8s为了集群安全,集群中趋向采用TLS+RBAC的安全配置方式,所以我们在部署过程中,所有组件都需要证书,并启用RBAC认证. 我们这里采用二进制安装,下载解压后,把对应组 ... 
- Centos7.0配置Hadoop2.7.0伪分布式
			一.ssh免密登录 1.命令ssh-keygen. overwrite输入y一路回车 2.将生成的密钥发送到本机 ssh-copy-id localhost中间会询问是否继续输入“yes” 3.测试免 ... 
- 去掉右键Open Folderas Intellij IDEA Project
			解决: WIN+R键打开运行,输入regedit 打开注册表 在地址栏输入: 计算机\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell 然 ... 
- 快速创建一个SpringBoot项目并整合Mybatis
			2019-09-15 一.Maven环境搭建 1.导入jar坐标 <project xmlns="http://maven.apache.org/POM/4.0.0" xml ... 
- vector简单常用用法
			Vector是什么? vector翻译为向量,从某种角度来说就是一个可以变长的数组,它会根据需要自动扩充数组的容量,除此之外其是一个STL中的模板类,其 还具有一些内部的方法. Vector的使用方法 ... 
- glib系列1 编译
			1 安装 meson glib 使用 meson进行构建,需要python3,首先下载meson, meson/meson.py 就是程序本身了,可以直接运行 git clone https://gi ... 
