Elasticsearch(ES)

目录

elasticsearch搜索引擎

一、elasticsearch的安装

 二、操作索引库

三、Restclient

DSL语句

一、DSL语句

二、restclient操作DSL语句

三、ES集群

        数据聚合

自动补全

es集群

elastic stack(ELK):结合elasticsearch、kibana、logstash、beats的技术栈

lucene:apache的搜索引擎类库,提供了搜索引擎的核心api。elasticsearch就是通过lucene实现

elasticsearch搜索引擎

一、elasticsearch的安装

        单点部署

创建网络,用于kibana和elasticsearch互联

docker network create es-net

拉取镜像(太慢了建议本地下载再导入)

docker pull elasticsearch:7.12.1

运行镜像

docker run -d \
    --name es \
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
    -e "discovery.type=single-node" \
    -v es-data:/usr/share/elasticsearch/data \
    -v es-plugins:/usr/share/elasticsearch/plugins \
    --privileged \
    --network es-net \
    -p 9200:9200 \
    -p 9300:9300 \
elasticsearch:7.12.1

  • -e "cluster.name=es-docker-cluster":设置集群名称

  • -e "http.host=0.0.0.0":监听的地址,可以外网访问

  • -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":内存大小

  • -e "discovery.type=single-node":非集群模式

  • -v es-data:/usr/share/elasticsearch/data:挂载逻辑卷,绑定es的数据目录

  • -v es-logs:/usr/share/elasticsearch/logs:挂载逻辑卷,绑定es的日志目录

  • -v es-plugins:/usr/share/elasticsearch/plugins:挂载逻辑卷,绑定es的插件目录

  • --privileged:授予逻辑卷访问权

  • --network es-net :加入一个名为es-net的网络中

  • -p 9200:9200:端口映射配置

  部署kibana(和elasticsearch一样下载)//可视化平台

docker run -d \
--name kibana \

-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601  \
kibana:7.12.1

安装ik分词器  //用何种分词方法,以及自定义分词

# 进入容器内部
docker exec -it elasticsearch /bin/bash

# 在线下载并安装
./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip

#退出
exit
#重启容器
docker restart elasticsearch

 二、操作索引库

mysql和elasticsearch对比

MySQL

Elasticsearch

说明

Table

Index

索引(index),就是文档的集合,类似数据库的表(table)

Row

Document

文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式

Column

Field

字段(Field),就是JSON文档中的字段,类似数据库中的列(Column)

Schema

Mapping

Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)

SQL

DSL

DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD

mapping是对索引库中文档的约束,常见的mapping属性包括: type:字段数据类型,常见的简单类型有: 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址) 数值:long、integer、short、byte、double、float、 布尔:boolean 日期:date 对象:object index:是否创建索引,默认为true analyzer:使用哪种分词器 properties:该字段的子字段

索引库

#创建索引库
PUT /student
{
  "mappings": {
    "properties":{
      "name":{
        "type":"keyword",
        "index":true
        },
      "sex":{
        "type":"keyword",
        "index":true
      },
      "words":{
        "type": "text",
        "index": false,
        "analyzer": "ik_smart"        
      }
    }
  }
}

#获取索引
GET /student

#删除索引
DELETE /student

#更新索引         mapping建立后不能更改,但是可以增加新字段
PUT /student/_mapping
{
  "properties":{
    "age":{      
      "type":"keyword",
      "index":true
    }
  }
}

文档

#创建文档
POST /student/_doc/1
{
  "name":"张三",
  "sex":"男",
  "words":"我不知道未来会带来什么,但我会努力过好每一天",
  "age":"21"
}

#获取文档
GET /student/_doc/1

#更新文档
#增量修改   修改某一个值
POST /student/_update/1
{
  "doc": {
    "age":"18"
  }
}
#全量修改   先删除后增加新文档
PUT /student/_doc/1
{
  "name":"李四",
  "sex":"男",
  "words":"我不知道未来会带来什么,但我会努力过好每一天",
  "age":"21"
}

#删除文档
DELETE /student/_doc/1

注意如果创建文档时,增加了mapping里面没有的字段时,es默认帮你创建,默认的规则为

JSON类型

Elasticsearch类型

字符串

日期格式字符串:mapping为date类型

普通字符串:mapping为text类型,并添加keyword类型子字段

布尔值

boolean

浮点数

float

整数

long

对象嵌套

object,并添加properties

数组

由数组中的第一个非空类型决定

空值

忽略

三、Restclient

restclient是es官方提供给java操作DSL语句的客户端。

索引的crud

创建索引

 @BeforeEach
    void initRestClient(){
        //        1.初始化restclient
         client = new RestHighLevelClient(RestClient.builder(HttpHost.create("192.168.35.150:9200")));
    }
    @AfterEach
    void closeRestClient() throws IOException {
        client.close();
    }

    @Test   //创建索引
    void createIndex() throws IOException {
        //  1.创建request
        CreateIndexRequest request = new CreateIndexRequest("hotel");
        //  2.请求参数    MAPPING_DEFAULT就是json格式的索引
        request.source(MAPPING_DEFAULT, XContentType.JSON);
        //  3.发送请求
        client.indices().create(request, RequestOptions.DEFAULT);
    }

获取索引

 @Test   //获取索引
    void existIndex() throws IOException {
        //  1.创建request
        GetIndexRequest request = new GetIndexRequest("hotel");
        //  2.发送请求
        boolean exist = client.indices().exists(request, RequestOptions.DEFAULT);
        // 3.输出
        System.out.println(exist ?"索引存在" :"索引不存在");
    }

删除索引

 @Test   //删除索引
    void deleteIndex() throws IOException {
        //  1.创建request
        DeleteIndexRequest request = new DeleteIndexRequest("hotel");
        //  2.发送请求
        AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
        // 3.输出
        System.out.println(response);
    }

文档的crud

创建文档

private RestHighLevelClient client;
    @Autowired
    private HotelService service;
    @BeforeEach
    void initRestClient(){
        client= new RestHighLevelClient(RestClient.builder(HttpHost.create("192.168.35.150:9200")));
    }
    @AfterEach
    void closeRestClient() throws IOException {
        client.close();
    }
    @Test
    void createDoc() throws IOException {
        Hotel hotel = service.getById(415600L);
        HotelDoc hotelDoc = new HotelDoc(hotel);
        //  1.创建request
        IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
        //  2.请求参数
        request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);
        //  3.发送请求
        client.index(request,RequestOptions.DEFAULT);
    }

查询文档

 @Test
    void getDoc() throws IOException {
        //  1.创建request
        GetRequest request = new GetRequest("hotel", "415600");
        //  2.发送请求
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        // 3.输出
        System.out.println(response.getSourceAsString());
    }

修改

 @Test //这是局部更新,全局更新和createDoc一样
    void updateDoc() throws IOException {
        //  1.创建request
        UpdateRequest request = new UpdateRequest("hotel", "415600");
        //  2.参数
        request.doc(
                "brand" , "和颐"
        );
        //  3.发送请求
        client.update(request,RequestOptions.DEFAULT);

    }

删除

@Test   //删除文档
    void deleteIndex() throws IOException {
        //  1.创建request
        DeleteRequest request = new DeleteRequest("hotel", "415600");
        //  2.发送请求
        client.delete(request,RequestOptions.DEFAULT);
    }

批量插入文档

 @Test   //批量插入文档
    void bulkRequset() throws IOException {
        BulkRequest request = new BulkRequest();
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.lt(Hotel::getPrice,200);
        List list = service.list(wrapper);
        for(Hotel hotel: list){
            System.out.println("==================="+hotel);
            HotelDoc hotelDoc = new HotelDoc(hotel);
            request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc),XContentType.JSON));
        }
        client.bulk(request,RequestOptions.DEFAULT);
    }

DSL语句

一、DSL语句

Elasticsearch提供了基于JSON的DSL(Domain Specific  Language)来定义查询。常见的查询类型包括:

        查询所有:查询出所有数据,一般测试用。例如:match_all

        全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如: match_query multi_match_query

        精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如: ids range term

        地理(geo)查询:根据经纬度查询。例如: geo_distance geo_bounding_box

        复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如: bool function_score

查询  macth

#查询全部   
GET  /hotel/_search
{
  "query": {
    "match_all": {}
  }}

#match查询多个字段    将所有字段都写到all,可以直接搜索多个单词,只要含有all里面的字#段都可以检索
GET /hotel/_search
{
  "query": {
    "match": { "all": "速8上海"}
  }}

查询多个字段  mutil_match        与上面的效果相同,推荐使用上面的那种

#mutil_match查询  匹配多个字段
GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "北京如家",
      "fields": ["city","brand","name"]
    }
  }}

精确查询 term

#term查询  精确查询,字段值必须完全匹配
GET /hotel/_search
{
  "query": {
    "term": {"city": {"value": "深圳"}}
  }}

范围查询  range

#range查询  范围查询
GET /hotel/_search
{
  "query": {
    "range": {

        "price": {"gte": 100,"lte": 150}
    }
  }}

地理查询        geo_distance (点)        geo_bounding_box(范围)

#地理查询 geo_distance
GET /hotel/_search
{
  "query": {
    "geo_distance": {
      "distance": "10km",
      "location": "22.642629,114.202799"
    }}}

#地理查询 geo_bounding_box
GET /hotel/_search
{
  "query": {
    "geo_bounding_box": {
      "location":{
        "top_left": {"lat":32,"lon":121},
        "bottom_right": {"lat":22,"lon":114}
      }
    }}}

复杂排序        function_source

elasticsearch中的相关性打分算法

        TF-IDF算法:在elasticsearch5.0之前,会随着词频增加而越来越大

        BM25算法:在elasticsearch5.0之后,会随着词频增加而增大,但增长曲线会趋于水平

#复合查询 function_score算分查询
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {"match_all": {}},
      "functions": [{
          "filter": {"term": {"city": "深圳"}},
          "weight": 10
        }],
      "boost_mode": "sum"
    }}}  

复杂排序         Boolean Query

布尔查询是一个或多个查询子句的组合。子查询的组合方式有:

         must:必须匹配每个子查询,类似“与”

        should:选择性匹配子查询,类似“或”

        must_not:必须不匹配,不参与算分,类似“非”

         filter:必须匹配,不参与算分(放在这里过滤不影响性能)

#复合查询 bool查询
GET /hotel/_search
{
  "query": {
    "bool": {
      "must":{ "match":{"name":"如家"}},
      "must_not": [{"range": { "price": { "gte": 200}}}],
      "should": [
        { "match": {"business": "松岗商业中心区"}},
        { "match": { "business": "房山风景区"}}],
      "filter": [{"term": {"city": "深圳"}}]
    }}}  

对于结果的处理,排序,分页,高亮

排序

elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。

#排序
GET /hotel/_search
{
  "query": {"match_all": {} },
  "sort": [

    {"score": { "order": "desc" }},
    {"price":{"order": "asc" }}
  ]}

#排序 地理排序
GET /hotel/_search
{
"query": {"match_all": {} },
  "sort": [
    {"_geo_distance": {
        "location": {"lat": 22,"lon": 114},
        "order": "asc",
        "unit": "km"}}
  ]}

 分页

from + size:

        优点:支持随机翻页

        缺点:深度分页问题,默认查询上限(from + size)是10000 场景:百度、京东、谷歌、淘宝这样的随机翻页搜索

after search:

        优点:没有查询上限(单次查询的size不超过10000)

        缺点:只能向后逐页查询,不支持随机翻页 场景:没有随机翻页需求的搜索,例如手机向下滚动翻页

#分页 from+size

GET /hotel/_search
{
  "query": {"match_all": {}},
  "sort": [{
      "_geo_distance": {
        "location": {"lat": 22,"lon": 114 },
        "order": "asc",
        "unit": "km"}

}],
  "from": 10,
  "size": 10}

高亮         

将搜索结果中的关键字用标签()标记出来 在页面中给标签添加css样式

GET /hotel/_search
{
  "query": {
    "match": {
      "all": "如家"
    }
  },
  "highlight": {
    "fields": {
      "name": {"require_field_match": "false"}
    }
  }}

二、restclient操作DSL语句

match、range、item、geo、function_source、boolean查询都是在query下的子字段,请求的方式都一样,只需要修改query()里面querybulider

@Test   //查询所有
    void match_all() throws IOException {
        //  1.创建request
        SearchRequest request = new SearchRequest("hotel");
        //  2.准备DSL
        //match_all 查询所有的DSL
        //request.source().query(QueryBuilders.matchAllQuery());

        //match     查询多个字段的DSL
        //request.source().query(QueryBuilders.matchQuery("all","速8上海"));

        //multiMatch    multiMatch查询多个字段的DSL
        //request.source().query(QueryBuilders.multiMatchQuery("上海","name","city","business"));

        //term      精确查询的DSL
        //request.source().query(QueryBuilders.termQuery("city","上海"));

        //range     范围查询的DSL
        //request.source().query(QueryBuilders.rangeQuery("price").lt(150));

        //bool      bool复杂查询的DSL
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("city","上海"));
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lt(150));
        request.source().query(boolQueryBuilder);
        
        //  3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //  4.解析返回值
        parseResponse(response);
    }

    @Test    //解析数据
    void parseResponse(SearchResponse response){
        SearchHits hits = response.getHits();
        //  5.查询总条数
        TotalHits totalHits = hits.getTotalHits();
        //  6.获取对象数组
        SearchHit[] searchHits = hits.getHits();
        for(SearchHit searchHit :searchHits){
            System.out.println(searchHit);
        }
    }

排序和分页查询

    @Test
    void sortAndPage() throws IOException {
        SearchRequest request = new SearchRequest("hotel");
        request.source().query(QueryBuilders.matchAllQuery());
        
        //分页
        request.source().from(0).size(10);
        //排序
        request.source().sort("price", SortOrder.ASC);

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        parseResponse(response);
    }

三、ES集群

        数据聚合

聚合的种类

  • 桶(Bucket)聚合:用来对文档做分组

    • TermAggregation:按照文档字段值分组

    • Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组

  • 度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等

    • Avg:求平均值

    • Max:求最大值

    • Min:求最小值

    • Stats:同时求max、min、avg、sum等

  • 管道(pipeline)聚合:其它聚合的结果为基础做聚合

DSL实现聚合

桶聚合,按照文档字段分

GET /hotel/_search
{
  "query": {
    "range": {
      "price": {"gte": 200,"lte": 500}
    }}, 
   "size": 0,                  // 设置size为0,结果中不包含文档,只包含聚合结果
  "aggs": {                   // 定义聚合
    "brandAgg": {         //给聚合起个名字
      "terms": {             // 聚合的类型,按照品牌值聚合,所以选择term
        "field": "brand", // 参与聚合的字段
        "size": 20 }        // 希望获取的聚合结果数量 
    } }
}

metric聚合

#定义meric聚合
GET /hotel/_search
{
  "query": {
    "match_all": {}
  }, 
  "size": 0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "order": {
          "_count": "asc"
        }, 
        "size": 10
      },
      "aggs": {                 // 是brands聚合的子聚合,也就是分组后对每组分别计算
        "score_stats": {     // 聚合名称
          "stats": { // 聚合类型,这里stats可以计算min、max、avg等
            "field": "score" // 聚合字段,这里是score
          }
        }
      }
    }
  }
}

restclient聚合

@Test
    void createAggregation() throws IOException {
        SearchRequest request = new SearchRequest("hotel");
        request.source().size(0);
        request.source().aggregation(AggregationBuilders
                .terms("brand_agg")
                .field("brand")
                .size(10)
        );
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        System.out.println(response);
    }

自动补全

        拼音分词器

       地址:GitHub - medcl/elasticsearch-analysis-pinyin: This Pinyin Analysis plugin is used to do conversion between Chinese characters and Pinyin.

下载导入es的plugin目录中,重启es

        自定义分词器

包含三部分:

  • character filters:在tokenizer之前对文本进行处理。例如删除字符、替换字符

  • tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart

  • tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": { // 自定义分词器
        "my_analyzer": {  // 分词器名称
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": { // 自定义tokenizer filter
        "py": { // 过滤器名称
          "type": "pinyin", // 过滤器类型,这里是pinyin
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "my_analyzer",
        "search_analyzer": "ik_smart"
      }
    }
  }
}

        自动补全

elasticsearch提供了Completion Suggester查询来实现自动补全功能。这个查询会匹配以用户输入内容开头的词条并返回。为了提高补全查询的效率,对于文档中字段的类型有一些约束:

  • 参与补全查询的字段必须是completion类型。

  • 字段的内容一般是用来补全的多个词条形成的数组。

比如,一个这样的索引库:

// 创建索引库
PUT test
{
  "mappings": {
    "properties": {
      "title":{
        "type": "completion"
      }
    }
  }
}

自动补全查询

// 自动补全查询
GET /test/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s", // 关键字
      "completion": {
        "field": "title", // 补全查询的字段
        "skip_duplicates": true, // 跳过重复的
        "size": 10 // 获取前10条结果
      }
    }
  }
}

es集群

rabbitmq实现es和mysql的数据同步。

es集群的搭建

用docker-compose来部署,编写一个compose文件

version: '2.2'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
    container_name: es01
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - elastic
  es02:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
    container_name: es02
    environment:
      - node.name=es02
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data02:/usr/share/elasticsearch/data
    networks:
      - elastic
  es03:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
    container_name: es03
    environment:
      - node.name=es03
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es02
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data03:/usr/share/elasticsearch/data
    networks:
      - elastic

volumes:
  data01:
    driver: local
  data02:
    driver: local
  data03:
    driver: local

networks:
  elastic:
    driver: bridge

运行docker-compose

docker-compose up

es集群不同接待职责

节点类型

配置参数

默认值

节点职责

master eligible

node.master

true

备选主节点:主节点可以管理和记录集群状态、决定分片在哪个节点、处理创建和删除索引库的请求

data

node.data

true

数据节点:存储数据、搜索、聚合、CRUD

ingest

node.ingest

true

数据存储之前的预处理

coordinating

上面3个参数都为false则为coordinating节点

路由请求到其它节点

合并其它节点处理的结果,返回给用户

用cerebro(和kibana类似)来管理,来管理es集群

集群的分布式存储

elasticsearch会通过hash算法来计算文档应该存储到哪个分片:

shard = hash(_routing) % number_of_shards

  • _routing默认是文档的id

  • 算法与分片数量有关,因此索引库一旦创建,分片数量不能修改!

集群故障转移 

集群的master节点会监控集群中的节点状态,如果发现有节点宕机,会立即将宕机节点的分片数据迁移到其它节点,确保数据安全,这个叫做故障转移。

你可能感兴趣的:(elasticsearch,搜索引擎,大数据)