内容目录

一个blog可能对应于很多的comments,或者一个员工对应于很多的经验。,
这种数据就是关系数据。

object数据类型

json文档本质上市分成的:文档可能包含内部对象,而内部对象又可能包含内部对象的本身;

file

  1. 外部文档也是一个json对象
  2. 它包含一个名为manager的内部对象
  3. 其中又包含一个名为name的内部对象

elasticsearch内部,该文档被索引为一个简单的键值对应列表,如下所示:

{
  "region":             "US",
  "manager.age":        30,
  "manager.name.first": "John",
  "manager.name.last":  "Smith"
}

接下来,我们首选创建一个叫做developer的索引,并输入如下的两个数据:

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"
    }
   ]
}

object query

这个时候我们想搜一个skill:languageruby,并且levelbeginner的文档。
我们可能想到的办法是:

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这个人的languageruby,并且他的levelbeginner
zhang san这个文档,应该不在搜寻之列。这是为什么呢?

原来,languagelevelskilljson内部数组项。
json对象被lucene扁平化后,我们失去了languagelevel之间的对应关系
取而代之的是如下的这种关系:

{
  "name": "zhang san",
  "skills.language" :["ruby", "javascript"],
  "skills.level": ["expert", "beginner"]
}

如上所示,我们看到的是一个扁平化的数组。
之前的那种languagelevel之间的对应关系已经不存在了。

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 query独立于其他对象的查询。
每个nested对象都被索引为一个单独的lucene文档。
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,在lucene中的数据结构变为:

{
  "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

同样,我们可以对索引来做一个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 来说,它只有一个 beginnerlevel

最后修改日期: 2024年8月23日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。