Elasticsearch (ES) 搜索引擎: 数据类型、动态映射、多类型(子字段)

原文链接:https://xiets.blog.csdn.net/article/details/132348634

版权声明:原创文章禁止转载

专栏目录:Elasticsearch 专栏(总目录)

ES 映射字段的 数据类型,官网文档参考:Field data types。

1. 基本数据类型

下面是 ES 常用的一些基本数据类型。

1.1 字符串

字符串 类型:

  • keyword:关键字类型。
  • text:文本类型。

    keyword 类型是不可切分的字符串类型,需要全匹配,用于字符串是否相等的比较。keyword 类型一般用于文档的过滤、排序和聚合。在实际场景用,keyword 一般用于描述 用户名、类型、用户ID、URL 等。

    text 类型是可进行分词分隔的字符串类型,支持部分匹配、模糊匹配。由于 text 是模糊匹配,所有支持匹配度打分。text 类型一般用于描述文章标题、文章内容等。

    官网相关链接:Keyword type family、Text type family

    使用示例:

    // 创建索引
    PUT /index01
    { "mappings": { "properties": { "name": { "type": "keyword"
                },
                "desc": { "type": "text"
                }
            }
        }
    }
    // 写入文档
    PUT /index01/_doc/001
    { "name": "用户名01",
        "desc": "这是一位大V用户。"
    }
    

    1.2 数值

    数值 类型:

    • long长整型。
    • integer:整型。
    • short:半长整形。
    • byte:字节类型。
    • float:浮点型。
    • double:双精度浮点型。
    • half_float:半精度浮点型。
    • scaled_float:可伸缩浮点型。
    • unsigned_long:无符号长整型。

      数值类型支持 相等、范围(大小)比较,也可用于对文档的过滤、排序和聚合。

      官网相关链接:Numeric field type

      使用示例:

      // 创建索引
      PUT /index02
      { "mappings": { "properties": { "age": { "type": "integer"
                  },
                  "salary": { "type": "double"
                  }
              }
          }
      }
      // 写入文档
      PUT /index02/_doc/001
      { "age": 30,
          "salary": 99999.99
      }
      

      1.3 布尔

      布尔类型:

      • boolean:布尔类型。

        布尔类型的值只有 true 和 false 两种情况的值。

        官网相关链接:Boolean field type

        使用示例:

        // 创建索引
        PUT /index03
        { "mappings": { "properties": { "is_male": { "type": "boolean"
                    }
                }
            }
        }
        // 写入文档
        PUT /index03/_doc/001
        { "is_male": true
        }
        PUT /index03/_doc/002
        { "is_male": false
        }
        

        1.4 日期

        日期类型:

        • date:日期类型。

          ES 中存储的日期类型是标准的 UTC 格式。date 类型值的字面表示可以是 格式化的日期时间格式 或 时间戳整数。

          date 类型的默认格式为:"strict_date_optional_time||epoch_millis"。其中 strict_date_optional_time 表示格式化日期时间字符串,例如:“yyyy-MM-dd”、“yyyy-MM-ddTHH:mm:ss”、“yyyy-MM-ddTHH:mm:ssZ”、“yyyyMMdd”、“yyyyMMddHHmmss” 等。epoch_millis 表示自纪元以来的毫秒数(时间戳毫秒数)。

          date 类型还可以在索引的 mapping 中设置 format 字段自定义格式。如果自定义格式,则后续写入只支持 format 中自定义的格式。

          日期类型支持 相等、范围(大小)比较。

          官网相关链接:Date field type

          使用示例:

          // 创建索引
          PUT /index04
          { "mappings": { "properties": { "create_time": { "type": "date"                      // 使用默认格式
                      },
                      "update_time": { "type": "date",
                          "format": "yyyy-MM-dd HH:mm:ss"     // 自定义格式
                      },
                      "delete_time": { "type": "date",
                          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"   // 可同时支持多种自定义格式
                      }
                  }
              }
          }
          // 写入文档
          PUT /index04/_doc/001
          { "create_time": "2023-07-10",
              "update_time": "2023-07-10 20:30:30"
          }
          PUT /index04/_doc/002
          { "create_time": "2015-01-01T12:10:30",
              "delete_time": "2023-01-01 00:00:00"
          }
          PUT /index04/_doc/003
          { "create_time": 1420070400001,
              "delete_time": 1420070400001
          }
          

          2. 复杂据类型

          2.1 数组

          ES 数组没有显式的定义方式,基本数据类型的普通字段就可以接收数组类型的值,只需要在写入文档时,把数据用中括号括起来即可。数组字段搜索时也和搜索普通字段一样。

          使用示例:

          // 创建索引
          PUT /book
          { "mappings": { "properties": { "tag": { // 声明一个关键字类型的 tag 字段
                          "type": "keyword"
                      }
                  }
              }
          }
          // 写入文档
          PUT /book/_doc/001
          { "tag": "编程语言"               // 写入字段值
          }
          PUT /book/_doc/002
          { "tag": ["人工智能", "编程语言"]     // 以数组的方式写入值
          }
          // 查看文档
          GET /book/_doc/001
          // 返回
          { "_index": "book",
              "_id": "001",
              "_version": 1,
              "_seq_no": 0,
              "_primary_term": 1,
              "found": true,
              "_source": { "tag": "编程语言"
              }
          }
          GET /book/_doc/002
          // 返回
          { "_index": "book",
              "_id": "002",
              "_version": 1,
              "_seq_no": 1,
              "_primary_term": 1,
              "found": true,
              "_source": { "tag": [
                      "人工智能",
                      "编程语言"
                  ]
              }
          }
          // 搜索文档
          POST /book/_search
          { "query": { "term": { "tag": { "value": "编程语言"
                      }
                  }
              }
          }
          // 返回
          { "took": 1,
              "timed_out": false,
              "_shards": { "total": 1,
                  "successful": 1,
                  "skipped": 0,
                  "failed": 0
              },
              "hits": { "total": { "value": 2,
                      "relation": "eq"
                  },
                  "max_score": 0.21110919,
                  "hits": [
                      { "_index": "book",
                          "_id": "001",
                          "_score": 0.21110919,
                          "_source": { "tag": "编程语言"
                          }
                      },
                      { "_index": "book",
                          "_id": "002",
                          "_score": 0.21110919,
                          "_source": { "tag": [
                                  "人工智能",
                                  "编程语言"
                              ]
                          }
                      }
                  ]
              }
          }
          

          2.2 对象

          对象类型也没有显示的定义方式,也没有指定的 type 类型值,只需要在定义字段的时候,继续给字段加属性即可。

          使用示例:

          // 创建索引
          PUT /topic
          { "mappings": { // 映射
                  "properties": { // 映射的属性
                      "title": { // 属性1: keyword类型的 title 字段
                          "type": "keyword"
                      },
                      "content": { // 属性2: text类型的 content 字段
                          "type": "text"
                      },
                      "statistics": { // 属性3: 对象类型的 statistics 字段, 对象有两个属性
                          "properties": { // 对象的属性
                              "comment_count": { "type": "integer"
                              },
                              "like_count": { "type": "integer"
                              }
                          }
                      }
                  }
              }
          }
          // 写入文档
          PUT /topic/_doc/001
          { "title": "主题标题001",
              "content": "主题内容001",
              "statistics": { "like_count": 100,
                  "comment_count": 200
              }
          }
          // 搜索文档
          POST /topic/_search
          { "query": { "range": { "statistics.like_count": { // 使用 对象.属性 的方式引用属性值
                          "gte": 100,
                          "lte": 500
                      }
                  }
              }
          }
          // 返回
          { "took": 1,
              "timed_out": false,
              "_shards": { "total": 1,
                  "successful": 1,
                  "skipped": 0,
                  "failed": 0
              },
              "hits": { "total": { "value": 1,
                      "relation": "eq"
                  },
                  "max_score": 1,
                  "hits": [
                      { "_index": "topic",
                          "_id": "001",
                          "_score": 1,
                          "_source": { "title": "主题标题001",
                              "content": "主题内容001",
                              "statistics": { "like_count": 100,
                                  "comment_count": 200
                              }
                          }
                      }
                  ]
              }
          }
          

          2.3 地理坐标

          ES 支持地理位置坐标和形状区域数据类型的存储和搜索。

          其中地理坐标存储了经纬度,可以根据位置距离搜索数据。geo_point 类型表示地理坐标类型。

          官网相关链接:

          • Geopoint field type:地理坐标类型。
          • Geoshape field type:地理形状类型。

            geo_point 使用示例:

            // 创建索引
            PUT /hotel
            { "mappings": { "properties": { "title": { "type": "keyword"
                        },
                        "location": { // 地理坐标类型的属性
                            "type": "geo_point"
                        }
                    }
                }
            }
            // 写入文档
            PUT /hotel/_doc/001
            { "title": "AAA大酒店",
                "location": { "lat": 22.5369848,          // 纬度, 正数表示北纬, 负数表示南纬
                    "lon": 113.9271766          // 经度, 正数表示东经, 负数表示西经
                }
            }
            PUT /hotel/_doc/002
            { "title": "BBB大酒店",
                "location": [113.9174415, 22.5352364]       // 以数组的形式写入, 格式: [经度, 纬度]
            }
            PUT /hotel/_doc/003
            { "title": "CCC大酒店",
                "location": "22.5412263,114.0613099"        // 以字符串的形式写入, 格式: "纬度,经度"
            }
            // 根据距离搜索
            GET /hotel/_search
            { "query": { "geo_distance": { // 根据距离搜索
                        "location": { // 中心点 (指定位置坐标), "location" 是 geo_point 类型的字段名称
                            "lat": 22.5281343,
                            "lon": 113.9321684
                        },
                        "distance": "5km"           // 搜索距离中心点 5km 范围内的酒店
                    }
                }
            }
            // 返回
            { "took": 3,
                "timed_out": false,
                "_shards": { "total": 1,
                    "successful": 1,
                    "skipped": 0,
                    "failed": 0
                },
                "hits": { "total": { "value": 2,
                        "relation": "eq"
                    },
                    "max_score": 1,
                    "hits": [
                        { "_index": "hotel",
                            "_id": "001",
                            "_score": 1,
                            "_source": { "title": "AAA大酒店",
                                "location": { "lat": 22.5369848,
                                    "lon": 113.9271766
                                }
                            }
                        },
                        { "_index": "hotel",
                            "_id": "002",
                            "_score": 1,
                            "_source": { "title": "BBB大酒店",
                                "location": [
                                    113.9174415,
                                    22.5352364
                                ]
                            }
                        }
                    ]
                }
            }
            

            3. 动态映射

            ES 向索引写入文档时,如果相关字段没有在映射中定义,则 ES 会根据写入的数据结构和类型自动扩展映射,这种机制称为动态映射。

            动态映射时,JSON 数据类型和索引字段类型的对应关系:

            JSON 类型ES 索引字段类型
            null不增加字段
            true 或 falseboolean
            intergerlong
            floatfloat
            object对象类型
            array根据数组中的第一个非空值决定
            stringdate、double、long、text 都有可能,根据数据格式进行判断

            通常在创建索引时,最好先把映射字段的数据类型先定义好,因为 ES 的动态映射生成的字段类型可能会与用户的预期有差别。

            4. 多类型(子字段)

            针对同一个字段,有时可能需要多种数据类型。比如搜索酒店名称时,有时希望分词模糊搜索,有时又希望全匹配搜索,这时就可以把名称定义为 text 类型,然后再增加多一个 keyword 类型的子字段。

            使用示例:

            // 创建索引
            PUT /hotel
            { "mappings": { "properties": { "name": { // 字段, 名称为 "name", 类型为 text
                            "type": "text",
                            "fields": { // 定义子字段
                                "keyword_name": { // 子字段, 名称为 "keyword_name", 类型为 keyword
                                    "type": "keyword"
                                }
                            }
                        }
                    }
                }
            }
            // 写入文档
            PUT /hotel/_doc/001
            { "name": "AAA大酒店"        // 只需要写入主字段, 会自动应用到子字段
            }
            PUT /hotel/_doc/002
            { "name": "BBB大酒店"
            }
            // 搜索文档
            POST /hotel/_search
            { "query": { "match": { // 分词模糊搜索
                        "name": "酒店"
                    }
                }
            }
            POST /hotel/_search
            { "query": { "term": { // 使用子字段全匹配搜索
                        "name.keyword_name": "AAA大酒店"
                    }
                }
            }