Elasticsearch 复杂类型 Nested

举报
赵不辞 发表于 2023/07/09 18:59:56 2023/07/09
【摘要】 复杂类型建模到底是个什么东东?建模解决什么问题?复杂类型的的检索问题 Nested主要解决复杂类型无法搜索的问题nested属于object类型的一种,是Elasticsearch中用于复杂类型对象数组的索引操作。Elasticsearch没有内部对象的概念,因此,ES在存储复杂类型的时候会把对象的复杂层次结果扁平化为一个键值对列表。 扁平化处理带来的问题正常如果我们不使用Nested对象时...

复杂类型

建模到底是个什么东东?

建模解决什么问题?

复杂类型的的检索问题

Nested

主要解决复杂类型无法搜索的问题

nested属于object类型的一种,是Elasticsearch中用于复杂类型对象数组的索引操作。Elasticsearch没有内部对象的概念,因此,ES在存储复杂类型的时候会把对象的复杂层次结果扁平化为一个键值对列表。

扁平化处理带来的问题

正常如果我们不使用Nested对象时es会如下处理这个结构:

PUT my-index-000001/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}
上面的文档被创建之后,user数组中的每个json对象会以下面的形式存储
{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

如我们上面定义的user对象它包含first和last属性,我们检索时可能是想要first=xx and last=xxx,

如果扁平化处理就会失去这个逻辑关系

它会去找"user.first" : [ "alice", "john" ],

然后OR上"user.last" : [ "smith", "white" ]

创建Nested

PUT <index_name>
{
  "mappings": {
    "properties": {
      "<nested_field_name>": {
        "type": "nested"
      }
    }
  }
}

查询nested

GET <index_name>/_search
{
  "query": {
    "nested": {
      "path": "<nested_field_name>",
      "query": {
        ...
      }
    }
  }
}

Nested可选参数

path:nested对象的查询深度.通俗点来讲就是你不断的点出来那个属性位置

score_mode:评分计算方式,这指的是多个检索字段都有评分的话怎么合成一个总评分的规则

  • avg (默认):使用所有匹配的子对象的平均相关性得分。
  • max:使用所有匹配的子对象中的最高相关性得分。
  • min:使用所有匹配的子对象中最低的相关性得分。
  • none:不要使用匹配的子对象的相关性分数。该查询为父文档分配得分为0。
  • sum:将所有匹配的子对象的相关性得分相加。

Nested测试

测试: 非nested索引

创建

DELETE test_idx_nested
PUT test_idx_nested
{
  "mappings": {
    //设置mapping的属性(字段)
    "properties":{
      "goods_list":{
        //如果它是一个复杂类型还有其他字段同样通过properteis设置
        "properties":{
          //属性1名称
          "name":{
            //定义属性必须有类型
            "type":"text",
            //根据需要是否需要定义 analyzer
            "analyzer":"ik_max_word",
            //是否定义子字段,一直不明白子字段的用户
            //定义字段的keyword,用子字段形式定义
            "fields":{
              "name_sub_field_keyword":{
                "type":"keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
} 

插入测试数据

PUT /test_idx_nested/_doc/1
{
  "order_name": "小米10 Pro订单",
  "desc": "shouji zhong de zhandouji",
  "goods_count": 3,
  "total_price": 12699,
  "goods_list": [
    {
      "name": "小米10 PRO MAX 5G",
      "price": 4999
    },
    {
      "name": "钢化膜",
      "price": 19
    },
    {
      "name": "手机壳",
      "price": 1999
    }
  ]
}
PUT /test_idx_nested/_doc/2
{
  "order_name": "扫地机器人订单",
  "desc": "shouji zhong de zhandouji",
  "goods_count": 2,
  "total_price": 12699,
  "goods_list": [
    {
      "name": "小米扫地机器人儿",
      "price": 1999
    },
    {
      "name": "洗碗机",
      "price": 4999
    }
  ]
}

查询测试

GET test_idx_nested/_search
{
  "query": {
    "bool": {
      //must必须都满足这个查询预期应该没有结果,但是实际查询出来了
      //可以猜测为什么?
      "must": [
        {
          "match": {
            "goods_list.name": "洗碗机"
          }
        },
        {
          "match": {
            "goods_list.price": "1999"
          }
        }
      ]
    }
  }
}

预期查询结果

一般不应该查出来的却查出来了就是因为分词
当然也可能是因为扁平化

如何验证是否被分词了?

GET _analyze
{
  "text": ["小米10 Pro订单"],
  "analyzer": "ik_max_word"
}

测试: Nested索引

创建

PUT test_idx_nested
{
  "mappings": {
    "properties": {
      "goods_list": {
        "type": "nested",
        "properties": {
          "name":{
            "type":"text",
            "analyzer":"ik_max_word",
            "fields":{
              "name_sub_field_keyword":{
                "type":"keyword",
                "ignore_above": 256
              }
            }
          }
          ,"price":{
            "type":"text",
            "analyzer":"ik_max_word"
          }
        }
      }
    }
  }
}

查询嵌套结果

GET test_idx_nested/_search?pretty=true
{
  "query": {
    //指定是nested类型的数据的查询
    "nested": {
      //这个类型必须指定path就是表示从哪个field开始检索
      "path": "goods_list",
      "query": {
        //内部还得指定查询,这里使用bool,到这里就跟正常查询一样了
        "bool": {
          "must": [
            //第一个条件
            {
              "match": {
                "goods_list.name": "洗碗机"
              }
            }
            //第二个条件,如果第二个条件不符合是查不出结果的 
            ,{
              "match": {
                "goods_list.price": "4999"
              }
            }
          ]
        }
      }
    }
  }
}

多重嵌套例子

创建

PUT /test_multi_nested_area
{
  "mappings": {
    "properties": {
      "province": {
        "type": "nested",
        "properties": {
          "name": {
            "type": "text",
            "analyzer": "ik_max_word"
          },
          "cities": {
            "type": "nested",
            "properties": {
              "name": {
                "type": "text",
                "analyzer": "ik_max_word"
              },
              "district": {
                "type": "nested",
                "properties": {
                  "name": {
                    "type": "text",
                    "analyzer": "ik_max_word"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

插入测试数据

PUT /test_multi_nested_area/_doc/1
{
  "province": {
    "name": "北京",
    "cities": [
      {
        "name": "北京市",
        "district": [
          {"name":"丰台区"},
          {"name":"海淀区"},
          {"name":"朝阳区"},
          {"name":"东城区"},
          {"name":"西城区"},
          {"name":"昌平区"}
          ]
      }
    ]
  }
}
PUT /test_multi_nested_area/_doc/2
{
  "province": {
    "name": "河南省",
    "cities": [
      {
        "name": "郑州市",
        "district": [
          {
            "name": "金水区"
          },
          {
            "name": "高新区"
          },
          {
            "name": "郑东新区"
          },
          {
            "name": "二七区"
          },
          {
            "name": "中原区"
          },
          {
            "name": "惠济区"
          }
        ]
      },
      {
        "name": "鹤壁市",
        "district": [
          {
            "name": "山城区"
          },
          {
            "name": "淇滨区"
          },
          {
            "name": "鹤山区"
          },
          {
            "name": "朝歌"
          },
          {
            "name": "浚县"
          }
        ]
      }
    ]
  }
}
PUT /test_multi_nested_area/_doc/3
{
  "province": {
    "name": "台湾省",
    "cities": [
      {
        "name": "台北市",
        "district": [
          {
            "name": "中正区"
          },
          {
            "name": "大同区"
          },
          {
            "name": "中山区"
          },
          {
            "name": "万华区"
          },
          {
            "name": "信义区"
          },
          {
            "name": "松山区"
          }
        ]
      },
      {
        "name": "高雄",
        "district": [
          {
            "name": "小港区"
          },
          {
            "name": "鼓山区"
          },
          {
            "name": "三民区"
          }
        ]
      }
    ]
  }
}

查询

city为包含北京市或者淇滨区的[省份]

GET /test_multi_ nested_area/_search
{
  "query": {
    "nested": {
      "path": "province",
      "query": {
        "nested": {
          "path": "province.cities",
          "query": {
            "bool": {
              "should": [
                {
                  "match": {
                    "province.cities.name": "北京市"
                  }
                },
                {
                  "nested": {
                    "path": "province.cities.district",
                    "query": {
                      "bool": {
                        "must": [
                          {
                            "match": {
                              "province.cities.district.name": "淇滨区"
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}

fields设计目的

在 Elasticsearch 中,fields 是用于定义字段的子字段(sub-field)的部分。它的作用是为同一个字段提供不同的处理和索引选项。

常见的使用场景和解决的问题包括:

  1. 排序:使用子字段定义一个字段的关键字(keyword)类型,可以用于排序操作。关键字类型是不分析的,它存储原始的、未经分析的文本,并且可以精确匹配和排序。这对于需要对文本字段进行精确排序的场景非常有用,例如按字母顺序对名称进行排序。

  2. 聚合:使用子字段定义一个字段的关键字类型,可以在聚合操作中使用。聚合是 Elasticsearch 中强大的数据分析工具,它允许根据文档的字段值进行分组、计数、求和等操作。关键字类型可以在聚合操作中提供准确的、无需分析的文本匹配。

  3. 高亮显示:使用子字段定义一个字段的关键字类型,可以在搜索结果中进行高亮显示。高亮显示是一种突出显示搜索结果中匹配的文本的方式,它可以提高用户体验和搜索结果的可读性。关键字类型可以用于精确匹配并保留原始文本,从而在高亮显示中提供准确的结果。

  4. 多字段查询:使用子字段定义一个字段的不同类型,可以根据不同的查询需求进行优化。例如,可以为一个字段定义一个文本类型子字段和一个关键字类型子字段,分别用于全文搜索和精确匹配。这样可以根据查询类型选择合适的子字段进行查询,并获得更好的搜索结果。

通过使用 fields 定义子字段,您可以更细致地控制字段的处理和索引行为,以满足不同的查询需求,并在排序、聚合、高亮显示和多字段查询等方面提供更好的功能和性能。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。