分布式搜索引擎elasticsearch

elasticsearch:一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析
elastic stack(ELK):是以elasticsearch为核心的技术栈

elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK),被广泛应用在日志数据分析、实时监控等领域
elasticsearch是elastic stack的核心,负责存储、搜索、分析数据
kibana  数据可视化
Logstash、Beats  数据抓取

elasticsearch采用倒排索引

文档:每条数据就是一个文档
词条:文档按照语义分成的词语
id      title      price
1     小米手机  3499
2     华为手机   4999
3   华为小米充电器 49


词条         文档
小米        1,3,4
手机        1,2
华为        2,3
充电器     3


elasticsearch是面向文档存储的,文档数据会被序列化json格式后存储到elasticsearch

索引:相同类型的文档集合
映射:索引中文档的字段约束信息,类似表结构约束

MySQL   Elasticsearch
table表      index
row行        document
column列    field
schema约束  mapping
SQL               DSL(实现CRUD)


MySQL:擅长事务类型操作,可以确保数据的安全和一致性
Elasticsearch:擅长海量数据的搜索、分析、计算

Elasticsearch内置的分词对中文处理不太友好,一般使用IK分词器引

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

RestClient:封装DSL语句,通过http请求发送给ES


RestClient操作索引库
初始化
private RestHighLevelClient client;

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

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

创建索引库
 // 1.创建Request对象
    CreateIndexRequest request = new CreateIndexRequest("hotel");
    // 2.准备请求的参数:DSL语句
    request.source(MAPPING_TEMPLATE, XContentType.JSON);
    // 3.发送请求
    client.indices().create(request, RequestOptions.DEFAULT);

删除索引库
 // 1.创建Request对象
    DeleteIndexRequest request = new DeleteIndexRequest("hotel");
    // 2.发送请求
    client.indices().delete(request, RequestOptions.DEFAULT);

判断索引库是否存在
 // 1.创建Request对象
    GetIndexRequest request = new GetIndexRequest("hotel");
    // 2.发送请求
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);

RestClient操作文档

新增文档
 //根据id查询mysql数据
        Hotel hotel = iHotelService.getById(60363L);
        //转换为文档类型
        HotelDoc hotelDoc=new HotelDoc(hotel);
        //准备Request对象
        IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
        //准备json文档
        request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);
        //发送请求
        client.index(request,RequestOptions.DEFAULT);

查询文档
 //根据id查询mysql数据
       //准备Request对象
        GetRequest request = new GetRequest("hotel", "60363");
        //发送请求,得到响应
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        //解析响应结果
        String json = response.getSourceAsString();

        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
删除文档
DeleteRequest request = new DeleteRequest("hotel", "60363");
        client.delete(request,RequestOptions.DEFAULT);


更新文档
 //准备request
        UpdateRequest request = new UpdateRequest("hotel", "60363");
        //准备请求参数
        request.doc(
                "price","1345"
        );
        //发送请求
        client.update(request,RequestOptions.DEFAULT);

批量导入文档
// 批量查询酒店数据
    List<Hotel> hotels = hotelService.list();

    // 1.创建Request
    BulkRequest request = new BulkRequest();
    // 2.准备参数,添加多个新增的Request
    for (Hotel hotel : hotels) {
        // 2.1.转换为文档类型HotelDoc
        HotelDoc hotelDoc = new HotelDoc(hotel);
        // 2.2.创建新增文档的Request对象
        request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc), XContentType.JSON));
    }
    // 3.发送请求
    client.bulk(request, RequestOptions.DEFAULT);

DSL查询文档
- 查询所有:查询出所有数据,一般测试用。例如:match_all
- 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
  - match_query
  - multi_match_query
- 精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
  - ids
  - range
  - term
- 地理(geo)查询:根据经纬度查询。例如:
  - geo_distance
  - geo_bounding_box
- 复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件例如:
  - bool
  - function_score

检索所有
GET /hotel/_search
{
  "query": {
    "match_all": {}
  }
}

全文检索
GET /hotel/_search
{
  "query": {
    "match": {
      "city": "外滩"
    }
  }
}

精确查询
range(查询根据值的范围查询)
term(根据词条精确值)
GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "上海"
      }

GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 500
      }

地理算分
GET /hotel/_search
{
  "query": {
    "geo_distance":{
      "distance":"15km",
      "location":"31.21,121.5"
}


复合查询:将上述各种查询条件组合起来
fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {  .... }, // 原始查询,可以是任意条件
      "functions": [ // 算分函数
        {
          "filter": { // 满足的条件,品牌必须是如家
            "term": {
              "brand": "如家"
            }
          },
          "weight": 2 // 算分权重为2
        }
      ],
      "boost_mode": "sum" // 加权模式,求和
    }


bool:布尔查询是一个或多个查询子句的组合
must:必须匹配每个子查询,类似与
should:选择性匹配子查询,类似或
must_not:必须不匹配
filter:必须匹配,不参与算分

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

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

分页
ES默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了
ES通过修改from、size参数控制要返回的分页结果
from 分页开始的位置,默认为0 (page-1)*pagesize
size  期望获取的文档总数   pagesize

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

高亮
在搜索结果中把搜索关键字突出显示
默认情况下,ES搜索字段必须与高亮字段一致
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "外滩"
    }
  },
  "highlight": {
    "fields": {
      "name": {//指定高亮的字段
        "pre_tags":"<em>",//用来标记高亮字段的前置标签
        "post_tafs":"</em>"//用来标记高亮字段的后置标签
      }
    }
  }
}


RestClient查询文档
// 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    request.source()
        .query(QueryBuilders.matchAllQuery());
    // 3.发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    // 4.解析响应
    handleResponse(response);
}

private void handleResponse(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);
        System.out.println("hotelDoc = " + hotelDoc);


match查询
 request.source()
        .query(QueryBuilders.matchQuery("all", "如家"));

精确查询
QueryBuilders.termQuery("city","杭州")
QueryBuilders.rangeQuery("price").gte(100).lte(150)


布尔查询
  // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    // 2.1.准备BooleanQuery
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    // 2.2.添加term
    boolQuery.must(QueryBuilders.termQuery("city", "杭州"));
    // 2.3.添加range
    boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));
    request.source().query(boolQuery);

分页排序
  // 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);

高亮
  // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        // 2.准备DSL
        // 2.1.query
        request.source().query(QueryBuilders.matchQuery("all", "如家"));
        // 2.2.高亮
        request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));

自动补全
PUT /hotel
{
  "settings": {
    "analysis": {
      "analyzer": {
        "text_anlyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        },
        "completion_analyzer": {
          "tokenizer": "keyword",
          "filter": "py"
        }
      },
      "filter": {
        "py": {
          "type": "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,
          "keep_first_letter" :false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id":{
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "text_anlyzer",
        "search_analyzer": "ik_smart",
        "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": "text_anlyzer",
        "search_analyzer": "ik_smart"
      },
      "suggestion":{
          "type": "completion",
          "analyzer": "completion_analyzer"
      }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值