分布式搜索引擎elasticsearch(二)

一、elasticsearch的DSL语法

1、查询语法的分类

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

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

  2)全文检索查询:利用分词器对用户的输入进行分词,然后使用倒排索引。适用语法:

        match_query:针对单个条件(字段类型进行匹配),也就是分词的数据匹配一个字段类型

        multi_match_query:针对多个条件(字段类型进行匹配),也就是分词的数据匹配多个字段类型。

3)精确查询:根据词条精准查询,一般是不分词的。比如keyword,数值、日期、boolean等类型字段。比如:

        ids:查询多个id。

        range:范围匹配。

        term:精准一对一匹配。

4)地理查询,根据经纬度匹配:

        geo_distance:

        geo_bounding_box:

5)复合查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。

        bool:多个查询条件进行组合查询。

        function_score:针对符合条件的文档进行分数的额外处理,以致于达到查询结果达到自定义的展示顺序。

2、基本语法

查询语句:GET /indexName/_search

1)查询所有     match_all   

2)全文检索查询

a、match_query:通过分词进行查找

一般是根据单个字段类型来进行查找,所以一般查找的都是复合字段(all)。

b、multi_match:根据多个字段查询,参与查询字段越多,查询性能越差。

3)精确查找

        定义:精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。

a、term:根据词条精确值查询

b、range:根据值的范围查询

        gte --- 大于等于      gt ---大于

        lte --- 小于等于        lt---小于

4)地理查询

        根据经纬度查询。        

        常见的使用场景包括: 携程:搜索我附近的酒店 滴滴:搜索我附近的出租车 微信:搜索我附近的人。

       

geo_bounding_box:查询geo_point值落在某个矩形范围的所有文档也就是选择两个点(对角线关系),根据这两个点得到一个矩形。

    

geo_distance:查询到指定中心点小于某个距离值的所有文档

5)复合查询

        由于某个搜索需要符合多个条件,这个时候就需要将我们上述的搜索条件进行一次拼接查询。

复合查询 Boolean Query,布尔查询是一个或多个查询子句的组合。

子查询的组合方式有:

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

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

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

filter:必须匹配,不参与算分

        上述语句含义就是:城市是上海,且酒店是皇冠假日或者华美达的。价格大于500,并且评分要大于45。

案例:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。

 

6)fuction score:算分函数查询

        算分函数查询,可以控制文档相关性算分,控制文档排名。例如百度竞价等。

   a、相关性算分

  elasticsearch中的相关性打分算法是什么?

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

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

        当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。

        例如,我们搜索 "虹桥如家",结果如下:

              

        根据结果我们可以看到,越接近查询条件的词条,分数越高,排名越高。

        这个分数我们可以人为自定义控制,将一些我们需要的数据分数提高,从而使得其排名提高,达到我们的自定义展示效果。

        使用 function score query,可以修改文档的相关性算分(query score),根据新得到的算分排序。

解释:

        "function_score" : 表示的是这个是一个算分查询

                 "query":查询条件,第一次筛选出符合条件的初始数据。

                 "functions":表示进行算分操作。

                          "filter":根据筛选条件得到需要进行算分的数据。

                          "weight":给一个常量,作为函数结果。

                  "boost_mode":定义function score(算分结果)和query score(查询结果)的运算方式。

案例:给“如家”这个品牌的酒店排名靠前一些

分析:1)找到所有的酒店  2)是如家酒店的加上10

如果字段类型是text,就无法使用 term这些精确匹配。否则查询为空。

function score query定义的三要素是什么?

过滤条件:哪些文档要加分

算分函数:如何计算function  score      

加权方式:function score 与 query score如何运算   "boost_mode"

7)搜索结果处理

 1、排序

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

案例:给价格进行升序排序,评分降序排序

2、分页

 

深度分页问题

        ES是分布式的,所以会面临深度分页问题。例如按price排序后,获取from = 990,size =10的数据:

1、首先在每个数据分片上都排序并查询前1000条文档。

2、然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档。

3、最后从这1000条中,选取从990开始的10条文档。

        如果搜索页数过深,或者结果集(from + size)越大,对内存和CPU的消耗也越高。因此ES设定结果集查询的上限是10000。

from+size 分页

优点:支持随机翻页

缺点:深度分页问题,默认查询上限(from + size)是10000

场景:百度、京东、谷歌、淘宝这样的随机翻页搜索

after search 分页

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

缺点:只能向后逐页查询,不支持随机翻页

场景:没有随机翻页需求的搜索,例如手机向下滚动翻页

scroll 分页

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

缺点:会有额外内存消耗,并且搜索结果是非实时的

场景:海量数据的获取和迁移。从ES7.1开始不推荐,建议用 after search方案。

3、高亮

        

3、代码实战

1) match 简单查询
package com.hihonor.controller;

import com.alibaba.fastjson.JSONObject;
import com.hihonor.bean.Hotel;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Map;

@RestController
@RequestMapping("elastic/dsl")
public class ElasticDslController {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @PostMapping("/match")
    public void matchAll() throws IOException {
        // 指定查询的索引
        SearchRequest request = new SearchRequest("myhotel");


        // 查询所有
        MatchAllQueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();
        // 执行查询语句
        request.source().query(matchAllQuery);
        SearchResponse responseMatchAll = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        showSearchString(responseMatchAll, 0);


        // 查询单个字段
        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("name", "如家");
        // 执行查询语句
        request.source().query(matchQuery);
        SearchResponse responseMatch = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        showSearchString(responseMatch, 0);


        // 查询多个字段
        MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery("上海如家", "city", "brand");
        // 执行查询语句
        request.source().query(multiMatchQuery);
        SearchResponse responseMultiMatch = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        showSearchString(responseMultiMatch, 0);

        // term精确查询
        TermQueryBuilder termQuery = QueryBuilders.termQuery("id", "36934");
        request.source().query(termQuery);
        SearchResponse responseTerm = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        showSearchString(responseTerm, 0);
        // 范围查询
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price").gte(336);
        request.source().query(rangeQuery);
        SearchResponse responseRange = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        showSearchString(responseRange, 0);
    }



    /**
     * 查询结果进行展示
     *
     * @param responseMatchAll 查询得到的结果
     */
    private void showSearchString(SearchResponse responseMatchAll, int highLight) {
        System.out.println("查询的数据个数是"+responseMatchAll.getHits().getTotalHits());
        SearchHit[] hits = responseMatchAll.getHits().getHits();
        for (SearchHit hit : hits) {
            Hotel hotel = JSONObject.parseObject(hit.getSourceAsString(), Hotel.class);
            // highLight为1,进行一下高亮处理展示
            if (highLight == 1) {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if (CollectionUtils.isEmpty(highlightFields)) {
                    continue;
                }
                HighlightField highlightField = highlightFields.get("name");
                String name = highlightField.getFragments()[0].toString();
                hotel.setName(name);
                System.out.println("高亮展示"+hotel.toString());
                continue;
            }
            String sourceAsString = hit.getSourceAsString();
            System.out.println(sourceAsString);
        }
    }

}
2)boolQuery---复合查询
package com.hihonor.controller;

import com.alibaba.fastjson.JSONObject;
import com.hihonor.bean.Hotel;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Map;

@RestController
@RequestMapping("elastic/dsl")
public class ElasticDslController {
    @Autowired
    private RestHighLevelClient restHighLevelClient;


    // 复合查询
    @PostMapping("/boolQuery")
    public void boolQuery() throws IOException {
        SearchRequest request = new SearchRequest("myhotel");
        // 复合条件查询
        // 搜索名字包含“如家”,价格不高于400的酒店。
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 搜索名字包含“如家”
        boolQueryBuilder.must(QueryBuilders.matchQuery("name", "如家"));
        // 价格不高于400
        boolQueryBuilder.mustNot(QueryBuilders.rangeQuery("price").gt(400));
        request.source().query(boolQueryBuilder);
        // 按照评分降序
        request.source().sort("score", SortOrder.DESC);
        // 分页  查询前 10 条数据
        request.source().from(0).size(10);
        SearchResponse boolQuery =         
        restHighLevelClient.search(request,RequestOptions.DEFAULT);
        showSearchString(boolQuery, 0);
    }


    /**
     * 查询结果进行展示
     *
     * @param responseMatchAll 查询得到的结果
     */
    private void showSearchString(SearchResponse responseMatchAll, int highLight) {
        System.out.println("查询的数据个数是"+responseMatchAll.getHits().getTotalHits());
        SearchHit[] hits = responseMatchAll.getHits().getHits();
        for (SearchHit hit : hits) {
            Hotel hotel = JSONObject.parseObject(hit.getSourceAsString(), Hotel.class);
            // highLight为1,进行一下高亮处理展示
            if (highLight == 1) {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if (CollectionUtils.isEmpty(highlightFields)) {
                    continue;
                }
                HighlightField highlightField = highlightFields.get("name");
                String name = highlightField.getFragments()[0].toString();
                hotel.setName(name);
                System.out.println("高亮展示"+hotel.toString());
                continue;
            }
            String sourceAsString = hit.getSourceAsString();
            System.out.println(sourceAsString);
        }
    }

}
3)functionQuery 算分查询
package com.hihonor.controller;

import com.alibaba.fastjson.JSONObject;
import com.hihonor.bean.Hotel;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Map;

@RestController
@RequestMapping("elastic/dsl")
public class ElasticDslController {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    // 算分查询   查询 名字是 如家,id是36934的分数加上30
    @PostMapping("/functionQuery")
    public void functionQuery() throws IOException {
        SearchRequest request = new SearchRequest("myhotel");
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("name", "如家"));

        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders
                .functionScoreQuery(
                        boolQueryBuilder,
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                        QueryBuilders.termQuery("id", "441836"), ScoreFunctionBuilders.weightFactorFunction(30))
                        });

        request.source().query(functionScoreQuery);

        SearchResponse functionResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        showSearchString(functionResponse, 0);
    }



    /**
     * 查询结果进行展示
     *
     * @param responseMatchAll 查询得到的结果
     */
    private void showSearchString(SearchResponse responseMatchAll, int highLight) {
        System.out.println("查询的数据个数是"+responseMatchAll.getHits().getTotalHits());
        SearchHit[] hits = responseMatchAll.getHits().getHits();
        for (SearchHit hit : hits) {
            Hotel hotel = JSONObject.parseObject(hit.getSourceAsString(), Hotel.class);
            // highLight为1,进行一下高亮处理展示
            if (highLight == 1) {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if (CollectionUtils.isEmpty(highlightFields)) {
                    continue;
                }
                HighlightField highlightField = highlightFields.get("name");
                String name = highlightField.getFragments()[0].toString();
                hotel.setName(name);
                System.out.println("高亮展示"+hotel.toString());
                continue;
            }
            String sourceAsString = hit.getSourceAsString();
            System.out.println(sourceAsString);
        }
    }

}
4)高亮查询展示
package com.hihonor.controller;

import com.alibaba.fastjson.JSONObject;
import com.hihonor.bean.Hotel;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Map;

@RestController
@RequestMapping("elastic/dsl")
public class ElasticDslController {
    @Autowired
    private RestHighLevelClient restHighLevelClient;


    // 高亮查询
    @PostMapping("/matchHighLight")
    public void matchHighLight() throws IOException {
        // 指定查询的索引
        SearchRequest request = new SearchRequest("myhotel");
        // term精确查询
        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("name", "如家");
        request.source().query(matchQuery);

        // 高亮的语句   高亮字段是哪个   是否需要和拆线呢字段匹配(也就是本方法内的id)
        request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
        SearchResponse responseTerm = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        showSearchString(responseTerm, 1);

    }


    /**
     * 查询结果进行展示
     *
     * @param responseMatchAll 查询得到的结果
     */
    private void showSearchString(SearchResponse responseMatchAll, int highLight) {
        System.out.println("查询的数据个数是"+responseMatchAll.getHits().getTotalHits());
        SearchHit[] hits = responseMatchAll.getHits().getHits();
        for (SearchHit hit : hits) {
            Hotel hotel = JSONObject.parseObject(hit.getSourceAsString(), Hotel.class);
            // highLight为1,进行一下高亮处理展示
            if (highLight == 1) {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if (CollectionUtils.isEmpty(highlightFields)) {
                    continue;
                }
                HighlightField highlightField = highlightFields.get("name");
                String name = highlightField.getFragments()[0].toString();
                hotel.setName(name);
                System.out.println("高亮展示"+hotel.toString());
                continue;
            }
            String sourceAsString = hit.getSourceAsString();
            System.out.println(sourceAsString);
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值