ElasticSearch全文搜索总结

欢迎访问我的个人博客:www.ifueen.com

ElasticSearch

概述

ES是Lucene的一个封装工具,解决了Lucene配置麻烦,不支持分布式的缺点

ES也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单

安装

官方下载:https://www.elastic.co/downloads/elasticsearch

然后解压,运行bin路径下的elasticsearch.bat文件

1574244745968

1574244783437

这样就成功运行了,然后在浏览器输入localhost:9200

1574244817786

出现这个界面就代表成功启动

辅助工具Kibana5

在使用MySql的时候,可以用Navcation来进行可视化管理,Kibana5就是可以对ElasticSearch进行可视化管理的一款工具

官方下载:https://www.elastic.co/downloads/kibana

解压然后修改config/kibana.yml,设置elasticsearch.url的值为已启动的ES地址值(一般不需要修改,默认就是对的)

启动Kibana5 : bin\kibana.bat

默认访问地址:http://localhost:5601

成功访问

增删改查操作

首先明确一点,ES是完全遵从了Restful风格的,关于Restful风格,随便一搜一大堆文章,这里就不详细介绍了

在ES中存储数据的行为就叫做索引(indexing),文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以简单的对比传统数据库和ES的对应关系:

关系数据库(MYSQL) -> 数据库DB-> 表TABLE-> 行ROW-> 列Column

ES的基础语法

  • 增加:PUT PUT {index}/{type}/{id}
  • 修改:同PUT {index}/{type}/{id}
  • 删除:DELETE {index}/{type}/{id}
  • 查询:GET {index}/{type}/{id}

下面通过几个Demo来进行演示

IK分词器

ES默认对英文文本的分词器支持较好,但和lucene一样,如果需要对中文进行全文检索,那么需要使用中文分词器,同lucene一样,在使用中文全文检索前,需要集成IK分词器

GitHub下载:https://github.com/medcl/elasticsearch-analysis-ik

解压,并将其内容放置于ES根目录/plugins/ik

然后重启ES

测试分词

注意:IK分词器有两种类型,分别是ik_smart分词器和ik_max_word分词器。

ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。

ik_max_word:会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合

索引

ES索引的增删改查

# 创建索引库

PUT imp
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1
  }
}
# 查询索引库

GET _cat/indices

# 查看指定索引库
GET _cat/indices/imp

# 修改索引库

# 删除索引库
DELETE imp

DSL查询

DSL过滤语句和DSL查询语句非常相似,但是它们的使用目的却不同:DSL过滤查询文档的方式更像是对于我的条件"有"或者"没有"(等于 ;不等于),而DSL查询语句则像是"有多像"(模糊查询)

DSL过滤和DSL查询在性能上的区别:

  • 过滤结果可以缓存并应用到后续请求。-> 精确过滤后的结果拿去模糊查询性能高
  • 查询语句同时匹配文档,计算相关性,所以更耗时,且不缓存。
  • 过滤语句可有效地配合查询语句完成文档过滤

案例:

#DSL查询
GET fueen/person/_search
{
  "query": {
    "match": {
      "speak": "高级动物"
    }
  }
}

# 创建测试数据
PUT fueen/user/5
{
  "id":5,
  "sex":"SAUMAG Note10 Pro",
  "ceagtor":"手机",
  "money":8000
}

GET fueen/user/_search?_source

# DSL过滤

GET fueen/user/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "ceagtor": "手机"
        }}
      ],
      "filter": {
        "range": {
          "money": {
            "gte": 6000,
            "lte": 8000
          }
        }
      }
    }
  },
  "from": 0, 
  "size": 10,
  "_source": ["sex","ceagtor","money"],
  "sort": [
    {
      "money": "desc"
    }
  ]
}

文档映射

ES的文档映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型

就是规定了每个输入字段值的数据类型

案例

# 映射
PUT imp/life/_mapping
{
  "life":{
    "properties":{
      "id":{
        "type":"long"
      },
      "name":{
        "type":"text",
        "analyzer":"ik_smart",
        "search_analyzer":"ik_smart"
      }
    }
  }
}

# 查看文档映射
GET imp/_mapping/life

# 删除映射
DELETE imp

# 动态模板
PUT _template/kof_template  
{
  "template":   "*",  
  "settings": { "number_of_shards": 1 }, 
  "mappings": {
    "_default_": {
      "_all": { 
        "enabled": false 
      },
      "dynamic_templates": [
        {
          "string_as_text": { 
            "match_mapping_type": "string",
            "match":   "*_text", 
            "mapping": {
              "type": "text",
              "analyzer": "ik_max_word",
              "search_analyzer": "ik_max_word",
              "fields": {
                "raw": {
                  "type":  "keyword",
                  "ignore_above": 256
                }
              }
            }
          }
        },
        {
          "string_as_keyword": { 
            "match_mapping_type": "string",
            "mapping": {
              "type": "keyword"
             }
          }
        }
      ]
    }
  }}

集群

为什么需要ES集群

和Redis一样,集群能够做很多事情

  • 单节点故障
  • 支持高并发
  • 海量数据存储

ES的集群节点

主节点

node.master=true,代表该节点有成为主资格,主节点的主要职责是和集群操作相关的内容,如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。一般会把主节点和数据节点分开,node.master=true , node.data=false

数据节点

node.data=true,数据节点主要是存储索引数据的节点,主要对文档进行增删改查操作,聚合操作等,数据节点对CPU,IO,内存要求较高,优化节点的时候需要做状态监控,资源不够时要做节点扩充。配置:mode.master=false,mode.data=true

负载均衡节点

当主节点和数据节点配置都设置为false的时候,该节点只能处理路由请求,处理搜索,分发索引操作等,从本质上来说该客户节点表现为智能负载平衡器。配置:mode.master=false,mode.data=false

模拟搭建集群

准备三个ES服务

可以模拟出三个不同的文件,修改不同的端口

配置说明

  • Node1-配置
# 统一的集群名
cluster.name: my-ealsticsearch
# 当前节点名
node.name: node-1
# 对外暴露端口使外网访问
network.host: 127.0.0.1
# 对外暴露端口
http.port: 9201
#集群间通讯端口号
transport.tcp.port: 9301
#集群的ip集合,可指定端口,默认为9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
  • Node2-配置
# 统一的集群名
cluster.name: my-ealsticsearch
# 当前节点名
node.name: node-2
# 对外暴露端口使外网访问
network.host: 127.0.0.1
# 对外暴露端口
http.port: 9202
#集群间通讯端口号
transport.tcp.port: 9302
#集群的ip集合,可指定端口,默认为9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
  • Node3-配置
# 统一的集群名
cluster.name: my-ealsticsearch
# 当前节点名
node.name: node-3
# 对外暴露端口使外网访问
network.host: 127.0.0.1
# 对外暴露端口
http.port: 9203
#集群间通讯端口号
transport.tcp.port: 9303
#集群的ip集合,可指定端口,默认为9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]

分别启动三个ES节点 , 访问:http://127.0.0.1:9201/

然后再通过Kiban5去访问,修改默认配置路径为:elasticsearch.url: “http://localhost:9201”

访问成功

Java操作ES

创建一个Maven项目,在pom.xml中加入

<dependencies>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>transport</artifactId>
            <version>5.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.7</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

写一个连接ES的工具类

package com.ifueen.es;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * ES集群的工具类
 */
public class ESClientUtil {

    public static TransportClient getClient(){
        Settings settings = Settings.builder()
        .put("cluster.name","my-ealsticsearch")
        .put("client.transport.sniff", true).build();
        
        TransportClient client = null;
        try {
            client = new PreBuiltTransportClient(settings)
                    .addTransportAddress(
                    		new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9302));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return client;
    }
}

然后开始测试

package com.ifueen.es;

import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequestBuilder;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.junit.Test;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 测试集群
 */
public class TestESCluster {
    //获取客户端
    TransportClient client = ESClientUtil.getClient();
    /**
     * 添加文档
     */
    @Test
    public void testadd(){

        //创建索引
        IndexRequestBuilder index = client.prepareIndex("fueen", "person", "1");
        Map<String,Object> data = new HashMap<String, Object>();
        data.put("id","1");
        data.put("username","fueen");
        data.put("speak","我们不能失去信仰");
        //添加
        IndexResponse indexResponse = index.setSource(data).get();
        System.out.println(indexResponse);
        client.close();

    }

    /**
     * 获取文档
     */
    @Test
    public void testquery(){
        GetResponse getFields = client.prepareGet("fueen", "person", "1").get();
        System.out.println(getFields.getSource());
    }

    /**
     * 更新
     */
    @Test
    public void testupdate(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("id","1");
        map.put("username","这个世界会好吗");
        map.put("speak","李志");
        UpdateRequestBuilder builder = client.prepareUpdate("fueen", "person", "1");
        UpdateResponse updateResponse = builder.setDoc(map).get();
        System.out.println(updateResponse);
        client.close();
    }

    /**
     * 删除文档
     */
    @Test
    public void testdel(){
        DeleteRequestBuilder del = client.prepareDelete("fueen", "person", "1");
        DeleteResponse deleteResponse = del.get();
        System.out.println(deleteResponse);
        client.close();
    }

    /**
     * 批量添加
     */
    @Test
    public void testbulikadd(){
        BulkRequestBuilder builder = client.prepareBulk();
        Map<String, Object> map = new HashMap<>();
        map.put("id","1");
        map.put("username","会沉寂吗");
        map.put("speak","我的金桔");
        builder.add(client.prepareIndex("fueen","person","1")
                .setSource(map));


        Map<String, Object> map1 = new HashMap<>();
        map1.put("id","2");
        map1.put("username","会沉寂吗");
        map1.put("speak","人民不需要自由");
        builder.add(client.prepareIndex("fueen","person","2")
                .setSource(map1));

        BulkResponse bulkItemResponses = builder.get();
        Iterator<BulkItemResponse> iterator = bulkItemResponses.iterator();
        while (iterator.hasNext()){
            BulkItemResponse next = iterator.next();
            System.out.println(next.getResponse());
        }
        client.close();
    }

    /**
     * 批量查询
     */
    @Test
    public void testbulikquery(){
        SearchRequestBuilder fueen = client.prepareSearch("fueen");
        fueen.setTypes("person");
        fueen.setFrom(0);
        fueen.setSize(10);

        //查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        List<QueryBuilder> must = boolQueryBuilder.must();
        must.add(QueryBuilders.matchQuery("username","会沉寂吗"));

        SearchResponse searchResponse = fueen.setQuery(boolQueryBuilder).get();
        SearchHits hits = searchResponse.getHits();
        System.out.println("条数:"+hits.getTotalHits());
        hits.forEach(h->{
            System.out.println(h);
        });
        client.close();
    }

}


发布了87 篇原创文章 · 获赞 7 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/f2315895270/article/details/103205669