Elasticsearch 学习

Elasticseach 学习

ES 简介

ES结合kibana、Logstash、Beat,也就是elastic stack(ELK)。用于日志数据分析可视化,实时监控等领域

image-20231103135639293

elasticseach是slastic stack的核心,负责存储,搜索,分析数据

elasticseach的底层是Lucene,Lucene是Java语言的一个搜索引擎类库,是Apache公司的顶级项目,官网https://lucene.apache.org/

image-20231103140033052

image-20231103140352168

基于Lucene做二次开发形成elasticseach

image-20231103140508131

image-20231103140728486

正向索引和倒排索引

传统数据库,如MySQL采用正向索引

![image-20231103141052785](https://gitee.com/zwx0203/cloudimage/raw/master/202311031410896.png

image-20231103141327599

image-20231103142052223

image-20231103142217517

ES 与 MySQL概念对比

文档(可以理解为mysql表中一行数据),序列化为json格式后存储在elasticseach中

image-20231103142711137

索引(index):相同类型的文档的集合

image-20231103143310392

image-20231103143424951

概念对比

DSL使用JSON风格语句来CRUD。

在MySQL中SQL通过connection发给MySQL。

DSL通过http来发送请求,因为es给的是restful接口,这种接口与语言无关,任何只要能发http请求的语言都能把它的DSL发给es的restful接口让es进行处理。

image-20231103143746287

问题:什么时候用mysql什么时候用es ?

两者实际上是互补关系,而不是替代的关系

用户写数据直接写到mysql,因为mysql可确保数据安全&一致性。

用户搜索数据则通过es来进行。

通过中间组件将mysql数据同步给es

image-20231103144607101

image-20231103145110229

安装ES、Kibana、Ik分词器

elasticsearch:Elasticsearch 7.12.0 | Elastic

​ 下载解压后,双击bin中的elasticsearch.bat 然后访问http://localhost:9200/ 若生成如下界面 则成功

image-20231103154509302

Kibana:Kibana 7.12.0 | Elastic

​ 下载解压后,双击bin中kibana.bat 然后访问http://127.0.0.1:5601/ 若生成如下界面 则成功

image-20231103154340585

ik分词器:https://github.com/medcl/elasticsearch-analysis-ik 将ik分词器解压后放到es目录的plugins下

image-20231103154116911

IK分词器的用法

ik_smart 分词算法 ,最少切分

image-20231103155202555

image-20231103155234192

ik_max_word 模式 最细切分

image-20231103155411841

![image-20231103155325717](https://gitee.com/zwx0203/cloudimage/raw/master/202311031553762.png

image-20231103155357563

ik分词器的使用:【精选】ElasticSearch——IK分词器的下载及使用_ik分词器下载-CSDN博客

ik分词器-拓展词库

ik分词器-禁用词库

image-20240117103459413

什么时候分词?

答:有两个时间分词。第一个是将文档创建到索引的时候,要对文档某个内容进行分词,将词条创建倒排索引

第二个是当用户来搜索时,用户输入一大串话,需要给它进行分词

IK分词器总结

image-20231103161928090

索引库操作

索引库对应MySQL中的表,文档对应MySQL表中一行行的数据。

在MySQL中先创建表,才能在表中存入数据。

在elasticsearch中同样先得建立索引库,才能存入文档。

因此,这里先学索引库操作,再学文档操作。

mapping映射属性

es中没有数组,但它允许同一种类型有多个值。数据类型则为数组中数据的类型

image-20231109092931354

image-20231109093100226

索引库的CRUD

创建索引库

image-20231110085341241

创建下面的索引表

image-20231110092618366

#创建索引库
PUT /heima_all
{
  "mappings":{
    "properties": {
      "age": {
        "type": "integer"
      },
      "weigth": {
        "type": "float"
      },
      "isMarried": {
        "type": "boolean"
      },
      "info": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "email": {
        "type": "keyword",
        "index": false
      },
      "score": {
        "type": "float"
      },
      "name": {
        "type": "object",
        "properties": {
          "firstname": {
            "type": "keyword"
          },
          "lastName": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
查、删 索引库

GET /索引库名

DELETE /索引库名

image-20231110093106802

修改索引库

es禁止修改索引库,但可以添加新的字段

··

#在heimaa索引库中新增"address"字段
PUT /heimaa/_mapping
{
  "properties": {
    "address": {
      "type": "text",
      "analyzer": "ik_smart"
    }
  }
}

image-20231110094926573

文档的CRUD

新增文档

image-20231110095441121

查询文档,删除文档

image-20231207135137689

image-20231207135519659

修改文档

image-20231207140457312

总结

image-20231207140940888

RestClient操作索引库

案例

image-20231207142144059

image-20231207144641293

image-20231207144848187

image-20231207151954617

要根据多个字段,比如根据brand,name,business等来查,则可以使用copy_to定义一个字段,将brand,name,business等拷贝到指定字段如下。在通过指定字段去查就好了

image-20231207152431089

###酒店demo的mapping
PUT /hotel
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword" 
      },
      "name": {
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address": {
        "type": "keyword",
        "index": false 
      },
      "price": {
        "type": "integer"
      },
      "score": {
        "type": "integer"
      },
      "brand": {
        "type": "keyword",
        "copy_to": "all"
      },
      "city": {
        "type": "keyword"
      },
      "starName": {
        "type": "keyword"
      },
      "business": {
        "type": "keyword",
        "copy_to": "all"
      },
      "location": {
        "type": "geo_point"
      },
      "pic": {
        "type": "keyword",
        "index": false
      },
      "all": {
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

步骤3详见 hotel-demo项目 D:\zwx\code3\hotel-demo

image-20231207153123171

步骤4 用JavaRestClient来创建索引库

在测试类中先进行如下操作,http://localhost:9200为ES客户端的地址

private RestHighLevelClient client;

@BeforeEach
void setUp() {
    this.client = new RestHighLevelClient(RestClient.builder(
            HttpHost.create("http://localhost:9200")
    ));
}

@AfterEach
void tearDown() throws IOException {
    this.client.close();
}

image-20231207161921624

image-20231207164124118

总结Java RestClient索引库操作的步骤

image-20231207165208761

RestClient操作文档

案例

image-20231207165510531

步骤1 初始化在操作索引库中已经完成

步骤2 新增文档

image-20231207170209772

 /**
     * 创建文档(倒排索引)
     * @throws IOException
     */
    @Test
    void AddHotelDocument() throws IOException {
        //根据id查询酒店数据
        Hotel hotel = hotelService.getById(47478L);
        //转化为文档类型
        HotelDoc hotelDoc = new HotelDoc(hotel);

        //1 准备request对象
        IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
        //2 准备json 文档
        //JSON.toJSONString(对象) :将对象序列化为Json
        request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);

        //3 发送请求
        client.index(request, RequestOptions.DEFAULT);
    }

步骤3 查询文档

image-20231208090020726

 /**
     * 查找文档
     * @throws IOException
     */
    @Test
    void getDocumentById() throws IOException {
        //1 准备请求
        GetRequest request = new GetRequest("hotel","47478");

        //2 发出响应,返回结果
        GetResponse response = client.get(request, RequestOptions.DEFAULT);

        //3 处理结果
        String json = response.getSourceAsString();
        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
        
        System.out.println(hotelDoc);
    }

步骤4 修改文档

image-20231208094144541

/**
     * 修改文档-局部更新
     */
    @Test
    void updateDocumentById() throws IOException {
        //1 创建request对象
        UpdateRequest request = new UpdateRequest("hotel","47478");

        //2 准备参数
        request.doc(
                "name","速8酒店(上海松江中心店)"
        );
        //3
        client.update(request, RequestOptions.DEFAULT);
    }

步骤5 删除文档

   /**
     * 删除文档
     */
    @Test
    void deleteDocumentById() throws IOException {
        //1 创建request对象
        DeleteRequest request = new DeleteRequest("hotel","47478");

        //2 删除
        client.delete(request, RequestOptions.DEFAULT);
    }

总结

image-20231208100227956

RestClient 批量导入Mysql数据到ES

image-20231208100728918

/**
     * 将mysql中酒店数据全导入es
     * @throws IOException
     */
    @Test
    void testBulkRequest() throws IOException {
        //批量查询酒店数据
        List<Hotel> hotels = hotelService.list();

        //1 创建Request
        BulkRequest request = new BulkRequest();
        //2 准备参数 添加多个Request对象
        for (Hotel hotel : hotels) {
            // 将hotel转成hotelDoc
            HotelDoc hotelDoc = new HotelDoc(hotel);
            // 创建新增文档的Request对象, add里面可以写Index,Delete等语句
            request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc), XContentType.JSON));
        }

        //3 发送请求
        client.bulk(request,RequestOptions.DEFAULT);
    }

ES 搜索功能

image-20231208104945541

详见官网文档:[Elasticsearch Guide 8.11] | Elastic

image-20231208105112677

DSL查询语法

image-20231208111932777

image-20231208112733340

全文检索查询

全文检索查询,会对用户输入内容分词,常用于搜索

image-20231208134340026

match查询:全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索

multi_match查询:根据多个字段查

查询语法:

###全文检索查询
#match查询  推荐使用 搜索的字段越多,查询的效率越低
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "外滩如家"
    }
  }
}
#multi_match查询
GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "外滩如家",
      "fields": ["all","city"]
    }
  }
}

小结

建议使用copyto,把多个要查的字段拷贝到一个字段中。如下 “all” 字段包含了 name,brand,business 三个字段

###酒店demo的mapping
PUT /hotel
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword" 
      },
      "name": {
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address": {
        "type": "keyword",
        "index": false 
      },
      "price": {
        "type": "integer"
      },
      "score": {
        "type": "integer"
      },
      "brand": {
        "type": "keyword",
        "copy_to": "all"
      },
      "city": {
        "type": "keyword"
      },
      "starName": {
        "type": "keyword"
      },
      "business": {
        "type": "keyword",
        "copy_to": "all"
      },
      "location": {
        "type": "geo_point"
      },
      "pic": {
        "type": "keyword",
        "index": false
      },
      "all": {
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

image-20231208150107178

精确查询

查keyword、数值、日期、boolean等字段,不分词

image-20231208151530660

image-20231208152825601

例如:

###精确检索: 不分词,搜到的跟给的一模一样
#term查询
GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "深圳上海"
      }
    }
  }
}
#range查询
GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 1108, 
        "lte": 3000
      }
    }
  }
}

小结

image-20231208152910423

地理查询

FIELD的类型为"geo_point"

image-20231208153021430

geo_bounding_box 查询(用的比较少),

#geo_bounding_box: 查询geo_point值落在某个矩形范围的所有文档
GET /hotel/_search
{
  "query": {
    "geo_bounding_box": {
      "location": {
        "top_left": {
          "lat": 31.1,
          "lon": 121.5
        },
        "bottom_right": {
          "lat": 30.9,
          "lon": 121.7
        }
      }
    }
  }
}

geo_distance 查询 (用的多)

image-20231208154829172

#geo_distance 查询
GET /hotel/_search
{
  "query": {
    "geo_distance": {
      "distance": "2km",
      "location": "31.21,121.5"
    }
  }
}

复合查询

前面全文检索查询,精确查询,地理查询统称为简单查询。复合查询是将简单查询组合起来,实现更复杂的搜索逻辑

image-20231208155943909

相关性算分

image-20231208164758391

image-20231208165026620

复合查询之Function score查询

使用Function Score Query 来人为地修改相关性算分(比如针对RMB玩家,让人家的相关性算分高一点)

image-20231211090310647

案例:给"如家"酒店排名靠前

image-20231211090618717

#function score查询
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "all": "外滩"
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "brand": "如家"
            }
          },
          "weight": 10
        }
      ],
      "boost_mode": "sum"
    }
  }
}
复合查询之Boolean 查询,,也叫过滤查询

image-20231211092025033

案例

image-20231211092338311

放到must中,会影响算分,算分的条件越多,性能越差,故把不重要的放到must_not中

#复合查询之bool查询  过滤查询
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "如家"
          }
        },
        {
          "geo_distance": {
            "distance": "10km",
            "location": {
              "lat": 31.21,
              "lon": 121.5
            }
          }
        }
      ],
      "must_not": [
        {
          "range": {
            "price": {
              "gt": 400
            }
          }
        }
      ]
    }
  }
}

放到must_not中不影响算分

GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "如家"
          }
        }
      ],
      "must_not": [
        {
          "range": {
            "price": {
              "gt": 400
            }
          }
        }
      ],
      "filter": [
        {
          "geo_distance": {
            "distance": "10km",
            "location": {
              "lat": 31.21,
              "lon": 121.5
            }
          }
        }
      ]
    }
  }
}

bool查询小结

image-20231211094717163

ES搜索结果处理

排序

image-20231211140019140

案例1:酒店数据按照用户评价降序排序,评价相同则按照价格升序排序

image-20231211140147600

#sort 排序
GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "score": "desc"
    },
    {
      "price": "asc" 
    }
  ]
}


GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "score": {
        "order": "desc"
      },
      "price": {
        "order": "asc"
      }
    }
  ]
}

案例2:看地图上哪些酒店离你近,做一个升序排序

image-20231211141013074

分页

from = (第几页 - 1) * size,从1开始

image-20231211143637998

GET /hotel/_search
{
  "query": {"match_all": {}},
  "sort": [{"price": "asc"}],
  "from": 9990  ,
  "size": 10
}

深度分页问题

image-20231211144336437

image-20231211144609867

es分页小结 (from+size用的最多)image-20231211144723210

高亮

把搜索结果中的关键字高亮显示

image-20231211145547781

#高亮,#默认标签是<em>。 默认情况下,ES搜索字段必须与高亮字段一致,但可以把"require_field_match" 设为false来实现,搜索字段与高亮字段不一致也可以高亮
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "如家"
    }
  }, 
  "highlight": {
    "fields": {
      "name": {
        "pre_tags": "<em>", # 这两句可以不写,因为Es默认就是这两句 
        "post_tags": "</em>", #
        "require_field_match": "false"
      }
    }
  }
}

ES搜索结果处理整体语法

image-20231211152616532

RestClient 查询文档

返回结果的字段解析

image-20231211100818326

image-20231211102145701

image-20231211102214992

查询

image-20231211102254998

match、multi_match、match_all

QueryBuilders.matchAllQuery()

image-20231211155008136

精确查询

image-20231211155115752

复合查询

 @Test
    void testBool() throws IOException {
        //1 准备request
        SearchRequest request = new SearchRequest("hotel");

        //2 准备DSL
        // 准备booleanQuery
        BoolQueryBuilder booledQuery = QueryBuilders.boolQuery();
        // 添加term
        booledQuery.must(QueryBuilders.termQuery("city","深圳"));

        booledQuery.filter(QueryBuilders.rangeQuery("price").lte(350));

        request.source().query(booledQuery);
        //3 发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //4 处理请求
        handleResponse(response);
    }

    private static void handleResponse(SearchResponse response) {
        //解析结果
        SearchHits searchHits = response.getHits();

        long total = searchHits.getTotalHits().value;

        System.out.println("一共搜索到" + total + "条数据");

        SearchHit[] hits = searchHits.getHits();

        for (SearchHit hit : hits) {

            String json = hit.getSourceAsString();

            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);

            System.out.println(hotelDoc);
        }
    }

要构建查询只需要记住QueryBuilders类就行了

排序和分页

对搜索结果的排序和分页是与查询在同级的参数

image-20231212084538794

 @Test
    void testPageAndSort() throws IOException {
        //页码, 每页大小
        int page = 2,size =5;

        //1 准备request
        SearchRequest request = new SearchRequest("hotel");

        //2 准备DSL
        //2.1 query
        request.source().query(QueryBuilders.matchAllQuery());
        //2.2 sort
        request.source().sort("price", SortOrder.ASC);
        //2.3 分页 from、size
        request.source().from((page - 1) * size).size(5);
        //3 发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //4 处理请求
        handleResponse(response);
    }

高亮

image-20231212085415363

image-20231212090003907

@Test
void testHighlight() throws IOException {
    //页码, 每页大小
    int page = 2,size =5;


    //1 准备request
    SearchRequest request = new SearchRequest("hotel");

    //2 准备DSL
    //2.1 query
    request.source().query(QueryBuilders.matchQuery("all","如家  "));

    //2.4 高亮 highlight
    request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
    //3 发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //4 处理请求
    handleHighlightResponse(response);
}
    
    
private static void handleHighlightResponse(SearchResponse response) {
    //4解析结果
    SearchHits searchHits = response.getHits();
    //4.1 获取总条数
    long total = searchHits.getTotalHits().value;
    System.out.println("一共搜索到" + total + "条数据");
    //4.2 文档数组
    SearchHit[] hits = searchHits.getHits();
    //4.3 遍历
    for (SearchHit hit : hits) {
        //获取文档source
        String json = hit.getSourceAsString();
        //反序列化
        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
        //获取高亮结果
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        if (!CollectionUtils.isEmpty(highlightFields)){
            //根据字段名获取高亮结果
            HighlightField highlightField = highlightFields.get("name");

            if (highlightField != null) {
                //获取高亮值
                String name = highlightField.getFragments()[0].string();
                //覆盖非高亮结果
                hotelDoc.setName(name);
            }
        }

        System.out.println(hotelDoc);
    }
}

image-20231212092641452

黑马旅游案例

  • 酒店搜索和分页
  • 酒店结果过滤
  • 我周边的酒店
  • 酒店竞价排名

启动hotel-deomo服务,进入http://localhost:8089/

酒店搜索与分页

image-20231212094215062

image-20231212094356048

image-20231212094615233

酒店结果过滤

image-20231212102657156

image-20231212102821233

image-20231212102946519

我附近的酒店

image-20231212105254338

image-20231212105446295

广告置顶

image-20231212112104789

#添加isAD字段
POST /hotel/_update/1908594080
{
  "doc": {
    "isAD": true
  }
}

POST /hotel/_update/1725781423
{
  "doc": {
    "isAD": true
  }
}

function score 组合查询Java语法
image-20240117103606838

你好!关于学习Elasticsearch,我可以给你一些指导。Elasticsearch是一个开源的分布式搜索和分析引擎,主要用于快速、实时地存储、搜索和分析大量数据。下面是一些学习Elasticsearch的步骤: 1. 了解基本概念:开始学习Elasticsearch之前,你需要了解一些基本的概念,比如索引(index)、类型(type)、文档(document)、字段(field)等。这将帮助你更好地理解Elasticsearch的工作原理。 2. 安装和配置:根据你的操作系统,你可以从Elasticsearch官方网站下载并安装合适的版本。安装完成后,你需要进行适当的配置,如设置集群名称、分配内存等。 3. 学习REST API:Elasticsearch提供了丰富的REST API,用于与其进行交互。了解如何使用这些API来索引、搜索和删除数据是学习Elasticsearch的重要一步。 4. 索引和搜索数据:学习如何创建索引、添加文档以及执行搜索操作是使用Elasticsearch的关键。掌握查询语法、过滤器、聚合操作等功能可以帮助你更有效地使用Elasticsearch。 5. 数据建模和分析:学习如何设计合适的数据模型和映射,以及如何使用Elasticsearch进行数据分析和可视化是提高你的技能的重要一步。 6. 扩展和优化:学习如何在生产环境中扩展和优化Elasticsearch集群是非常重要的。了解如何分片、复制、调优性能等将帮助你更好地管理和维护你的数据。 7. 学习资源:除了官方文档,还有很多优秀的学习资源可供参考,如书籍、教程和在线课程等。利用这些资源可以更系统地学习和掌握Elasticsearch。 希望这些步骤能对你学习Elasticsearch有所帮助!如果有任何问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值