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。

原文地址:https://www.cnblogs.com/sanduzxcvbnm/p/12085019.html

时间: 2024-10-12 01:33:17

Elasticsearch: nested对象的相关文章

Elasticsearch之Nested Aggregation

(这是一个小系列:请戳:Elasticsearch之Nested(嵌套)系列,查看其他nested相关文章) In the same way as we need to use the special nested query to gain access to nested objects at search time, the dedicated nested aggregation allows us to aggregate fields in nested objects: 与在检索时

Elasticsearch之Nested Object

Given the fact that creating, deleting, and updating a single document in Elasticsearch is atomic, it makes sense to store closely related entities within the same document. 考虑到在ES里面建立,删除和更新一个单一文本是原子性的,那么将相关实体保存在同一个文本里面是有意义的. PUT /my_index/blogpost/1

Elastic search中使用nested类型的内嵌对象

在大数据的应用环境中,往往使用反范式设计来提高读写性能. 假设我们有个类似简书的系统,系统里有文章,用户也可以对文章进行赞赏.在关系型数据库中,如果按照数据库范式设计,需要两张表:一张文章表和一张赞赏历史记录表,赞赏历史记录表包括了赞赏者姓名和赞赏金额. 在Elastic search中,由于都是json格式存储,则可以在一个index存储系统中的文章及其赞赏记录,这种情况下需要在elastic search中使用nested类型的内嵌对象.因为如果使用数组或者object对象的话,赞赏者姓名和

四十七 Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索的自动补全功能

elasticsearch(搜索引擎)提供了自动补全接口 官方说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-completion.html 1.创建搜索自动补全字段suggest 自动补全需要用到一个字段名称为suggest类型为Completion类型的一个字段 所以我们需要用将前面的elasticsearch-dsl操作elasticsearch(搜索引擎)增加sugg

Elasticsearch常用操作:映射篇

[TOC] 其实就是es的字段类型是由es来做自动检测还是由我们自己来指定,因此会分为动态映射和静态映射. 1 动态映射 1.1 映射规则 JSON格式的数据 自动推测的字段类型 null 没有字段被添加 true or false boolean类型 浮点类型数字 float类型 数字 long类型 JSON对象 object类型 数组 由数组中第一个非空值决定 string 有可能是date类型(开启日期检测).double或long类型.text类型.keyword类型 1.2 日期检测

ElasticSearch学习文档2018.11

1       Elasticsearch安装 1.1    ES6.0版本安装head插件 1.1 下载head插件 下载地址:https://github.com/mobz/elasticsearch-head:点击clone or download按钮 1.2  安装node.js 下载地址:https://nodejs.org/en/download/ 1.3  安装grunt 运行head需要借助grunt命令 ,但是安装grunt需要借助npm 进入nodejs安装根目录,输入以下命

Elasticsearch入门CRUD(新增、查询、修改、删除)

IT交流互助QQ群:79190545    (Sql Server / Java / Asp.Net / C# / JS / Jquery / Oracle / My Sql / ) -- 欢迎大家进来 聊天,交流 !!.(定期清理 不说话.潜水太深的同学) 1. 项目中引用 Elasticsearch.Net         Elasticsearch 其他版本可在: http://www.nuget.org/  找到对应的项目以源码 ! 本文以 2.4.1版本为例 首先我们打开项目,点击-工

elasticsearch之入门hello(java)一

1.书写pom.xml文件 <dependencies> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>com.alibab

Python分布式爬虫必学框架Scrapy打造搜索引擎 学习教程

Python分布式爬虫打造搜索引擎Scrapy精讲—用Django实现搜索的自动补全功能 elasticsearch(搜索引擎)提供了自动补全接口 1.创建搜索自动补全字段suggest 自动补全需要用到一个字段名称为suggest类型为Completion类型的一个字段 所以我们需要用将前面的elasticsearch-dsl操作elasticsearch(搜索引擎)增加suggest类型为Completion 注意:因为elasticsearch-dsl源码问题,设置字段为Completio