Elasticsearch 复杂类型 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 定义子字段,您可以更细致地控制字段的处理和索引行为,以满足不同的查询需求,并在排序、聚合、高亮显示和多字段查询等方面提供更好的功能和性能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Elasticsearch中的nested是一种数据类型,它允许在一个文档中嵌套另一个文档。这种嵌套结构可以用于处理复杂的数据结构,例如嵌套的数组或对象。使用nested类型可以方便地进行嵌套文档的查询和聚合操作。但是,由于nested类型需要额外的存储和计算资源,因此在使用时需要谨慎考虑性能问题。 ### 回答2: elasticsearch中的嵌套(nested)可以用于存储和查询具有多层级数据结构的文档。举一个例子,假设我们要存储一个具有以下结构的文档: ```json { "title": "book", "authors": [ { "name": "John Smith", "age": 25 }, { "name": "Jane Doe", "age": 30 } ] } ``` 在没有嵌套(nested)的情况下,我们可以使用对象(object)和嵌套数组(nested array)来实现此结构,但这样做可能会导致查询结果不准确,因为嵌套数组(nested array)会将子文档的属性(apparatus)拆分为单独的文档进行索引。 使用嵌套对象(nested object)可以避免这种情况。它是一个将所有子文档作为单独索引文档的新文档,同时它也是独立于父文档的索引文档,并且其父文档中的任何查询都不会影响其子文档。 以下是如何在elasticsearch中创建嵌套字段的示例映射(mapping): ```json PUT /books { "mappings": { "properties": { "title": { "type": "text" }, "authors": { "type": "nested", "properties": { "name": { "type": "text" }, "age": { "type": "integer" } } } } } } ``` 在查询中,我们可以使用嵌套查询(nested query)来查询特定子文档属性: ```json GET /books/_search { "query": { "nested": { "path": "authors", "query": { "bool": { "must": [ { "match": { "authors.name": "John Smith" } }, { "range": { "authors.age": { "gte": 25, "lte": 30 } } } ] } }, "inner_hits": {} } } } ``` 此查询将返回包含名称为"John Smith"和年龄在25到30之间的作者的所有书籍,并且它们的作者子文档可以使用内部内部(hit)查询通过“inner_hits”返回。 ### 回答3: Elasticsearch nestedElasticsearch中的一种数据类型,用于处理嵌套对象的索引和查询。这种数据类型允许将一个对象嵌套在另一个对象中,形成一个更复杂的数据结构。在以往的Elasticsearch版本中,如果我们需要处理这样的嵌套数据结构,通常会使用parent/child的数据结构来处理。但是在大数据集的情况下,使用parent/child会导致性能问题,因为每次查询都需要合并来自多个分片的结果集。 相比于parent/child的数据结构,Elasticsearch nested允许我们将所有的相关数据存储在同一个文档中,并利用Elasticsearch的分布式查询引擎同时查询所有的文档。这就大大提高了查询性能。同时,nested支持多级嵌套对象的索引和查询,可以轻松地处理非常复杂的数据结构。 在创建映射时,需要将嵌套对象定义为nested类型,如下所示: ``` PUT /nested_index { "mappings": { "properties": { "name": {"type": "text"}, "age": {"type": "integer"}, "books": {"type": "nested", "properties": { "title": {"type": "text"}, "author": {"type": "text"} } } } } } ``` 在上述映射中,我们定义了一个nested类型的books属性,其中包含title和author两个嵌套属性。这样,我们就可以将books属性作为一个完整的对象存储在Elasticsearch中,并能够进行复杂的查询操作。例如,我们可以查询所有书中作者为"John"的书: ``` POST /nested_index/_search { "query": { "nested": { "path": "books", "query": { "match": { "books.author": "John" } } } } } ``` 通过将nested属性定义为nested类型,我们能够轻松处理复杂数据结构,并提高查询性能。但需要注意的是,嵌套对象需要占用更多的存储空间,因此需要合理使用。同时,在进行复杂的查询操作时,需要对查询进行优化,以避免影响性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值