文档存储Elasticsearch系列--1 ES介绍

前言:Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,支持结构化文档数据的分布式存储,并提供准实时的查询,全文检索,数据聚合;

1 为什么要使用ES:
ES 本身存在哪些特性使得我们放弃传统关系型数据库,ES的特点:
(1)ES支持PB级别(100万G) 数据的查询,并且检索效率很高;
(2)提供按照文档的相关性评分的全文检索;
(3)支持分布式文档的存储,动态伸缩容;
(4)API形式进行交互使用简单,使用json进行数据扁平化存储;
(5)提供多种方式的数据聚合;

2 了解一些概念:
在学习ES 之前我们需要了解一些概念以便更好的使用
2.1 ES 中的索引:
类比mysql ; 一个索引等价于 一个库;一个 索引 应该是因共同的特性被分组到一起的文档集合。 例如,你可能存储所有的产品在索引 products 中,而存储所有销售的交易到索引 sales 中。 虽然也允许存储不相关的数据到一个索引中,但这通常看作是一个反模式的做法,一个索引名,这个名字必须小写,不能以下划线开头,不能包含逗号。我们用 website 作为索引名举例。

2.2 索引的类型(新版本已经去除了类型):
类比mysql ; 一个类型等价于一个具体的表;数据可能在索引中只是松散的组合在一起,但是通常明确定义一些数据中的子分区是很有用的。 例如,所有的产品都放在一个索引中,但是你有许多不同的产品类别,比如 “electronics” 、 “kitchen” 和 “lawn-care”。一个 _type 命名可以是大写或者小写,但是不能以下划线或者句号开头,不应该包含逗号, 并且长度限制为256个字符. 我们使用 blog 作为类型名举例。

2.3 索引中存储的文档原数据:
一个个文档以JSON 扁平化的结果存入到ES中,当存入了一个文档如何知道该文档的位置;
_index :文档在哪个索引存放
_type:文档在索引下的哪个类型
_id:文档在索引中的唯一标识,类似与Mysql 中表的唯一主键id;

3 ES 中数据类型:
类似与Mysql 表中的每个字段都对应一个数据类型,那么在ES 中是否也有数据类型,如果存在它都支持哪些类型;ex:存储一个日期字符串 “2023-01-01” ,ES 需要知道它是一个日期而不仅仅是个字符串,显然ES中是有数据类型的:
3.1 Elasticsearch 支持如下简单域类型:

  • 字符串: string
  • 整数 : byte, short, integer, long
  • 浮点数: float, double
  • 布尔型: boolean
  • 日期: date
    当你索引一个包含新域的文档—​之前未曾出现-- Elasticsearch 会使用 动态映射 ,通过JSON中基本数据类型,尝试猜测域类型,使用如下规则:
    文档存储Elasticsearch系列--1 ES介绍_第1张图片
    注意:
    (1)对于字符串类型的数据: string 类型域会被认为包含全文,默认会被分析(默认进行分词)。就是说,它们的值在索引前,会通过一个分析器,针对于这个域的查询在搜索前也会经过一个分析器,搜索的关键词可以被拆分成不同的此条,从而可以用于文档相关度的查询。
    (2)其他简单类型(例如 long , double , date 等)不会被分析,对于改类型字段的查询,只会用于精确查询;
    (3)虽然ES 可以在存入文档时尝试猜测数据类型,但是我们最好在创建索引时就手动为其设置,避免因为数据类型造成数据无法存入问题;
    创建索引时手动设置类型:
PUT /my_index0
{
  "mappings": {
    "properties": {
      "age": {
        "type": "integer"
      },
      "birstTime": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      },
      "email": {
        "type": "keyword"
      },
      "sex": {
        "type": "keyword"
      },
      "iqscore": {
        "type": "float"
      },
      "idCardNumber": {
        "type": "keyword"
      }
    }
  }
}

3.2 复杂核心域类型:
除了我们提到的简单标量数据类型, JSON 还有 null 值,数组,和对象,这些 Elasticsearch 都是支持的;
3.2.1 多值域:
很有可能,ES中的字段并不是只有一个值,如我们希望 tag 域包含多个标签。我们可以以数组的形式索引标签:

{ "tag": [ "search", "nosql" ]}

注意:数组是以多值域 索引的—可以搜索,但是无序的。 在搜索的时候,你不能指定 “第一个” 或者 “最后一个”。 更确切的说,把数组想象成 装在袋子里的值 。
3.2.2 空域:
当然,数组可以为空。这相当于存在零值。 事实上,在 Lucene 中是不能存储 null 值的,所以我们认为存在 null 值的域为空域。
下面三种域被认为是空的,它们将不会被索引:

"null_value":               null,
"empty_array":              [],
"array_with_null_value":    [ null ]

3.2.3 地理坐标点:
地理坐标点 是指地球表面可以用经纬度描述的一个点。 地理坐标点可以用来计算两个坐标间的距离,还可以判断一个坐标是否在一个区域中,或在聚合中。
地理坐标点不能被动态映射(dynamic mapping)自动检测,而是需要显式声明对应字段类型为 geo-point :

PUT /attractions
{
  "mappings": {
    "restaurant": {
      "properties": {
        "name": {
          "type": "string"
        },
        "location": {
          "type": "geo_point"
        }
      }
    }
  }
}

如上例,location 字段被声明为 geo_point 后,我们就可以索引包含了经纬度信息的文档了。经纬度信息的形式可以是字符串、数组或者对象:

PUT /attractions/restaurant/1
{
  "name":     "Chipotle Mexican Grill",
  "location": "40.715, -74.011" 
}

PUT /attractions/restaurant/2
{
  "name":     "Pala Pizza",
  "location": { 
    "lat":     40.722,
    "lon":    -73.989
  }
}

PUT /attractions/restaurant/3
{
  "name":     "Mini Munchies Pizza",
  "location": [ -73.983, 40.719 ] 
}

注意:地理坐标点用字符串形式表示时是纬度在前,经度在后( “latitude,longitude” ),而数组形式表示时是经度在前,纬度在后( [longitude,latitude] )—顺序刚好相反。

3.2.3 对象类型存储:
(1) 普通对象:类型object

{
  "gb": {
    "tweet": { 
      "properties": {
        "tweet":            { "type": "string" },
        "user": { 
          "type":             "object",
          "properties": {
            "id":           { "type": "string" },
            "gender":       { "type": "string" },
            "age":          { "type": "long"   },
            "name":   { 
              "type":         "object",
              "properties": {
                "full":     { "type": "string" },
                "first":    { "type": "string" },
                "last":     { "type": "string" }
              }
            }
          }
        }
      }
    }
  }
}

内部对象 经常用于嵌入一个实体或对象到其它对象中,对象的嵌套关系会丢失:
文档存储Elasticsearch系列--1 ES介绍_第2张图片
因为扁平化的存储导致数据关系丢失:
文档存储Elasticsearch系列--1 ES介绍_第3张图片
(2) 嵌套对象nested :
为了解决对象属性被拆分进行扁平化存储造成的关系丢失,ES需要提供一个新的数据类型来处理;
将字段类型设置为 nested 而不是 object 后,每一个嵌套对象都会被索引为一个 隐藏的独立文档;
在独立索引每一个嵌套对象后,对象中每个字段的相关性得以保留。我们查询时,也仅仅返回那些真正符合条件的文档。
不仅如此,由于嵌套文档直接存储在文档内部,查询时嵌套文档和根文档联合成本很低,速度和单独存储几乎一样。
嵌套文档是隐藏存储的,我们不能直接获取。如果要增删改一个嵌套对象,我们必须把整个文档重新索引才可以。值得注意的是,查询的时候返回的是整个文档,而不是嵌套文档本身;

   "idCard": {
        "type": "nested",
        "properties": {
          "idCardNumber": {
            "type": "keyword"
          },
          "idCardName": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            },
            "analyzer": "standard"
          },
          "idCardUrl": {
            "type": "keyword"
          },
          "iStatus": {
            "type": "integer"
          }
       }
   }

(3)父子关系文档:
对于内嵌对象来说,我们不能单独修改对象属性,必须要重新存入改文档;并且查询返回的是一个文档的全部,而不是单独的嵌套对象;ES 使用父子级关系来处理这个问题,使得在父-子关系文档中,父对象和子对象都是完全独立的文档,可以单独的修改和删除,并且只检索对应的子文档。
“type”: “branch” :标记employee 文档 是 branch 文档的子文档。

PUT /company
{
  "mappings": {
    "branch": {},
    "employee": {
      "_parent": {
        "type": "branch" 
      }
    }
  }
}

注意:
多代文档的联合查询(查看 祖辈与孙辈关系)虽然看起来很吸引人,但必须考虑如下的代价:

  • 联合越多,性能越差。每一代的父文档都要将其字符串类型的 _id 字段存储在内存中,这会占用大量内存。
  • 当你考虑父子关系是否适合你现有关系模型时,请考虑下面这些建议:
    尽量少地使用父子关系,仅在子文档远多于父文档时使用。
    避免在一个查询中使用多个父子联合语句。
    在 has_child 查询中使用 filter 上下文,或者设置 score_mode 为 none 来避免计算文档得分。
    保证父 IDs 尽量短,以便在 doc values 中更好地压缩,被临时载入时占用更少的内存。
    最重要的是: 先考虑下我们之前讨论过的其他方式来达到父子关系的效果。

参考:
ES权威指南

你可能感兴趣的:(java工具篇,db数据库,elasticsearch,大数据)