24、ElasticSearch

安装ElasticSearch

  • 安装1.8及以上的jdk
  • https://www.elastic.co/cn/downloads/elasticsearch 下载对应版本
  • 上传到云服务器 /usr/local/software/ 目录下
  • tar -zxvf elasticsearch-7.12.1-linux-x86_64.tar.gz解压
  • cd /usr/local/software/elasticsearch-7.12.1/bin
  • 执行./elasticsearch报错
    java.lang.RuntimeException: can not run elasticsearch as root
  • 新建es用户
  • cd /usr/local/software
  • groupadd esgroup
  • useradd esuser -g esgroup -p 123456
  • chown -R esuser:esgroup elasticsearch-7.12.1/
  • su - esuser
  • ./elasticsearch 前台启动
  • ./elasticsearch -d 后台启动 需要等一段时间
  • 配置跨域访问 /usr/local/software/elasticsearch-7.12.1/config/elasticsearch.yml 最后添加下面两行
http.cors.enabled: true
http.cors.allow-origin: "*"
  • curl 127.0.0.1:9200 成功会有相应信息

  • 如果出现 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]错误 编辑 /etc/sysctl.conf,追加以下内容:vm.max_map_count=655360保存后,执行:sysctl -p

  • 出现 max file descriptors [65535] for elasticsearch process is too low, increase to at least [65536] 切换到root用户编辑 /etc/security/limits.conf,追加以下内容

    soft nofile 65536
    hard nofile 65536
    
  • 默认只允许本机访问 配合Nginx 实现外网访问

   upstream es_pool{
        server 127.0.0.1:9200;
    }

    upstream es_header_pool{
        server 127.0.0.1:9100;
    }

    server {
        listen       80;
        server_name  localhost;

        location /es/ {
            proxy_pass http://es_pool/;
            proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /es_header/ {
            proxy_pass http://es_header_pool/;
            proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
     }
  • 允许内网访问的 elasticsearch.yml 配置文件
cluster.name: my-application
node.name: node-1
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["127.0.0.1", "[::1]"]
cluster.initial_master_nodes: ["node-1"]

http.cors.allow-origin: "*"
http.cors.enabled: true
http.cors.allow-headers : X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization
http.cors.allow-credentials: true

安装ElasticSearch-Head

  • 通过https://nodejs.org/en/download/ 下载nodejs版本

  • 上传到/usr/local/software下面

  • tar -xvf node-v14.17.0-linux-x64.tar.xz 解压

  • export PATH=$PATH:/usr/local/software/node-v14.17.0-linux-x64/bin

  • ln -s /usr/local/software/node-v14.17.0-linux-x64/bin/npm /usr/local/bin/
    ln -s /usr/local/software/node-v14.17.0-linux-x64/bin/node /usr/local/bin/

  • 执行node -v 打印版本 成功

  • 通过 https://github.com/mobz/elasticsearch-head 下载最新版本

  • yum install -y unzip zip 安装unzip解压

  • 上传到/usr/local/software/ unzip elasticsearch-head-master.zip

  • npm install -g grunt-cli 安装依赖

  • yum install bzip2 //可能会报解压错误 没有忽略即可

  • npm install cnpm -g --registry=https://registry.npm.taobao.org 使用npm因为网络原因总是报错

  • cd elasticsearch-head-master/ 执行 cnpm install

  • 修改Gruntfile.js

connect: {
    server: {
        options: {
            hostname: '*', # 添加这一行
            port: 9100,
            base: '.',
            keepalive: true
        }
    }
}
  • 修改_site下面的app.js 4388行左右将localhost替换成ElasticSearch的服务器地址
this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://192.168.0.199:9200";
  • npm run start 启动
  • nohup npm run start & 后台启动

安装 Kibana

  • https://www.elastic.co/cn/downloads/kibana 下载 版本一定要和ElasticSearch一致
  • 上传到服务器 /usr/local/software 并解压
  • cd /usr/local/software
  • mv kibana-7.12.1-linux-x86_64/ kibana
server.host: "192.168.0.199"
elasticsearch.hosts: ["http://192.168.0.199:9200"]
i18n.locale: "zh-CN"
  • 执行bin目录下的kibana ./kibana --allow-root
  • 访问 http://192.168.0.199:5601/

ElasticSearch 术语

  • 索引index —— 拥有相似文档的集合
  • 类型type —— 一个索引中可以定义多个类型
  • 字段field —— 相当于数据库表中的字段
  • 映射mapping —— 处理数据方式和规格方面做一些限制
  • 文档document —— 一个文档是一个可被索引的基础单元
  • 分片 —— 由于单台机器无法存储大量数据,ES 可以将一个索引中的数据切分为多个分片(Shard),分布在多台服务器上存储。
  • 复制 —— 备份

操作

  • 使用PUT请求添加text索引 http://192.168.0.199/es/test
  • 新建索引的时候设置mapping信息 注意 7.4 默认不在支持指定索引类型 http://192.168.0.199/es/test1 下面是请求体
{
    "mappings": {
        "properties": {
            "id": {
                "type": "long",
                "store": true
            },
            "title": {
                "type": "text",
                "store": true,
                "analyzer": "standard"
            },
            "content": {
                "type": "text",
                "store": true,
                "analyzer": "standard"
            }
        }
    }
}
  • 查看索引对应的映射 类型默认都是_doc
{
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        },
        "id": {
          "type": "long",
          "store": true
        },
        "title": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        }
      }
    }
  }
}
  • 索引创建完成后POST请求添加mapping信息 http://192.168.0.199/es/test/db1/_mapping?include_type_name=true 在test下面新增db1结果如下
{
  "mappings": {
    "db1": {
      "properties": {
        "content": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        },
        "id": {
          "type": "long",
          "store": true
        },
        "title": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        }
      }
    }
  }
}
  • DELETE请求删除索引库 http://192.168.0.199/es/test1
  • 添加文档(一条数据就是一个文档)http://192.168.0.199/es/test/db1/1 在db1类型下设置id为1的数据 下面是请求体 保持和映射一致就行 如果db1后面不加/1 会自动生成一个随机字符串id 操作时一般保持id字段和请求体里面的id字段保持一致
{
    "id":1,
    "title":"文档标题",
    "content":"文档内容"
}
  • id设置相同数据会被覆盖 用来实现修改文档

  • DELETE方法删除文档 http://192.168.0.199/es/test/db1/1

  • 查询

    • 根据id查询 GET请求 http://192.168.0.199/es/test/db1/1 即可

    • 关键字 POST http://192.168.0.199/es/test/db1/_search 只能使用一个汉字

      {
          "query":{
              "term":{
                  "title":"文" //在title字段上查询关键字有文的
              }
          }
      }
      
    • queryString查询 路径同上

        {
            "query_string":{
                "term":{
                    "default_field":"title",
                    "query":"文档"                
                }
            }
        }
    
    • 先分词 如果title中包含文 或者 档 的都会被查出来
    • 查看分词情况 GET 请求 http://192.168.0.199/es/test/_analyze
    {
      "analyzer" : "standard",
      "text": ["文档标题2"]
    }
    
    • 关键词查询 like query_string 先对查询内容分词
     {
            "query":{
                "query_string":{ 
                    "query":"title:(\"题2\")",
                    "default_operator": "and"
                }
            }
        }
    

    or

    {
        "query": {
            "match_phrase": {
                "title": "题2"
            }
        }
    }
    

安装中文分词器IK

ElasticSearch内置分词器

  1. standard分词器:(默认分词器)它会将词汇单元转换成小写形式,并除去停用词(a、an、the等)和标点符号,支持中文采用的方法为单字切分。
  2. simple分词器:首先会通过非字母字符来分割文本信息,然后将词汇单元统一成小写形式,该分词器会去掉数据类型的字符。
  3. Whitespace分词器:仅仅是去掉空格,对字符没有lowcase化,不支持中文;并且不对生成的词汇单元进行其他的标准化处理。
  4. language分词器:特定语言的分词器,不支持中文

中文分词器的安装

  • https://github.com/medcl/elasticsearch-analysis-ik/releases 下载最新版本的zip包
  • cd /usr/local/software/elasticsearch-7.12.1/plugins/
  • mkdir ik
  • 把文件移动到ik目录下
  • cd ik/
  • unzip elasticsearch-analysis-ik-7.12.1.zip
  • 切换用户重启 elasticsearch
  • 新建映射的时候就需使用ik分析器
  • ik_smart(最小) 和 ik_max_word(最细)的区别 举例分析程序员 ik_smart 会解析成程序员 ik_max_word 会解析成三个分别是 程序员 程序 员
{
  "mappings": {
    "db1": {
      "properties": {
        "content": {
          "type": "text",
          "store": true,
          "analyzer": "ik_smart"
        },
        "id": {
          "type": "long",
          "store": true
        },
        "title": {
          "type": "text",
          "store": true,
          "analyzer": "ik_max_word"
        }
      }
    }
  }
}

集群

修改config\elasticsearch.yml文件添加

#集群名称,保证唯一
cluster.name: my‐elasticsearch
#节点名称,必须不一样
node.name: node‐1 # node‐2 node‐3
#必须为本机的ip地址
network.host: 192.168.0.199 # 0.200  0.201
#服务端口号
http.port: 9200
#服务TCP端口号
transport.tcp.port: 9300
#设置集群自动发现机器ip集合
discovery.zen.ping.unicast.hosts:["192.168.0.199:9300","192.168.0.200:9300","192.168.0.201:9302"]

SpringBoot 整合

  • 创建工程 并引入依赖 内部引用的es版本必须要和服务器安装的匹配

    org.springframework.boot
    spring-boot-starter-data-elasticsearch
    2.3.10.RELEASE


    org.springframework.boot
    spring-boot-starter-test
    test

  • application.yml配置
spring:
  elasticsearch:
    rest:
      uris: 192.168.0.199:9200
      username: my-application
  #   uris:192.168.0.199:9200,192.168.0.200:9200,192.168.0.201:9200,
  • 实体与document对应类
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

@Document(indexName = "stu") //索引名称
@Data
public class Stu {
    @Id
    private Long stuId; //会创建相同的文档id 否则自动生成

    @Field(store = true)
    private String name;

    @Field(store = true)
    private Integer age;
}
  • 实体类中新加四个字段 会自动添加到索引里面
@Field(store = true)
private Float money;

@Field(store = true, type = FieldType.Keyword)
private String sign;

@Field(store = true)
private String description;

@Field(type = FieldType.Date, format = DateFormat.date_time)
private Date recordTime;
  • 关于常用方法 2.3.10 版本中的
@RunWith(SpringRunner.class)
@SpringBootTest
public class ESTest {
    /**
     * 一般不建议用Spring来创建/更新 删除索引
     * 一般针对文档进行CRUD操作
     */
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 创建/更新索引 如果存在 就更新 不会再创建 一般不使用
     */
    @Test
    public void createIndexStu() {
        Stu stu = new Stu();
        stu.setStuId(1006L);
        stu.setAge(33);
        stu.setName("李七");
        stu.setRecordTime(new Date());
        IndexQuery indexQuery = new IndexQueryBuilder().withObject(stu).build();
        IndexCoordinates indexCoordinates = elasticsearchRestTemplate.getIndexCoordinatesFor(Stu.class);
        elasticsearchRestTemplate.index(indexQuery, indexCoordinates);
    }

    /**
     * 更新文档
     */
    @Test
    public void updateStuDoc() {
        Document document = Document.create();
        document.put("name", "李四");
        document.put("age", 20);
        UpdateQuery updateQuery = UpdateQuery.builder("1000").withDocument(document).build();
        elasticsearchRestTemplate.update(updateQuery, elasticsearchRestTemplate.getIndexCoordinatesFor(Stu.class));
    }

    /**
     * 查询 + 高亮显示
     */
    @Test
    public void queryStuDoc() {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //必须满足的条件 加keyword不会拆分 不加会拆分成李和四
//        boolQueryBuilder.must().add(new TermQueryBuilder("name.keyword", "李四"));
//        boolQueryBuilder.must().add(new TermQueryBuilder("name", "四"));
        // 查询3天内数的数据
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("recordTime");
        rangeQueryBuilder.gte(LocalDateTime.now().minusDays(3));
//        或者下面的
//        rangeQueryBuilder
//                .from("2021-05-16T00:00:00.000Z")
//                .to("2021-05-19T23:59:59.000Z");
//        boolQueryBuilder.must().add(rangeQueryBuilder);


        // 分页查询20条
        PageRequest pageRequest = PageRequest.of(0, 20, Sort.by("stuId").descending());

        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder
                .withQuery(boolQueryBuilder)
                // 亲测使用.keyword有值但是高亮数据size = 0
                .withHighlightBuilder(new HighlightBuilder().field("name")
                        .preTags("")
                        .postTags(""))
                .withPageable(pageRequest);


        Query searchQuery = searchQueryBuilder.build();

        SearchHits result = elasticsearchRestTemplate.search(searchQuery, Stu.class);
        List> hitList = result.getSearchHits();
        System.out.println("hit size -> {}" + hitList.size());
        hitList.forEach(hit -> {
            Map> highlightFields = hit.getHighlightFields();
            List nameHighlight = highlightFields.get("name");
            if (nameHighlight != null) {
                for (String text : nameHighlight) {
                    hit.getContent().setName(text);
                }
            }
            System.out.println("返回数据:" + hit.getContent().toString());
        });
    }

    /**
     * 删除文档
     */
    @Test
    public void delIndexDoc() {
        elasticsearchRestTemplate.delete("1000", elasticsearchRestTemplate.getIndexCoordinatesFor(Stu.class));
    }
}

你可能感兴趣的:(24、ElasticSearch)