ElasticSearch搜索引擎

目录

1.ElasticSearch简介

2.ElasticSearch与MySQL区别

3.ElasticSearch核心概念介绍

3.1索引(Index)

3.1.1添加索引

3.1.2查询索引

3.1.3删除索引

3.2域(Field)

3.2.1数据类型

3.2.2添加映射

3.3文档(Document)

3.3.1添加文档

3.3.2查询文档

3.3.3修改文档

3.3.4删除文档

3.4倒排索引

4.Springboot整合ElasticSearch

4.1批量导入操作

4.2查询操作

4.2.1查询所有

4.2.2分页查询所有

4.2.3精确搜索

4.2.4模糊查询

4.2.5多域搜索

4.2.6搜索提示

4.2.7范围查询

4.2.8多条件查询

4.2.9纠错查询

4.2.10高亮展示

4.2.11加权查询


1.ElasticSearch简介

ElasticSearch是一个开源的、支持分布式的、能够进行近实时搜索和分析、能够处理PB级数据量的搜索引擎。目前有很多平台和工作场景会涉及到对ElasticSearch的使用。

2.ElasticSearch与MySQL区别

  • Mysql数据操作具备事务性,而ElasticSearch没有

  • MySQL支持外键,而ElasticSearch不支持

  • Mysql采用B+树索引,而ElasticSearch采用倒排索引

3.ElasticSearch核心概念介绍

ES在使用时,会涉及到五大核心概念:索引(Index)、映射(Mapping)、域(Field)、文档(Documen)、倒排索引。以一张MySQL中数据表为例:

ElasticSearchMysql
索引(Index)表(Table)
映射(Mapping)表结构
域(Field)字段列(Column)
文档(Document)一条数据(Row)

3.1索引(Index)

索引相当于关系型数据库中的一张表,一个index中包含若干个document,通过index代替一类类似的或相同的document。

3.1.1添加索引

# PUT 索引名称
PUT person

3.1.2查询索引

#查询单个索引 GET 索引名称

GET person

# 查询多个索引信息 GET 索引名称,索引名称
PUT person1
GET person,person1

# 查询所有索引信息
GET _all

3.1.3删除索引

#删除索引 DELETE 索引名称
DELETE person1

3.2域(Field)

域(Field)相当于数据表中的字段列,当有了索引之后,需要在索引中设置域的相关信息如:名称,类型等。这个过程称为:映射(Mapping)。相当于关系型数据库中设置表的信息。

3.2.1数据类型

字符串

  • text:会进行分词,如小米手机,会分成:小米,手机。 被分出来的每一个词,称为term(词条)

  • keyword:不会进行分词,如小米手机,只有一个词条,即小米手机。

数值

  • long:带符号64位整数

  • integer:带符号32位整数

  • short:带符号16位整数

  • byte:带符号8位整数

  • double:双精度64位浮点数

  • float:单精度32位浮点数

  • half_float:半精度16位浮点数

布尔:

  • boolean

二进制:

  • binary

日期:

  • date

范围类型:

  • integer_range

  • float_range

  • long_range

  • double_range

  • date_range

数组

对象

3.2.2添加映射

(1)为已存在的索引库添加映射

PUT person/_mapping
{
  "properties":{
    "name":{
      "type":"text"
    },
    "age":{
      "type":"integer"
    }
  }
}

#查看索引映射信息
GET person/_mapping

(2)创建索引并添加映射

#创建索引并添加映射
PUT person
{
  "mappings": {
    "properties": {
      "name":{
        "type": "text"
      },
      "age":{
        "type": "integer"
      }
    }
  }
}

(3)添加字段

对于映射,只能进行字段添加,不能对字段进行修改或删除,如有需要,则重新创建映射。

#添加字段
PUT person/_mapping
{
  "properties":{
    "name": {
        "type": "text"
      },
      "age": {
        "type": "integer"
      },
      "address":{
        "type":"text"
      }
  }
}

3.3文档(Document)

ES中最小的数据单元,代表索引中的一条数据,通常是使用json的数据格式表示的

3.3.1添加文档

(1)添加文档,手动设置id

#添加文档,手动设置id
POST person/_doc/1
{
  "name":"朱吴",
  "age":"23",
  "address":"南昌"
}

(2)添加文档,自动生成id

#添加文档,自动生成id
POST person/_doc
{
  "name":"朱徐",
  "age":"25",
  "address":"深圳"
}

3.3.2查询文档

(1)根据id查询文档

#GET 索引名称/_doc/文档id
GET person/_doc/1

(2)查询所有文档

#GET 索引名称/_search
GET person/_search

3.3.3修改文档

PUT person/_doc/1
{
  "name":"朱武",
  "age":"26",
  "address":"深圳"
}

3.3.4删除文档

#DELETE 索引名称/_doc/文档id
DELETE person/_doc/1

3.4倒排索引

要想理解倒排索引,首先思考一个问题,获取某个文件夹下所有文件名中包含Spring的文件

1)确定要搜索的文件夹
2)遍历文件夹下所有文件
3)判断文件名中是否包含Spring

这种思维可以理解为是一种正向思维的方式,从外往内,根据key找value。这种方式可以理解为正向索引。

而ElasticSearch为了提升查询效率,采用反向思维方式,根据value找key。

4.Springboot整合ElasticSearch

4.1批量导入操作

索引及映射准备

PUT hotel
{
  "mappings": {
    "properties": {
      
      "name":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "address":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "brand":{
        "type": "keyword"
      },
      "type":{
        "type": "keyword"
      },
       "price":{
        "type": "integer"
      },
      "specs":{
        "type": "keyword"
      },
       "salesVolume":{
        "type": "integer"
      },
      "area":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "imageUrl":{
        "type": "text"
      },
      "synopsis":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "createTime":{
        "type": "date",
        "format": "yyyy-MM-dd"
      },
      "isAd":{
        "type":"integer"
      }
    }
  }
}

在ES中提供了批量操作的方式:例如

#新增一号用户
#删除一号用户
#新增二号用户
POST _bulk
{"create":{"_index":"user","_id":"1"}}
{"name":"张三","age":18,"address":"北京"}
{"delete":{"_index":"user","_id":"1"}}
{"create":{"_index":"user","_id":"2"}}
{"name":"李四","age":20,"address":"黑龙江"}

在代码中,批量导入时,从MySQL中查出所有数据后批量导入ES

    //批量导入
    @Override
    public int addDocToES() {

        //todo 批量导入文档实现

        // 1.创建批量导入对象
        BulkRequest bulkRequest = new BulkRequest();
        //2.从MySQL数据库中查出所有数据
        List<HotelEntity> hotelList = hotelMapper.selectList(null);

        //3.遍历,使用带有转换日期的JSON序列化并将数据加入到创建的导入对象中
        for (HotelEntity hotelEntity : hotelList) {
            //3.1将实体进行序列化
            String data = JSON.toJSONStringWithDateFormat(hotelEntity, "yyyy-MM-dd", SerializerFeature.WriteDateUseDateFormat);
            //3.2 实例化IndexRequest并设置索引名,文档内容(对象的JSON格式)
            IndexRequest indexRequest = new IndexRequest("hotel").source(data, XContentType.JSON);
            //3.3加入批量批量导入对象中
            bulkRequest.add(indexRequest);
        }

        try {
            //4.通过restHighLevelClient调用bulk方法,开启执行批量操作
            BulkResponse responses = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            return responses.status().getStatus();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

4.2查询操作

4.2.1查询所有

GET hotel/_search
{
  "query": {
    "match_all": {}
  }
}

    //查询全部
    /*
        GET hotel/_search
        {
          "query": {
            "match_all": {}
          }
        }
     */
    @Override
    public Map<String, Object> matchAllQuery() {

         //todo 查询全部文档实现
        //1.创建搜索请求对象SearchRequest
        SearchRequest searchRequest = new SearchRequest("hotel");
        //2.创建searchSourceBuilder,进行筛选条件填充
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //2.1构建查询方式
        QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        searchSourceBuilder.query(queryBuilder);
        searchRequest.source(searchSourceBuilder);
        //3.通过restHighLevelClient调用search方法,开启查询
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //4.处理查询结果
            //4.1获取查询结果
            SearchHits hits = searchResponse.getHits();
            long totalHits = hits.getTotalHits().value;
            SearchHit[] hits1 = hits.getHits();
            //4.2,反序列化后添加到集合中
            List<HotelEntity>list = new ArrayList<>();
            for (SearchHit searchHit : hits1) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            //4.3封装成map
            Map<String,Object> map = new HashMap<>();
            map.put("list",list);
            map.put("totalResultSize",totalHits);

            return map;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.2分页查询所有

GET hotel/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 5
}

    public Map<String, Object> pageQuery(int current, int size) {
        //todo 分页查询文档实现
        //1.创建搜索请求对象SearchRequest
        SearchRequest searchRequest = new SearchRequest("hotel");
        //2.创建SearchSourceBuilder,进行筛选条件填充
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //2.1构建查询方式
        MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        searchSourceBuilder.query(queryBuilder);
        //2.2设置分页
        searchSourceBuilder.from((current-1)*size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        //3.执行查询
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
            //4.处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long hitsTotal = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit searchHit : hits){
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", hitsTotal);
            map.put("current", current);
            //设置总页数
            map.put("totalPage",(hitsTotal+size-1)/size);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.3精确搜索

termQuery:不会对查询条件进行分词(keyword)

GET hotel/_search
{
  "query": {
    "term": {
      "brand": {
        "value": "万豪"
      }
    }
  }
}
GET hotel/_search
{
  "query": {
    "term": {
      "brand": "万豪"
    }
  }
}

public Map<String, Object> brandTermQuery(int current, int size, Map<String, Object> searchParam) {

        //todo 按品牌精确查询实现
        //1.构建查询请求对象SearchRequest
        SearchRequest searchRequest = new SearchRequest("hotel");
        //2.创建SearchSourceBuilder,进行筛选条件填充
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //2.1构建查询方式
        if(!StringUtils.isEmpty(searchParam.get("brand"))){
            QueryBuilder queryBuilder = QueryBuilders.termQuery("brand", searchParam.get("brand").toString());
            searchSourceBuilder.query(queryBuilder);
        }
        //2.2设置分页
        searchSourceBuilder.from((current - 1)*size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        //3.执行查询
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //4.处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long hitsTotal = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }
            //5.封装返回结果
            Map<String,Object>  map = new HashMap<>();
            map.put("list",list);
            map.put("totalResultSize",hitsTotal);
            map.put("current",current);
            //6.设置总页数
            map.put("totalPage",(hitsTotal+size-1)/size);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

4.2.3分词匹配查询

GET hotel/_search
{
  "query": {
    "match": {
     "name":"北京市东城区万豪酒店"
    }
  }
}

     public Map<String, Object> nameMatchQuery(Integer current, Integer size, Map<String, Object> searchParam) {
       
        //todo 根据酒店名称匹配查询实现

        //1.构建查询请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //2.创建SearchSourceBuilder,进行筛选条件填充
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //3.查询条件构造
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", searchParam.get("name"));
        searchSourceBuilder.query(queryBuilder);
        //4.设置分页
        searchSourceBuilder.from((current-1)*size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        try {
            //5.执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //6.处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            //6.1命中结果总数
            long hitsTotal = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            //6.2.封装返回结果
            Map<String,Object> map = new HashMap<>();
            map.put("list",list);
            map.put("totalResultSize",hitsTotal);
            map.put("current",current);
            //6.3.设置总页数
            map.put("totalPage",(hitsTotal+size-1)/size);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.4模糊查询

wildcardQuery:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)

GET hotel/_search
{
  "query": {
    "wildcard": {
      "brand": {
        "value": "美*"
      }
    }
  }
}

    public Map<String, Object> nameWildcardQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        //todo 根据酒店名称模糊查询
        //1.构建查询对象searchRequest
        SearchRequest searchRequest = new SearchRequest("hotel");
        //2.构造searchSourceBuilder,构造DSL
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        WildcardQueryBuilder queryBuilder = QueryBuilders.wildcardQuery("brand", searchParam.get("name") + "*");
        sourceBuilder.query(queryBuilder);
        //3.设置分页
        sourceBuilder.from((current-1)*size);
        sourceBuilder.size(size);
        searchRequest.source(sourceBuilder);
        try {
            //4.执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //5.处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long hitsTotal = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            //6.封装返回信息
            Map<String,Object> map = new HashMap<>();
            map.put("list",list);
            map.put("totalResultSize", hitsTotal);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (hitsTotal + size - 1) / size);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.5多域搜索

queryStringQuery

•会对查询条件进行分词。

•然后将分词后的查询条件和词条进行等值匹配

•默认取并集(OR)

•可以指定多个查询字段

GET hotel/_search
{
  "query": {
    "query_string": {
      "fields": ["name","synopsis","area","address"],
      "query": "如心 OR spa"
    }
  }
}

    public Map<String, Object> searchQueryStringQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //构建查询对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //构建searchSourceBuilder,设置DSL
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //根据name,synopsis,area,address进行多域查询
        QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                .field("name")
                .field("synopsis")
                .field("area")
                .field("address")
                .defaultOperator(Operator.OR);
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current-1)*size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);

        try {
            //执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            //封装返回信息
            Map<String,Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.6搜索提示

(1)新建用于存储用户搜索记录的索引库

PUT suggest
{
  "mappings": {
    "properties": {
      "name":{
        "type": "completion",
        "analyzer": "ik_max_word"
      }
    }
  }    
}

(2)数据查询

GET suggest/_search
{
  "suggest": {
    "my-suggest": {
      "prefix": "酒店环境",
      "completion": {
        "field": "name",
        "size":10
      }
    }
  }
}

GET suggest/_search
{
  "suggest": {
    "my-suggest": {
      "prefix": "酒店",
      "completion": {
        "field": "name",
        "size":10
      }
    }
  }
}

    public List<String> searchSuggestInfo(String key) {

        //定义结果集
        List<String> result = new ArrayList<>();
        //构建查询对象
        SearchRequest searchRequest = new SearchRequest("suggest");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //构建自动补全搜索
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        CompletionSuggestionBuilder suggestion= SuggestBuilders.completionSuggestion("name").text(key).size(10);
        suggestBuilder.addSuggestion("my-suggest",suggestion);
        searchSourceBuilder.suggest(suggestBuilder);
        searchRequest.source(searchSourceBuilder);
        try {
            //执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //处理自动补全查询结果
            Suggest suggest = searchResponse.getSuggest();
            CompletionSuggestion  completionSuggestion = suggest.getSuggestion("my-suggest");
            List<CompletionSuggestion.Entry> entries = completionSuggestion.getEntries();

            Map<String, Object> map = new HashMap<>();
            if (entries!=null && entries.size()>0){
                entries.forEach(entry -> {
                    if (!ObjectUtils.isEmpty(entry.getOptions())){
                        entry.forEach(action -> {
                            String suggestInfo = action.getText().toString();
                            if (suggestInfo.equals(key)){
                                return;
                            }
                            result.add(suggestInfo);
                        });
                    }else {
                        map.put("name",key);
                    }
                });
            }
            //向索引库增量添加查询条件
            if (!ObjectUtils.isEmpty(map)) {
                IndexRequest indexRequest = new IndexRequest("suggest").source(map);
                restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);
            }
            System.out.println("list" + result);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

4.2.7排序查询

#降序
GET hotel/_search
{
  "sort": [
    {
      "salesVolume": {
        "order": "desc"
      }
    }
  ]
}

#升序
GET hotel/_search
{
  "sort": [
    {
      "salesVolume": {
        "order": "asc"
      }
    }
  ]
}

    public Map<String, Object> salesSortQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        
        //构建查询请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //构建SearchSourceBuilder,构建DSL
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        if ("desc".equalsIgnoreCase(searchParam.get("sortWay").toString())){
            searchSourceBuilder.sort("salesVolume", SortOrder.DESC);
        }else {
            searchSourceBuilder.sort("salesVolume",SortOrder.ASC);
        }
        //设置分页
        searchSourceBuilder.from((current-1)*size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);

        try {
            //执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            //map封装
            Map<String,Object> map = new HashMap<>();
            map.put("list",list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);
            map.put("sortWay",searchParam.get("sortWay"));
            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.7范围查询

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

    public Map<String, Object> priceRangeQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建查询请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //创建SearchSourceBuilder,设置DSL
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price")
                .gte(searchParam.get("minPrice"))
                .lte(searchParam.get("maxPrice"));
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current-1)*size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        try {
            //执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity>list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            //map封装
            Map<String,Object> map = new HashMap<>();
            map.put("list",list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            map.put("minPrice", searchParam.get("minPrice"));
            map.put("maxPrice", searchParam.get("maxPrice"));

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.8多条件查询

boolQuery:对多个查询条件连接。

连接方式:

must(and):条件必须成立

must_not(not):条件必须不成立

should(or):条件可以成立

filter:条件必须成立,性能比must高。

#must单独使用 品牌必须是万豪,地区必须是北京
GET hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "brand": {
              "value": "万豪"
            }
          }
        },
        {
          "term": {
            "area": {
              "value": "北京市"
            }
          }
        }
      ]
    }
  }
}

# must与filter组合使用 查询品牌为万豪下的,区域为北京,价格范围在500和2000之间的酒店
GET hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "brand": {
              "value": "万豪"
            }
          }
        }
      ],
      "filter":[ 
        {
        "term": {
          "area": "北京市"
        }
       },
       {
         "range":{
          "price": {
            "gte": 500,
            "lte": 2000
         }
         }
       }
      ]
    }
  }
}

#filter 单独使用   filter可以是单个条件,也可多个条件(数组形式)
GET hotel/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "brand": {
              "value": "万豪"
            }
          }
        }
      ]
    }
  }
}

    public Map<String, Object> searchBoolQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建查询请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //创建 SearchSourceBuilder 对象,设置DSL
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //多条件查询 :多域、品牌精确、城市精确、星级精确、价格范围、销量排序
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //多域
        if(!ObjectUtils.isEmpty(searchParam.get("condition"))){
            QueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                    .field("name")
                    .field("synopsis")
                    .field("area")
                    .field("address")
                    .defaultOperator(Operator.OR);
            boolQueryBuilder.must(queryStringQueryBuilder);
        }

        //品牌精准
        if(ObjectUtils.isNotEmpty(searchParam.get("brand"))){
            QueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", searchParam.get("brand"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //城市精确
        if (ObjectUtils.isNotEmpty(searchParam.get("area"))) {
            QueryBuilder termQueryBuilder = QueryBuilders.termQuery("area", searchParam.get("area"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //星级精确
        if (ObjectUtils.isNotEmpty(searchParam.get("specs"))) {
            QueryBuilder termQueryBuilder = QueryBuilders.termQuery("specs", searchParam.get("specs"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //价格范围
        if(ObjectUtils.isNotEmpty(searchParam.get("minPrice")) && !StringUtils.isEmpty(searchParam.get("maxPrice"))){
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price")
                    .gte(searchParam.get("minPrice"))
                    .lte(searchParam.get("maxPrice"));
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        //销量排序
        if (ObjectUtils.isNotEmpty(searchParam.get("sortWay"))){
            if("desc".equalsIgnoreCase(searchParam.get("sortWay").toString())){
                searchSourceBuilder.sort("salesVolume",SortOrder.DESC);
            }else {
                searchSourceBuilder.sort("salesVolume",SortOrder.ASC);
            }
        }

        searchSourceBuilder.query(boolQueryBuilder);
        //设置分页
        searchSourceBuilder.from((current-1)*size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        try {
            //执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            map.put("brand", searchParam.get("brand"));
            map.put("area", searchParam.get("area"));
            map.put("specs", searchParam.get("specs"));
            map.put("sortWay", searchParam.get("sortWay"));
            map.put("minPrice", searchParam.get("minPrice"));
            map.put("maxPrice", searchParam.get("maxPrice"));

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.9纠错查询

fuzzyQuery

​ 自动纠错查询,会自动尝试将搜索条件进行纠错,然后去跟term进行匹配。

  • fuzziness:查询允许的最大编辑距离,默认为0

  • prefix_length:设置前几个字符不允许编辑

GET hotel/_search
{
  "query": {
    "fuzzy": {
      "area": {
        "value": "北经市",
        "fuzziness": 1,
        "prefix_length": 1
      }
    }
  }
}

4.2.10高亮展示

如需将搜索条件以高亮形式展示,则需要在查询时,设置需要对哪一个域进行高亮展示

fields:指定要对哪个域高亮显示

pre_tags:设置高亮样式前缀

post_tags:设置高亮样式后缀

GET hotel/_search
{
  "query": {
    "term": {
      "name": {
        "value": "北京市"
      }
    }
  },
  "highlight": {
    "fields": {
      "name": {
        "pre_tags": "<font color = 'red'>",
        "post_tags": "</font>"
      }
    }
  }
}

    public Map<String, Object> searchHighLight(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建查询请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //创建SearchSourceBuilder对象,设置查询条件DSL
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        if (ObjectUtils.isNotEmpty(searchParam.get("condition"))){
            QueryBuilder queryBuilder = QueryBuilders.termQuery("name", searchParam.get("condition"));
            searchSourceBuilder.query(queryBuilder);
        }
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);
        //设置高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.preTags("<font color = 'red'>");
        highlightBuilder.postTags("</font>");

        searchSourceBuilder.highlighter(highlightBuilder);
        searchRequest.source(searchSourceBuilder);
        try {
            //执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //处理查询结果
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits){
                String sourceAsString = hit.getSourceAsString();
                HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
                //处理高亮结果
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField!=null){
                    Text[] fragments = highlightField.getFragments();
                    hotelEntity.setName(fragments[0].toString());
                }
                list.add(hotelEntity);
            }
            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
4.2.11加权查询

权重设置的两种方式:索引设置,查询设置

索引设置:不常用,因为随着业务的改变,要设置权重的域也会发生改变,而索引一旦创建则无法修改,除非删除索引重建。

索引设置:

PUT hotel
{
  "mappings": {
    "properties": {
      
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "boost": 5
      },
      "address":{
        "type": "text",
        "analyzer": "ik_max_word",
        "boost": 3
      }
    }
  }
}

查询设置:

该方式在开发中很常用,根据业务条件需求,在查询时灵活的配置权重。

​ 在下列查询中,query中的内容为主查询条件,functions中为判断要为哪些数据加权。weight为加权值。

GET hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "query_string": {
          "fields": ["name","synopsis","area","address"],
          "query": "北京市万豪spa三星"
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "isAd": "1"
            }
          },
          "weight": 100
        }
      ]
    }
  }
}

    public Map<String, Object> searchScoreQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建查询请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //创建SearchSourceBuilder,设置DSL
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                .field("name")
                .field("synopsis")
                .field("area")
                .field("address")
                .defaultOperator(Operator.OR);

        //构建加权条件

        FunctionScoreQueryBuilder.FilterFunctionBuilder[] scoreFunctionBuilders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAd", 1), ScoreFunctionBuilders.weightFactorFunction(100))
        };
        FunctionScoreQueryBuilder queryBuilder = QueryBuilders.functionScoreQuery(queryStringQueryBuilder, scoreFunctionBuilders);
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current-1)*size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);

        try {
            //执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits().value;
            SearchHit[] hits = searchHits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString,HotelEntity.class));
            }
            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值