Elasticsearch学习笔记

Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,流行的企业级搜索引擎,也是最受欢迎的企业搜索引擎*。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。

一 下载安装

因为elasticsearch是用Java开发的,所以要有Java环境。

1.1 Elasticsearch

  1. 官网下载压缩包
    Elasticsearch

  2. 上传到自定义目录下,解压

  3. 服务器设置
    ① 设置 vm.max_map_count,不小于262144
    修改/etc/sysctl.conf文件,添加

    vm.max_map_count=262144
    
    vim /etc/sysctl.conf
    /sbin/sysctl -p
    

    ② 开放端口
    es默认端口9200,9300
    9200为外部通信端口
    9300为节点通信端口

    firewall-cmd --zone=public --add-port=9200/tcp --permanent
    firewall-cmd --reload
    

    ③ 新建用户用于启动elasticsearch
    在Linux下,elasticsearch不支持使用root用户启动。

    groupadd elk
    useradd elk -g elk
    passwd elk
    chown -R elk:elk elasticsearch-7.11.1
    vim /etc/security/limits.conf  #调整elk用户的软硬大小限制(用户的最大打开文件数)
    su elk
    
    # 打开文件/etc/security/limits.conf添加下面的内容,不能小于65535
    elk - nofile 65535
    
  4. es配置
    ① es的jvm内存设置
    因为es是用Java写的,所以es运行在jvm上。
    最大值与最小值必须相同,不超过服务器总内存的50%。
    打开config/jvm.options文件
    在这里插入图片描述
    ② 设置当前节点的节点名和所属集群名
    在这里插入图片描述
    ③ 设置集群模式-单节点

    discovery.type: "single-node"
    

    ④ 如果有设置集群为单节点集群,则不用进行这一步。否则设置集群的主节点名和主节点地址(地址默认 为本地)
    在这里插入图片描述
    ⑤ 网络设置

    network.host: 0.0.0.0  # 绑定运行访问的IP,0.0.0.0运行所有IP访问
    http.port: 9200  # 配置运行端口
    
    # 配置跨域访问,运行elasticsearch-head连接es
    # 开启跨域访问
    http.cors.enabled: true
    # 允许跨域访问的IP
    http.cors.allow-origin: "*"
    # 如果es开启了安全模式,则配置
    http.cors.allow-headers: Authorization,X-Requested-With,Content-Type,Content-Length
    

    在这里插入图片描述

  5. 执行bin目录下的启动脚本
    ① Windows下,执行脚本elasticsearch.bat
    在这里插入图片描述 ② Linux下,执行命令

    ./bin/elasticsearch
    ./bin/elasticsearch -d [-p pid] # 后台运行
    

    在这里插入图片描述

  6. 访问,验证是否启动成功

    curl http://127.0.0.1:9200
    

    在这里插入图片描述

  7. 停止

    jps  # 查看Java进程
    jps | grep Elasticsearch
    kill -SIGTERM 进程ID
    pkill -F pid  # 读取进程文件pid中的进程ID,关闭进程
    
  8. 设置为开机自启动

    sudo /bin/systemctl daemon-reload
    sudo /bin/systemctl enable elasticsearch.service
    # 之后可以通过systemctl命令操作
    sudo systemctl start elasticsearch.service
    sudo systemctl stop elasticsearch.service
    
  9. 启动安全模式,设置内置用户的密码
    ①修改配置文件config/elasticsearch.yml,添加 :xpack.security.enabled: true
    ②重启es
    ③输入交互命令:./bin/elasticsearch-setup-passwords interactive
    ④设置密码
    在这里插入图片描述
    ⑤验证
    在这里插入图片描述

1.2 elasticsearch-head

elasticsearch-head是es的web前端工具,方便可视化的操作es

使用前提:有Node.js环境

  1. github下载压缩包
    在这里插入图片描述

  2. 解压

  3. 安装依赖
    进入项目根目录下,安装依赖,运行安装命令

    cnpm install
    
  4. 启动
    进入项目根目录下,执行命令

    npm run start
    

    在这里插入图片描述

  5. 访问

    http://127.0.0.1:9100
    // 如果es开启了安全模式,则需要带上账号密码
    http://127.0.0.1:9100/?auth_user=elastic&auth_password=elastic
    

    在这里插入图片描述

  6. 连接Elasticsearch
    在这里插入图片描述

1.3 Kibana

Kibana是一个与es协作的web端可视化工具,用于管理与操作es。又node.js开发,所以需要Node.js环境

  1. 官网下载压缩包
    注:与Elasticsearch的版本相同在这里插入图片描述

  2. 解压

  3. Linux下开放端口,默认端口5601

    firewall-cmd --zone=public --add-port=5601/tcp --permanent
    firewall-cmd --reload
    
  4. 允许远程访问:修改绑定地址 server.host ,默认为localhost
    修改配置文件config/kibana.yml
    在这里插入图片描述

  5. 绑定elasticsearch
    在这里插入图片描述

  6. 汉化
    修改配置文件config/kibana.yml
    在这里插入图片描述

  7. 启动
    ① Windows:执行bin目录下的启动脚本
    在这里插入图片描述
    ② Linux:

    cd /usr/local/ELK/kibana-7.11.1-linux-x86_64/
    ./bin/kibana
    # 后台启动
    nohup ./bin/kibana &  # nohup ./bin/kibana > logs/kibana.out 2>&1 &
    exit
    tail -f nohup.out
    
  8. 访问:http://localhost:5601
    在这里插入图片描述
    如果开启了安全模式,则进入登录页面(使用账号elastic)
    在这里插入图片描述

  9. 点击“”自己浏览“”
    在这里插入图片描述

  10. 操作es
    在这里插入图片描述
    在这里插入图片描述

  11. 操作用户
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  12. 其他设置
    在这里插入图片描述

  13. 关闭

    netstat -nlp | grep 5601
    kill -9 进程ID
    

1.4 ik分词器

官方文档

用于分词,支持自定义字典。

  1. 根据Elasticsearch版本从github下载对应版本的压缩包
    在这里插入图片描述

  2. 在Elasticsearch下面的plugins目录下新建ik文件夹

  3. 解压到ik目录下
    在这里插入图片描述

  4. 自定义字典
    新建xxx.dic文件

  5. 加入自定义字典
    打开elasticsearch-7.11.1/plugins/ik/config/IKAnalyzer.cfg.xml文件,把自己的字典加进去
    在这里插入图片描述

  6. 重启Elasticsearch
    在这里插入图片描述

  7. 分词查询

    // 最小分词
    GET _analyze
    {
      "analyzer": "ik_smart",
      "text": "中华人民共和国万岁"
    }
    
    // 最大分词
    GET _analyze
    {
      "analyzer": "ik_max_word",
      "text": "中华人民共和国万岁"
    }
    
  8. es内置分词器
    es内置分词器官方文档

二 es基础知识

2.1 定义

  • index:索引,相当于表,存放文档
  • doc:文档,相当于表的一行,json格式
  • field:字段,相当于表的列,doc中json数据的key值。多层嵌套json的field用点.连接
  • id:doc的唯一值
  • mappings:映射,相当于关系型数据库中的表结构,用于定义索引的数据结构,字段的数据类型
  • hits:命中,表示根据搜索条件,命中的结果

2.2 数据类型

2.2.1 字符串

keyword官方文档
text官方文档

类型注释
text用于全文搜索,不用于排序,如产品描述等,会被分词,匹配单个分词,
match_only_text一种空间优化的text,用于禁用计分,它最适合索引日志消息。
keyword不会分词,用于过滤与排序。如邮件地址、主机名、状态码等
constant_keyword特殊的keyword,类似Java中的常量
wildcard通配符,也是一个keyword字段,用于非结构化机器生成的内容,针对具有大值或高基数的字段进行了优化。
version版本,特殊的keyword,

2.2.2 数字

number官方文档
unsigned_long官方文档

  1. 整数

    在满足需求的情况下尽量选择小的。

    类型描述范围大小
    long一个有符号的 64 位整数-263 到 263 - 1
    integer一个有符号的 32 位整数-231 到 231-1
    short一个有符号的 16 位整数-32,768 到 32,767
    byte一个有符号的 8 位整数-128 到 127
    unsigned_long一个无符号的 64 位整数0 到 264-1(包括0和264-1)
  2. 浮点型

    类型描述
    double双精度 64 位 IEEE 754 浮点数,仅限于有限值。
    float单精度 32 位 IEEE 754 浮点数,仅限于有限值。
    half_float半精度 16 位 IEEE 754 浮点数,仅限于有限值。
    scaled_float由long支持的浮点数,按固定double比例因子缩放。

    对于float、half_float和scaled_float,-0.0和+0.0是不同的值,使用term查询查找-0.0不会匹配+0.0,反之亦然,同样range查询中上边界是-0.0不会匹配+0.0,下边界是+0.0不会匹配-0.0。

    其中scaled_float,比如价格只需要精确到分,price为57.34的字段缩放因子为100,存起来就是5734。

    优先考虑使用带缩放因子的scaled_float浮点类型。因为对于浮点类型,使用缩放因子将浮点数据存储为整数通常更有效,这对节省磁盘空间很有帮助,因为整数比浮点更容易压缩。

    如果scaled_float不合适,则选择满足需求的最小的。
    在这里插入图片描述

2.2.3 日期

  1. date
    官方文档
    日期类型,可以接受日期格式的字符串,或者自计算机纪元以来的秒数或毫秒数的数字(但数字必须为正数,所以1970以前,只能用字符串格式表示)。
    默认格式:“format”: “strict_date_optional_time||epoch_second”
    自定义日期格式

    PUT my-index-000001
    {
      "mappings": {
        "properties": {
          "date": {
            "type":   "date",
            "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
          }
        }
      }
    }
    

    多种格式用||分隔,其中第一个是搜索结果展示的日期格式。而在排序中,日期都是展示毫秒数。

  2. date_nanos
    官方文档
    保存时间自1970以来的纳秒的数字。

2.2.4 boolean

官方文档
布尔类型,可以接收true或false,或者它们两个的字符串"true"/“false”,也可以接受空字符串,表示false

2.2.5 binary(二进制)

官方文档
二进制类型是指用base64编码的二进制字符串,可用来存储二进制形式的数据,例如图像。默认情况下,该字段不存储且不可搜索。

2.2.6 ip

官方文档
用于索引或存储IPv4或者IPv6的地址

2.2.7 range(范围)

官方文档

范围字段类型表示介于上限和下限之间的连续值范围。它们是使用运算符 gt或gte为下界和lt或lte为上界定义的。

①integer_range,带符号的32位整数区间,最小值-231,最大值231-1
②long_range,带符号的64位整数区间,最小值-263,最大值263-1
③float_range,单精度32位IEEE 754浮点数区间
④double_range,双精度64位IEEE 754浮点数区间
⑤date_range,日期值范围,表示为系统纪元以来经过的无符号64位整数毫秒
⑥ip_range,支持IPv4或IPv6(或混合)地址ip值范围

2.2.8 地理数据

  • geo-point:地址位置数据类型,可以用来存放经纬度。可以接受5种类型的经纬度
    官方文档

    PUT my-index-000001
    {
      "mappings": {
        "properties": {
          "location": {
            "type": "geo_point"
          }
        }
      }
    }
    
    PUT my-index-000001/_doc/1
    {
      "text": "Geopoint as an object",
      "location": { 
        "lat": 41.12,
        "lon": -71.34
      }
    }
    
    PUT my-index-000001/_doc/2
    {
      "text": "Geopoint as a string",
      "location": "41.12,-71.34"  // 字符串,格式:"lat,lon"
    }
    
    PUT my-index-000001/_doc/3
    {
      "text": "Geopoint as a geohash",
      "location": "drm3btev3e86"  // geohash,Geohashes 是经纬度交错的位的base32编码字符串。geohash 中的每个字符都会为精度增加额外的 5 位。所以哈希越长,它就越精确。出于索引目的,geohash 被转换为纬度-经度对。在此过程中仅使用前 12 个字符,因此在 geohash 中指定超过 12 个字符不会提高精度。12 个字符提供 60 位,这应该将可能的错误减少到小于 2cm。
    }
    
    PUT my-index-000001/_doc/4
    {
      "text": "Geopoint as an array",
      "location": [ -71.34, 41.12 ]  // 数组,格式: [ lon, lat]
    }
    
    PUT my-index-000001/_doc/5
    {
      "text": "Geopoint as a WKT POINT primitive",
      "location" : "POINT (-71.34 41.12)" // 字符串,格式:"POINT(lon lat)"
    }
    
    GET my-index-000001/_search
    {
      "query": {
        "geo_bounding_box": { 
          "location": {
            "top_left": {
              "lat": 42,
              "lon": -72
            },
            "bottom_right": {
              "lat": 40,
              "lon": -74
            }
          }
        }
      }
    }
    
  • geo-shape:地理形状数据类型,当被索引的数据包含除地理点之外的形状(如长方形、圆形…)时,使用此数据类型。geo-shape映射将GeoJSON(一种对各种地理数据结构进行编码的格式)映射到geo-shape类型。可以表示点、线(两个点)、多边形(多个点,第一与最后一个点相同,闭合才是形状)、带孔多边形(多个多边形,第一个多边形是外边界,后面的是里面的孔)
    官方文档

2.2.9 Aggregate metric(聚合指标)

官方文档
聚合字段用来保存一组最大值,最小值,和,总数等其中几个指标的字段类型

  1. 定义

    PUT stats-index
    {
      "mappings": {
        "properties": {
          "agg_metric": {
            "type": "aggregate_metric_double",
            "metrics": [ "min", "max", "sum", "value_count" ],  // 字段中的指标
            "default_metric": "max"  // 默认指标,搜索时用来匹配
          }
        }
      }
    }
    
  2. 插入数据

    PUT stats-index/_doc/1
    {
      "agg_metric": {
        "min": -302.50,
        "max": 702.30,
        "sum": 200.0,
        "value_count": 25
      }
    }
    
  3. 查询

    POST stats-index/_search?size=0
    {
      "aggs": {
        "metric_min": { "min": { "field": "agg_metric" } },
        "metric_max": { "max": { "field": "agg_metric" } },
        "metric_value_count": { "value_count": { "field": "agg_metric" } },
        "metric_sum": { "sum": { "field": "agg_metric" } },
        "metric_avg": { "avg": { "field": "agg_metric" } }
      }
    }
    // 查询字段agg_metric中max(由default_metric指定)为702.30的
    GET stats-index/_search
    {
      "query": {
        "term": {
          "agg_metric": {
            "value": 702.30
          }
        }
      }
    }
    

2.2.10 Alias(别名)

官方文档
别名类型,即将一个字段作为另一个字段的别名来使用

PUT trips
{
  "mappings": {
    "properties": {
      "distance": {
        "type": "long"
      },
      "route_length_miles": {
        "type": "alias",  // 设置类型为别名
        "path": "distance"   // 指定为那个字段的别名,要是完整的路径
      },
      "transit_mode": {
        "type": "keyword"
      }
    }
  }
}

2.2.11 array

官方文档
在es中,没有专用的数组类型,但是默认情况下,任何类型都是数组类型。
比如字段name定义类型为keyword,但是它不仅支持字符串,也支持字符串数组。
通过中括号表示数组,但中括号中的数据类型必须相同。

PUT my_test_index
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "integer"
      },
      "birthday": {
        "type": "date"
      }
    }
  }
}

POST my_test_index/_doc
{
  "name": ["张三", "阿三"],
  "age": 18,
  "birthday": "2000-05-24"
}

2.2.12 向量

  • dense_vector(密集向量)
    官方文档
    存放多维向量数据,实际是存放数字类型的数组,其中的数字类型不一定要相同。
    dense_vector字段不支持查询、排序或聚合。它们只能通过专用的向量函数在脚本中访问。

    PUT my-index-000001
    {
      "mappings": {
        "properties": {
          "my_vector": {
            "type": "dense_vector",
            "dims": 3  // 向量维度,必须
          },
          "my_text" : {
            "type" : "keyword"
          }
        }
      }
    }
    
    PUT my-index-000001/_doc/1
    {
      "my_text" : "text1",
      "my_vector" : [0.5, 10, 6]
    }
    
  • sparse_vector(稀疏向量)
    官方文档

    稀疏向量字段存储浮点值的稀疏向量。向量中可以包含的最大维数不应超过1024。不同文档的维度数量可能不同。稀疏_向量场是单值场。
    这些向量可用于文档评分。例如,文档分数可以表示给定查询向量和索引文档向量之间的距离。
    将稀疏向量表示为对象,其中对象字段是维度,字段值是这些维度的值。维度是0到65535之间的整数值,编码为字符串。尺寸不需要按顺序排列。

    PUT my-index-000001
    {
      "mappings": {
        "properties": {
          "my_vector": {
            "type": "sparse_vector"
          },
          "my_text" : {
            "type" : "keyword"
          }
        }
      }
    }
    
    PUT my-index-000001/_doc/1
    {
      "my_text" : "text1",
      "my_vector" : {"1": 0.5, "5": -0.5,  "100": 1}
    }
    
    PUT my-index-000001/_doc/2
    {
      "my_text" : "text2",
      "my_vector" : {"103": 0.5, "4": -0.5,  "5": 1, "11" : 1.2}
    }
    

2.2.13 token_count(令牌计数)

官方文档
实际上是一个integer字段。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "name": { 
        "type": "text",
        "fields": {
          // 计算name中单词的数量
          "length": { 
            "type":     "token_count",
            "analyzer": "standard"
          }
        }
      }
    }
  }
}

PUT my-index-000001/_doc/1
{ "name": "John Smith" }

PUT my-index-000001/_doc/2
{ "name": "Rachel Alice Williams" }
// 搜索名字是三个单词的
GET my-index-000001/_search
{
  "query": {
    "term": {
      "name.length": 3 
    }
  }
}

2.2.14 histogram(直方图)

官方文档

2.2.15 join

官方文档
在doc级别建立一对多的关系,即将某一些doc作为某一个doc的子级。

2.2.16 对象

  • object:接收一个json对象。官方文档

  • flattened(扁平)
    官方文档
    flattened类型接受json格式数据,即把一个json对象作为一个字段。它只允许基本查询,不支持数字范围查询或高亮显示。

    PUT bug_reports
    {
      "mappings": {
        "properties": {
          "title": {
            "type": "text"
          },
          "labels": {
            "type": "flattened"
          }
        }
      }
    }
    
    POST bug_reports/_doc/1
    {
      "title": "Results are not sorted correctly.",
      "labels": {
        "priority": "urgent",
        "release": ["v1.2.5", "v1.3.0"],
        "timestamp": {
          "created": 1541458026,
          "closed": 1541457010
        }
      }
    }
    
  • nested:嵌套类型,特殊的object类型。可以保持对象的独立性,不使对象被扁平化为多值字段
    官方文档

    // 下面示例中,如果没有第二步,类型为object,那么最后一步搜索是有结果的,但两个
    // 搜索条件并不在同一个对象里面,所以这是错误的。有第二步指定类型,就不会有搜索结果
    DELETE my-index-000001
    
    PUT  my-index-000001
    {
      "mappings": {
        "properties": {
          "group": {
            "type": "text"
          },
          "user": {
            "type": "nested"
          }
        }
      }
    }
    
    PUT my-index-000001/_doc/1
    {
      "group" : "fans",
      "user" : [ 
        {
          "first" : "John",
          "last" :  "Smith"
        },
        {
          "first" : "Alice",
          "last" :  "White"
        }
      ]
    }
    
    GET /my-index-000001/_mapping
    
    GET my-index-000001/_search
    {
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "Smith" }}
          ]
        }
      }
    }
    

2.2.17 percolator

官方文档

2.2.18 形状

  • point(点)
    官方文档
    存放二维平面的点数据。

    PUT my-index-000001
    {
      "mappings": {
        "properties": {
          "location": {
            "type": "point"
          }
        }
      }
    }
    // 四种插入数据的方式
    PUT my-index-000001/_doc/1
    {
      "text": "Point as an object",
      "location": { 
        "x": 41.12,
        "y": -71.34
      }
    }
    
    PUT my-index-000001/_doc/2
    {
      "text": "Point as a string",
      "location": "41.12,-71.34" 
    }
    
    
    PUT my-index-000001/_doc/4
    {
      "text": "Point as an array",
      "location": [41.12, -71.34] 
    }
    
    PUT my-index-000001/_doc/5
    {
      "text": "Point as a WKT POINT primitive",
      "location" : "POINT (41.12 -71.34)" 
    }
    
  • shape(形状)
    官方文档
    用于二维平面的几何图形。
    支持点、直线,多边形、一组未连接但可能相关的点、一组直线、一组单独的多边形(有孔多边形)、或点线等混合形状。

2.2.19 rank_feature / rank_features

rank_feature官方文档
rank_features官方文档

2.2.20 search_as_you_type

官方文档

2.3 元数据

官方文档

每个文档都有与其关联的元数据,例如_index/ _type、_id等。创建映射类型时,可以自定义其中一些元数据字段的行为。

  • _index:文档所属的索引
  • _type:文档的映射类型
  • _id:文档的唯一标识
  • _source:文档主体的原始JSON
  • _size:_source的大小(字节)
  • _doc_count:当文档表示预聚合数据时,用于存储文档计数的自定义字段
  • _field_names:文档中包含非空值的所有字段。
  • _ignored:存储和索引被忽略的字段
  • _routing:将文档路由到特定分片的自定义路由值。
  • _meta:自定义元数据,用于存储特定于应用程序的元数据,例如文档所属的在应用程序中对应的类
  • _tier:在跨多个索引执行查询时,有时需要将保存在给定数据层(data_hot、data_warm、data_cold或data_frozen)节点上的索引作为目标。该_tier字段允许匹配tier_preference文档被索引到的索引设置。在某些查询中可以访问首选值

三 kibana操作CRUD

3.1 index(索引)

  1. 创建

    // 创建索引my_test_index
    PUT my_test_index
    
  2. 查询

    // 查看所有索引状态
    GET _cat/indices
    // 查看索引my_test_index
    GET my_test_index
    
  3. 删除

    // 删除索引my_test_index
    DELETE my_test_index
    

3.2 mapping(映射)

无法删除映射,也无法修改已有的字段映射,只能重建索引。

  1. 创建
    ① 动态映射
    即自动创建映射。可以直接插入数据,es会帮忙创建映射。

    POST my_test_index/_doc
    {
      "name": "张三",
      "age": 18,
      "birthday": "2000-05-24"
    }
    

    ② 显示映射
    手动创建映射

    PUT my_test_index
    {
      "mappings": {
        "properties": {
          "name": {
            "type": "text"
          },
          "age": {
            "type": "integer"
          },
          "birthday": {
            "type": "date"
          }
        }
      }
    }
    
  2. 查看

    // 查看全部
    GET my_test_index/_mapping
    // 查看指定字段的映射,多个字段用英文逗号分割
    GET my_test_index/_mapping/field/birthday,name
    // 还可以用通配符
    GET my_test_index/_mapping/field/b*
    // 从多个索引中查询,多个索引用英文逗号分割
    GET /my_test_index,my_test_index2/_mapping/field/b*
    // 从所有索引中查询
    GET /_all/_mapping/field/b*
    
  3. 添加

    PUT my_test_index/_mapping
    {
      "properties": {
        "tel": {
          "type": "keyword"
        }
      }
    }
    

3.3 aliases(别名)

官方文档
别名有数据流别名和索引别名。别名与数据流或索引是一对多的关系。可以用别名代替索引名等进行操作

  1. 添加

    POST _aliases
    {
      "actions": [
        {
          "add": {
            "index": "logs-nginx.access-prod",
            "alias": "logs"
          }
        }
      ]
    }
    POST _aliases
    {
      "actions": [
        {
          "add": {
            "index": "logs-*",
            "alias": "logs"
          }
        }
      ]
    }
    
  2. 查看

    // 查看集群所有的别名
    GET _alias
    
    // 查看指定索引或数据流的别名
    GET my-data-stream/_alias
    
    // 查看别名logs对应的索引和数据流
    GET _alias/logs
    
  3. 移除

    POST _aliases
    {
      "actions": [
        {
          "remove": {
            "index": "logs-nginx.access-prod",
            "alias": "logs"
          }
        }
      ]
    }
    
  4. 批量操作

    POST _aliases
    {
      "actions": [
        {
          "remove": {
            "index": "logs-nginx.access-prod",
            "alias": "logs"
          }
        },
        {
          "add": {
            "index": "logs-my_app-default",
            "alias": "logs"
          }
        }
      ]
    }
    

3.4 操作数据

3.4.1 插入

  • 插入一条数据

    // 向索引my-test-index插入一行数据,指定ID为1
    PUT /my-test-index/_doc/1
    {
      "name": "zs",
      "age": 18,
      "teacher": [{
        "name": "赵老师",
        "age": 30
      }, {
        "name": "钱老师",
        "age": 40
      }]
    }
    // 向索引my-test-index插入一行数据,不指定ID,生成随机ID
    POST /my-test-index/_doc
    {
      "name": "zs",
      "age": 18,
      "teacher": [{
        "name": "赵老师",
        "age": 30
      }, {
        "name": "钱老师",
        "age": 40
      }]
    }
    
  • 插入多条数据

    PUT /my-test-index/_bulk
    {"create": {}}
    {"name":"zs","age":18,"teacher":[{"name":"赵老师","age":30},{"name":"钱老师","age":40}]}
    {"create": {}}
    {"name":"zs","age":18,"teacher":[{"name":"赵老师","age":30},{"name":"钱老师","age":40}]}
    

3.4.2 搜索

  1. match、match_phrase和term
    match:会对查询条件进行分词。搜索结果只要匹配其中一个分词就可以
    match_phrase:会对查询条件进行分词。但搜索结果需要包含所有分词,且连续
    term:不对查询条件进行分词

  2. _source:元JSON,即插入数据时提供的JSON,查询会默认返回,且是全量返回。设置为false则不会返回。也可以指定返回字段

    GET /recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "_source": false
    }
    
    GET /recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "_source": ["name"]
    }
    
  3. fields:字段,指定返回t特定字段的结果,不返回只存储未映射的字段

    GET /recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "_source": false,
      "fields": ["name"]
    }
    // 如果字段是时间或地理空间坐标,还可以指定格式
    POST my-index-000001/_search
    {
      "query": {
        "match": {
          "user.id": "kimchy"
        }
      },
      "fields": [
        "user.id",
        "http.response.*",         
        {
          "field": "@timestamp",
          "format": "epoch_millis" 
        }
      ],
      "_source": false
    }
    // 如果是嵌套字段,需要写全路径,用点分隔
    POST my-index-000001/_search
    {
      "fields": ["user.first"],
      "_source": false
    }
    // 要返回只存储未映射的字段,使用"include_unmapped" : true 
    POST my-index-000001/_search
    {
      "fields": [
        "user_id",
        {
          "field": "session_data.object.*",
          "include_unmapped" : true 
        }
      ],
      "_source": false
    }
    
  4. size:指定返回结果的条数。

    // 返回3条数据
    GET recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "size": 3
    }
    
  5. from:对返回结果进行截取,指定从哪一条开始,

    // 对根据“鱼”查询到的结果,从第二条开始截取
    GET recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "from": 2
    }
    
  6. 分页:size + from

    // 查询第二页数据,每页3条
    GET recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "size": 3, 
      "from": 3
    }
    
  7. sort(排序):根据某些(可多个)字段对查询结果进行排序。

    // 根据字段rating对返回结果进行排序
    GET recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "sort": [
        {
          "rating": {
            "order": "desc"
          }
        }
      ]
    }
    
  8. collapse(折叠):根据某个字段对返回结果去重。

    // 对查询结果根据字段type去重
    GET recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "collapse": {
        "field": "type"
      }
    }
    

    根据某个字段对返回结果分组,可以对每组数据进行组内排序,还可以指定每组有几条数据。

    GET /recipes/_search
    {
      "query": {
        "match": {
          "name": "鱼"
        }
      },
      "collapse": {
        "field": "type",
        "inner_hits": [{  // 分组可以一次支持多种组内实现方式
          "name": "most_recent",  // 分组名称,自定义
          "size": 2,  // 每个分组的数据量
          "sort": [{
            "rating": "desc"  // 分组根据字段rating进行倒序排序
          }]
        },{
          "name": "max",
          "size": 3,
          "sort": [{
            "rating": "asc"
          }]
        }],
        "max_concurrent_group_searches": 4  // 每组允许检索的并发请求数
      },
      "size": 4,
      "sort": [
        {
          "rating": {
            "order": "asc"
          }
        }
      ],
      "from": 0
    }
    

    组内(inner_hits内部)还可以支持继续使用collapse。

  9. filter:过滤,没有打分,但效率高,会对命中和聚合都进行过滤

    GET /recipes/_search
    {
      "query": {
        "bool": {
          "filter": [{
            "term": {
              "name": "鱼"
            }}
          ]
        }
      }
    }
    
  10. post_filter:后过滤,有打分,只对命中结果进行过滤

  11. highlight:高亮显示。原理是对返回结果中添加标签样式

```java
GET /recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "highlight": {
    "fields": {
      "name": {}
    }
  }
}
// 使用内置styled标签架构
GET /recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "highlight": {
    "tags_schema": "styled", 
    "fields": {
      "name": {}
    }
  }
}
// 自定义标签
GET /recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "highlight": {
    "fields": {
      "name": {}
    },
    "pre_tags": ["<tag1>", "<tag2>"],
    "post_tags": ["</tag1>", "</tag2>"]
  }
}
// 即使字段是单独存储的,也强制突出显示基于源的字段。默认为false.
GET /recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "highlight": {
    "fields": {
      "name": {"force_source" : true}
    }
  }
}
```

四 集成springboot

官方文档

  1. 新建项目

  2. 引入依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <version>0.0.1-SNAPSHOT</version>
        <name>api</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
            <elasticsearch.version>7.11.1</elasticsearch.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.75</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
  3. 同步Elasticsearch版本
    在这里插入图片描述

  4. 配置

    spring:
      elasticsearch:
        rest:
          uris: 127.0.0.1:9200
    
  5. 使用
    批量插入

    public String parseDataToEs(String keyword) {
       try {
            // 获取数据
            List<Content> contents = htmlParseUtil.parseJd(keyword);
    
            // 判断索引是否存在
            GetIndexRequest existsRequest = new GetIndexRequest("jd_commodity");
            boolean existsResponse = restHighLevelClient.indices().exists(existsRequest, RequestOptions.DEFAULT);
            if (!existsResponse) {
                // 创建索引
                CreateIndexRequest createRequest = new CreateIndexRequest("jd_commodity");
                restHighLevelClient.indices().create(createRequest, RequestOptions.DEFAULT);
            }
            // 批量请求
            BulkRequest bulkRequest = new BulkRequest();
            for (Content content: contents) {
                bulkRequest.add(new IndexRequest("jd_commodity").source(JSON.toJSONString(content), XContentType.JSON));
            }
            // 发送请求
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (bulkResponse.hasFailures()) {
                return "failed";
            }
        } catch (IOException e) {
            return "failed";
        }
        return "successful";
    }
    

    搜索

    public List<Map<String, Object>> searchPage(String keyword, int from, int size) {
        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest("jd_commodity");
        // 构建搜索条件
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", keyword);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(matchQueryBuilder);
        // 分页
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);
    
        searchRequest.source(searchSourceBuilder);
        try {
            // 发送请求
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            List<Map<String, Object>> mapList = new ArrayList<>();
            for (SearchHit hit: searchResponse.getHits()) {
                // 高亮替换
                HighlightField name = hit.getHighlightFields().get("name");
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                if (name != null) {
                    Text[] fragments = name.fragments();
                    String temp = "";
                    for (Text text: fragments) {
                        temp += text;
                    }
                    sourceAsMap.put("name", temp);
                }
                mapList.add(sourceAsMap);
            }
            return mapList;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    

    其他

    @Test
    void createIndex() throws IOException {
        CreateIndexRequest request = new CreateIndexRequest("springboot");
        CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(response.index());
        restHighLevelClient.close();
    }
    
    @Test
    void existIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest("springboot");
        boolean response = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        System.out.println(response);
        restHighLevelClient.close();
    }
    
    @Test
    void deleteIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest("springboot");
        AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
        System.out.println(response.isAcknowledged());
        restHighLevelClient.close();
    }
    
    @Test
    void createId() throws IOException {
        GetIndexRequest existsRequest = new GetIndexRequest("springboot");
        boolean existsResponse = restHighLevelClient.indices().exists(existsRequest, RequestOptions.DEFAULT);
        if (!existsResponse) {
            CreateIndexRequest createRequest = new CreateIndexRequest("springboot");
            restHighLevelClient.indices().create(createRequest, RequestOptions.DEFAULT);
        }
        User user = new User("张三", 3);
        user.setAge("aaa".equals(user.getName()) ? 0 : 1);
        IndexRequest indexRequest = new IndexRequest("springboot");
        indexRequest.id("1");
        indexRequest.timeout(TimeValue.timeValueSeconds(1));
        indexRequest.source(JSON.toJSONString(user), XContentType.JSON);
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        System.out.println(indexResponse.status());
        restHighLevelClient.close();
    }
    
    @Test
    void existsId() throws IOException {
        GetRequest getRequest = new GetRequest("springboot", "1");
        // 不获取返回的_source的上下文,效率更快
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        System.out.println(exists);
        restHighLevelClient.close();
    }
    
    @Test
    void getId() throws IOException {
        GetRequest getRequest = new GetRequest("springboot", "1");
        GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        System.out.println(getResponse.toString());
        System.out.println(getResponse.getIndex());
        System.out.println(getResponse.getId());
        System.out.println(getResponse.getFields());
        System.out.println(getResponse.getSeqNo());
        System.out.println(getResponse.getPrimaryTerm());
        System.out.println(getResponse.getSourceAsString());
        restHighLevelClient.close();
    }
    
    @Test
    void updateId() throws IOException {
        UpdateRequest updateRequest = new UpdateRequest("springboot", "1");
        User user = new User("李四", 4);
        updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
        updateRequest.timeout("1s");
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println(updateResponse.toString());
        System.out.println(updateResponse.status());
        restHighLevelClient.close();
    }
    
    @Test
    void deleteId() throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest("springboot", "1");
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println(deleteResponse.status());
        System.out.println(deleteResponse.toString());
        restHighLevelClient.close();
    }
    
    /**
     * 批处理
     *
     * @throws IOException io异常
     */
    @Test
    void bulkCreate() throws IOException {
        GetIndexRequest existsRequest = new GetIndexRequest("springboot");
        boolean existsResponse = restHighLevelClient.indices().exists(existsRequest, RequestOptions.DEFAULT);
        if (!existsResponse) {
            CreateIndexRequest createRequest = new CreateIndexRequest("springboot");
            restHighLevelClient.indices().create(createRequest, RequestOptions.DEFAULT);
        }
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        for (int i = 0; i < 10; i++) {
            User user = new User(String.valueOf(i), i);
            bulkRequest.add(new IndexRequest("springboot").id(String.valueOf(i)).source(JSON.toJSONString(user), XContentType.JSON));
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(bulkResponse.status());
        System.out.println(bulkResponse.hasFailures()); // 是否失败
        restHighLevelClient.close();
    }
    
    @Test
    void search() throws IOException {
        SearchRequest searchRequest = new SearchRequest("springboot");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    
        MatchQueryBuilder query = QueryBuilders.matchQuery("name", "1");
        sourceBuilder.query(query);
        sourceBuilder.timeout(TimeValue.timeValueSeconds(10));
        // 分页
        sourceBuilder.from(0);
        sourceBuilder.size(2);
        // 高亮必须要有sourceBuilder.query(query);查询条件,且高亮字段必须与查询字段相同
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.preTags();
        highlightBuilder.postTags();
        sourceBuilder.highlighter(highlightBuilder);
    
        searchRequest.source(sourceBuilder);
    
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(searchResponse.status());
        System.out.println(JSON.toJSONString(searchResponse));
        for (SearchHit searchHit: searchResponse.getHits().getHits()) {
            System.out.println(searchHit.toString());
        }
        restHighLevelClient.close();
    }
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
elasticsearch 学习笔记包括以下内容: 一、Elasticsearch概述: - Elasticsearch是一种开源的分布式搜索和分析引擎,可以用于快速搜索、分析和存储大量的结构化和非结构化数据。 - Elasticsearch与Solr相比有一些区别,包括用户、开发和贡献者社区的规模和成熟度等方面。 二、Elasticsearch安装: 1. 下载Elasticsearch,可以从官方网站或华为云镜像下载。 2. 安装Elasticsearch。 三、安装head插件: - head插件是一个可视化的管理界面,可以方便地管理和监控Elasticsearch集群。 四、安装Kibana: 1. Kibana是一个开源的数据可视化工具,用于展示和分析Elasticsearch中的数据。 2. 下载Kibana并安装。 3. 启动Kibana并进行访问测试。 4. 可选的汉化操作。 五、ES核心概念理解: - 学习ES的核心概念,包括索引、文档、映射、查询等。 以上是elasticsearch学习笔记的主要内容,希望对你有帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Elasticsearch 学习笔记(上)](https://blog.csdn.net/m0_52691962/article/details/127064350)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值