Elasticsearch实战(一):Springboot实现Elasticsearch统一检索功能

这篇具有很好参考价值的文章主要介绍了Elasticsearch实战(一):Springboot实现Elasticsearch统一检索功能。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章索引

Elasticsearch实战(一):Springboot实现Elasticsearch统一检索功能
Elasticsearch实战(二):Springboot实现Elasticsearch自动汉字、拼音补全,Springboot实现自动拼写纠错
Elasticsearch实战(三):Springboot实现Elasticsearch搜索推荐
Elasticsearch实战(四):Springboot实现Elasticsearch指标聚合与下钻分析
Elasticsearch实战(五):Springboot实现Elasticsearch电商平台日志埋点与搜索热词

一、准备工作

1、搭建docker环境

快速搭建centos7虚拟机——使用virtualbox+vagrant
centos7安装与卸载docker-简单而详细无坑

2、安装es+kibana

docker安装elasticSearch+kibana

注意,我们本次使用的是elasticSearch7.4.0 + kibana7.4.0

3、es安装ik分词器

(1)下载安装

下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
找到对应版本的分词器:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.0/elasticsearch-analysis-ik-7.4.0.zip

# 将分词器插件拷贝到es容器
docker cp /root/elasticsearch-analysis-ik-7.4.0.zip 7f1456dff26d:/usr/share/elasticsearch/plugins
# 进入容器
docker exec -it cb5bffb16ac5 /bin/bash
# 安装zip命令
yum install -y unzip zip
# 创建目录
mkdir /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.0
# 解压
unzip -d /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.0 /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.0.zip
rm -f /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.0.zip
# 重启es
docker restart cb5bffb16ac5
# 查看日志
docker logs cb5bffb16ac5

(2)测试

GET _analyze
{
  "analyzer" : "ik_smart",
  "text" : "小米全面屏手机奥利给"
}


GET _analyze
{
  "analyzer" : "ik_max_word",
  "text" : "小米全面屏手机奥利给"
}

我们发现,执行结果,并不识别“全面屏” 、 “奥利给”。我们需要自定义分词。

cd /mydata/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.0/config
vi IKAnalyzer.cfg.xml

#修改内容:
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">my.dic</entry>

# 自定义分词
vi my.dic
# 内容:
奥利给
全面屏

# 重启es
docker restart cb5bffb16ac5

此时,奥利给 、 全面屏 这两个网络词语,就支持了。

4、Springboot

(1)引包

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.4.0</version>
</dependency>

(2)客户端公共方法

private final static String cluster_host = "192.168.56.10";
private final static Integer eNode3_port = 9200;
/**
 * 超时时间设为5分钟
 */
private static final int TIME_OUT = 5 * 60 * 1000;

private static final RestHighLevelClient client = highLevelClient();

private static RestClientBuilder restClientBuilder() {
    return RestClient.builder( // 可以传多个作为集群
            new HttpHost(cluster_host, eNode3_port, "http"));

}

/**
 * 获取客户端
 */
public static RestHighLevelClient highLevelClient() {
    RestClientBuilder restClientBuilder = restClientBuilder();
    restClientBuilder.setRequestConfigCallback(
            new RestClientBuilder.RequestConfigCallback() {
                @Override
                public RequestConfig.Builder customizeRequestConfig(
                        RequestConfig.Builder requestConfigBuilder) {
                    return requestConfigBuilder.setSocketTimeout(TIME_OUT);
                }
            });

    return new RestHighLevelClient(restClientBuilder);
}

(3)公共实体类

//如果加该注解的字段为null,那么就不序列化
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommonEntity implements Serializable {
    //页码
    private int pageNumber;
    //每页数据条数
    private int pageSize;
    //索引名称
    private String indexName;
    //高亮列
    private String highlight;
    //排序 DESC  ASC
    private String sortOrder;
    //排序列
    private String sortField;
    //自动补全建议列
    private String suggestFileld;
    //自动补全建议值
    private String suggestValue;
    //自动补全返回个数
    private Integer suggestCount;
    //动态查询参数封装(重要)
    Map<String, Object> map;
    //批量增加list
    private List<Map<String, Object>> list;

	// ... get  set
}
public class CommonMap<K,V> extends HashMap<K,V> {

    public CommonMap putData(K key, V value) {
        super.put(key, value);
        return this;
    }
}

(4)公共工具类

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @Class: SearchTools 查询服务工具类
 */
public class SearchTools {

    /*
     * @Description: 高亮前端显示组装,SearchResponse传递引用
     * 为什么二次处理高亮?
     * 原因:被设置的高亮列,es自动放到了highlight属性中;这个属性渲染了高亮的着色
     * 数据传输的时候,我们需要将它取出来
     * 覆盖到我们的_source中
     * @Method: setHighResult
     * @Param: [searchResponse, commonEntity]
     * @Update:
     * @since: 1.0.0
     * @Return: java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     *
     *
     */
    public static void setHighResultForCleintUI(SearchResponse searchResponse, String highlightField) {
        if (StringUtils.isNoneEmpty(highlightField)) {
            for (SearchHit hit : searchResponse.getHits()) {
                //获取高亮字段map
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                //获取到具体的高亮列
                HighlightField highlightFieldName = highlightFields.get(highlightField);
                //getSourceAsMap拿到具体的数据
                Map<String, Object> source = hit.getSourceAsMap();
                if (highlightFieldName != null) {
                    //获取渲染后的文本
                    Text[] fragments = highlightFieldName.fragments();
                    String name = "";
                    for (Text text : fragments) {
                        name += text;
                    }
                    source.put(highlightField, name);   //高亮字段替换掉原本的内容
                }
            }


        }
    }
    /*
     * @Description: 获取高亮构建器
     * @Method:
     * @Param:
     * @Update:
     * @since: 1.0.0
     * @Return:
     *
     */

    public static HighlightBuilder getHighlightBuilder(String highlightField) {

        // 设置高亮,使用默认的highlighter高亮器,默认em斜体
        HighlightBuilder highlightBuilder = new HighlightBuilder(); //生成高亮查询器
        highlightBuilder.field(highlightField);      //高亮查询字段
        highlightBuilder.requireFieldMatch(false);     //如果要多个字段高亮,这项要为false
        highlightBuilder.preTags("<span style= " +
                "color:red;font-weight:bold;font-size:15px;" +
                ">");   //高亮设置
        highlightBuilder.postTags("</span>");
        //下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
        highlightBuilder.fragmentSize(800000); //最大高亮分片数
        highlightBuilder.numOfFragments(0); //从第一个分片获取高亮片段
        return highlightBuilder;
    }


    /*
     * @Description: 获取排序  DESC  ASC 前端不区分大小写,默认返回DESC
     * @Method: getSortOrder
     * @Param: [sortOrder]
     * @Update:
     * @since: 1.0.0
     * @Return: org.elasticsearch.search.sort.SortOrder
     *
     */
    public static SortOrder getSortOrder(String sortOrder) {
        SortOrder so = null;
        sortOrder = StringUtils.isEmpty(sortOrder) ? "" : sortOrder.toLowerCase();
        switch (sortOrder) {
            case "desc":
                so = SortOrder.DESC;
                break;
            case "asc":
                so = SortOrder.ASC;
                break;
            default:
                so = SortOrder.DESC;
                break;
        }
        return so;
    }


    /*
     * @Description: MAP转数组
     * @Method: mapToObjectGropu
     * @Param: [data]
     * @Update:
     * @since: 1.0.0
     * @Return: java.lang.Object[]
     *
     */
    public static Object[] mapToObjectGroup(Map<String, Object> data) {
        List<Object> args = new ArrayList<Object>();
        if (data != null) {
            data.forEach((key, value) -> {
                args.add(key);
                args.add(value);
            });
        }

        return args.toArray();
    }

    /*
     * @Description: 根据客户端传来的查询参数(标准的DSL语句)构建XContentParser
     * @Method: getXContentParser
     * @Param: []
     * @Update:
     * @since: 1.0.0
     * @Return: org.elasticsearch.common.xcontent.XContentParser
     *
     */
    public static XContentParser getXContentParser(CommonEntity commonEntity) throws IOException {

        //构建SearchModule对象置 ,通过构造器注册解析器、建议器、排序等
        SearchModule searchModule = new SearchModule(Settings.EMPTY, false, Collections.emptyList());
        //获取注册成功的注册解析器、建议器、排序
        NamedXContentRegistry registry = new NamedXContentRegistry(searchModule.getNamedXContents());
        //将前端传来的DSL参数通过解析解解析
        XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(registry, LoggingDeprecationHandler.INSTANCE, JSONObject.toJSONString(commonEntity.getMap()));
        return parser;
    }

    /*
     * @Description: 将查询出来的数据放到本地局部线程变量中
     * @Method: setResponseThreadLocal
     * @Param: [response]
     * @Update:
     * @since: 1.0.0
     * @Return: void
     *
     */
    public static void setResponseThreadLocal(SearchResponse response) {
        //查询出来的数据
        SearchHit[] sh = response.getHits().getHits();
        //定义list用来接收所有Resource下面的结果集
        List<JSONObject> list = new ArrayList<JSONObject>();
        if (sh != null) {
            for (SearchHit hit : sh) {
                list.add(JSONObject.parseObject(hit.getSourceAsString()));
            }
            //将数据放入到本地线程
            ResponseThreadLocal.set(list);
        }
    }

}
/**
 * @Description: 使用线程本地局部变量处理结果集
 */
public class ResponseThreadLocal {
    private static final ThreadLocal<List<JSONObject>> threadLocal = new ThreadLocal<List<JSONObject>>();

    /*
     * @Description: 通过本地线程局部变量获取结果集
     * @Method: getList
     * @Param: []
     * @Update:
     * @since: 1.0.0
     * @Return: java.util.List<com.alibaba.fastjson.JSONObject>
     *
     */
    public static List<JSONObject> get() {
        return threadLocal.get();
    }

    /*
     * @Description:
     * @Method: 将统计后的数据集放入到当前线程
     * @Param: [list]
     * @Update:
     * @since: 1.0.0
     * @Return: void
     *
     */
    public static void set(final List<JSONObject> list) {
        threadLocal.set(list);
    }

    /*
     * @Description:
     * @Method: 清空当前线程本地局部变量值;否则内存泄露
     * @Param: []
     * @Update:
     * @since: 1.0.0
     * @Return: void
     *
     */
    public static void clear() {
        threadLocal.set(null);
    }

}

5、数据准备

(1)添加映射

PUT product
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
    
      },
      "price": {
        "type": "double"
      } 
    }
  }
}

或者使用代码:

/*
 * @Description: 新增索引+setting+映射+自定义分词器pinyin
 * setting可以为空(自定义分词器pinyin在setting中)
 * 映射可以为空
 * @Method: addIndexAndMapping
 * @Param: [commonEntity]
 * @Return: boolean
 *
 */
public boolean addIndexAndMapping(CommonEntity commonEntity) throws Exception {
    //设置setting的map
    Map<String, Object> settingMap = new HashMap<String, Object>();
    //创建索引请求
    CreateIndexRequest request = new CreateIndexRequest(commonEntity.getIndexName());
    //获取前端参数
    Map<String, Object> map = commonEntity.getMap();
    //循环外层的settings和mapping
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        if ("settings".equals(entry.getKey())) {
            if (entry.getValue() instanceof Map && ((Map) entry.getValue()).size() > 0) {
                request.settings((Map<String, Object>) entry.getValue());
            }
        }
        if ("mapping".equals(entry.getKey())) {
            if (entry.getValue() instanceof Map && ((Map) entry.getValue()).size() > 0) {
                request.mapping((Map<String, Object>) entry.getValue());
            }

        }
    }
    //创建索引操作客户端
    IndicesClient indices = client.indices();
    //创建响应对象
    CreateIndexResponse response = indices.create(request, RequestOptions.DEFAULT);
    //得到响应结果
    return response.isAcknowledged();
}

(2)批量添加数据

/*
 * @Description: 批量新增文档,可自动创建索引、自动创建映射
 * @Method: bulkAddDoc
 * @Param: [indexName, map]
 *
 */
public static RestStatus bulkAddDoc(CommonEntity commonEntity) throws Exception {
    //通过索引构建批量请求对象
    BulkRequest bulkRequest = new BulkRequest(commonEntity.getIndexName());
    //循环前台list文档数据
    for (int i = 0; i < commonEntity.getList().size(); i++) {
        bulkRequest.add(new IndexRequest().source(XContentType.JSON, SearchTools.mapToObjectGroup(commonEntity.getList().get(i))));
    }
    //执行批量新增
    BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    return bulkResponse.status();
}

public static void main(String[] args) throws Exception {
	// 批量插入
    CommonEntity commonEntity = new CommonEntity();
    commonEntity.setIndexName("product"); // 索引名
    List<Map<String, Object>> list = new ArrayList<>();
    commonEntity.setList(list);
    list.add(new CommonMap<String, Object>().putData("name", "小米(MI)10手机全面屏").putData("price", 2233.8));
    list.add(new CommonMap<String, Object>().putData("name", "小米(MI)11手机划超酷炫").putData("price", 13123));
    list.add(new CommonMap<String, Object>().putData("name", "小米(MI)电脑大da屏").putData("price", 213));
    list.add(new CommonMap<String, Object>().putData("name", "华为手机全面屏大").putData("price", 342));
    list.add(new CommonMap<String, Object>().putData("name", "华为手机大屏大").putData("price", 1234));
    list.add(new CommonMap<String, Object>().putData("name", "华为电脑全面屏大奥利给").putData("price", 345));
    list.add(new CommonMap<String, Object>().putData("name", "华为平板电脑全面屏大奥利给").putData("price", 1234));
    list.add(new CommonMap<String, Object>().putData("name", "荣耀小米手机").putData("price", 45234));
    list.add(new CommonMap<String, Object>().putData("name", "手机平板全面屏").putData("price", 4532));
    bulkAddDoc(commonEntity);
}

查询一下,发现有九条数据了:

GET product/_search

二、全文检索

1、代码

/*
 * @Description: 全文检索
 * 使用matchQuery在执行查询时,搜索的词会被分词器分词
 * @Method: searchMatch
 * @Param: [indexName, key, value]
 * >>>>>>>>>>>>编写思路简短总结>>>>>>>>>>>>>
 * >>>>>>>1、构建远程查询
 * >>>>>>>2、构建查询请求
 * >>>>>>>3、构建查询条件
 * >>>>>>>4、设置高亮
 * >>>>>>>5、设置分页
 * >>>>>>>   加入SearchRequest
 * >>>>>>>6、处理高亮
 *
 */
public static SearchResponse matchQuery(CommonEntity commonEntity) throws Exception {
    //构建查询响应
    SearchResponse response = null;
    //构建查询请求用来完成和搜索文档,聚合,建议等相关的任何操作同时也提供了各种方式来完成对查询结果的高亮操作。
    SearchRequest searchRequest = new SearchRequest(commonEntity.getIndexName());
    //构建DSL请求体;trackTotalHits如果不设置true,查询数据最大值还是10000
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().trackTotalHits(true);
    //获取前端的查询条件(Map查询条件)
    getClientConditions(commonEntity, searchSourceBuilder);
    //高亮设置
    searchSourceBuilder.highlighter(SearchTools.getHighlightBuilder(commonEntity.getHighlight()));
    //前端页码
    int pageNumber = commonEntity.getPageNumber();
    //前端每页数量
    int pageSize = commonEntity.getPageSize();
    //计算查询的下标,从0开始
    int dest = (pageNumber - 1) * pageSize;
    searchSourceBuilder.from(dest);
    //每页数量
    searchSourceBuilder.size(pageSize);
    //查询条件对象放入请求对象中
    searchRequest.source(searchSourceBuilder);
    //方法执行开始时间
    long startTime = System.currentTimeMillis();
    System.out.println("开始Elasticsearch查询...");
    //执行远程查询,使用RequestOptions.DEFAULT用来构建一个默认缓冲区限制为100MB(源码为DEFAULT_BUFFER_LIMIT = 100 * 1024 * 1024),和header为空、WarningsHandler为空
    //的参数选项
    response = client.search(searchRequest, RequestOptions.DEFAULT);
    //计算远程查询耗时
    System.out.println("结束Elasticsearch查询总耗时:" + (System.currentTimeMillis() - startTime) + "毫秒");
    //处理高亮
    SearchTools.setHighResultForCleintUI(response, commonEntity.getHighlight());
    return response;

}

/*
 * @Description: 获取前端的查询条件
 * @Method: getClientConditions
 * @Param: [commonEntity, searchSourceBuilder]
 *
 */
private static void getClientConditions(CommonEntity commonEntity, SearchSourceBuilder searchSourceBuilder) {
    //循环前端的查询条件
    for (Map.Entry<String, Object> m : commonEntity.getMap().entrySet()) {
        if (StringUtils.isNotEmpty(m.getKey()) && m.getValue() != null) {
            String key = m.getKey();
            String value = String.valueOf(m.getValue());
            //构造请求体中“query”:{}部分的内容 ,QueryBuilders静态工厂类,方便构造queryBuilder
            //将搜索词分词,再与目标查询字段进行匹配,若分词中的任意一个词与目标字段匹配上,则可查询到。
            searchSourceBuilder.query(QueryBuilders.matchQuery(key, value));
            System.out.println(("search for the keyword:" + value));
        }
    }
}
public static void main(String[] args) throws Exception {
	// 全文检索
    CommonEntity queryEntity = new CommonEntity();
    queryEntity.setPageNumber(1);// 第一页
    queryEntity.setPageSize(5); // 一页条数
    queryEntity.setIndexName("product"); // 索引名
    queryEntity.setHighlight("name"); // 高亮字段
    queryEntity.setMap(new CommonMap<>().putData("name", "华为全面屏")); // 要查询的字段 + 内容
    SearchResponse searchResponse = matchQuery(queryEntity);
    long aSize = searchResponse.getHits().getTotalHits().value;
    System.out.println(("数据总数量为>>>" + aSize));
    long cSize = searchResponse.getHits().getHits().length;
    System.out.println(("本次获取数据量为>>>" + cSize));
    System.out.println("内容为>>>" + JSON.toJSONString(searchResponse.getHits().getHits()));
    System.out.println("全部内容>>>" + JSON.toJSON(searchResponse));
}

2、为什么二次处理高亮

原因:被设置的高亮列,es自动放到了highlight属性中;这个属性渲染了高亮的着色
数据传输的时候,我们需要将它取出来
覆盖到我们的_source中

三、结构化搜索与过滤

1、概述

实现查询价格在【2000-3000】并且是【京东物流】并且评论数【从大到小进行排序】的商品,filter也常和range范围查询一起结合使用,range范围可供组合的选项。

注意!被查询的字段类型是必须是keyword,这样字段在索引时不会进行分词。如果类型为text,字段值在索引时会分词,这样反而查不到结果了。

GET product/_search
{
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "storetype": "自营"
                    }
                },
                {
                    "term": {
                        "twolevel": "手机"
                    }
                }
            ],
            "filter": {
                "range": {
                    "price": {
                        "gte": 2000,
                        "lte": 3000
                    }
                }
            }
        }
    },
    "sort": [
        {
            "evalcount": {
                "order": "desc"
            }
        }
    ]
}

结构化查询(Query DSL)
query的时候,会先比较查询条件,然后计算分值,最后返回文档结果
结构化过滤(Filter DSL)
过滤器,对查询结果进行缓存,不会计算相关度,避免计算分值,执行速度非常快

2、结构化过滤(Filter DSL)

(1)term 过滤

term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型),相当于sql age=26

{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}

(2)terms 过滤

terms 允许指定多个匹配条件。如果某个字段指定了多个值,那么文档需要一起去做匹配。
相当于sql: age in

{"terms": {"age": [26, 27, 28]}}

(3)range 过滤

range 过滤允许我们按照指定范围查找一批数据:

{
    "range": {
        "price": {
            "gte": 2000,
            "lte": 3000
        }
    }
}

gt : 大于
lt : 小于
gte : 大于等于
lte :小于等于
相等于sql between

(4)exists 和 missing 过滤

exists 和 missing 过滤可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的IS_NULL条件

{
	"exists": {
		"field": "title"
	}
}

(5)bool 过滤

用来合并多个过滤条件查询结果的布尔逻辑:
must:多个查询条件的完全匹配,相当于 and。
must_not: 多个查询条件的相反匹配,相当于 not;
should:至少有一个查询条件匹配,相当于 or;
相当于sql and 和or

{
    "bool": {
        "must": {
            "term": {
                "folder": "inbox"
            }
        },
        "must_not": {
            "term": {
                "tag": "spam"
            }
        },
        "should": [
            {
                "term": {
                    "starred": true
                }
            },
            {
                "term": {
                    "unread": true
                }
            }
        ]
    }
}

3、结构化查询(Query DSL)

(1)bool 查询

bool 查询与 bool 过滤相似,用于合并多个查询子句。不同的是,bool 过滤可以直接给出是否匹配成功, 而bool 查询要计算每一个查询子句的 _score

{
    "bool": {
        "must": {
            "match": {
                "title": "how to make millions"
            }
        },
        "must_not": {
            "match": {
                "tag": "spam"
            }
        },
        "should": [
            {
                "match": {
                    "tag": "starred"
                }
            },
            {
                "range": {
                    "date": {
                        "gte": "2014-01-01"
                    }
                }
            }
        ]
    }
}

(2)bool嵌套查询

{
    "bool": {
        "should": [
            {
                "term": {
                    "productID": "KDKE-B-9947-#kL5"
                }
            },
            {
                "bool": {
                    "must": [
                        {
                            "term": {
                                "productID": "JODL-X-1937-#pV7"
                            }
                        },
                        {
                            "term": {
                                "price": 30
                            }
                        }
                    ]
                }
            }
        ]
    }
}

(3)match_all 查询

使用match_all 可以查询到所有文档,是没有查询条件下的默认语句。

{
	"match_all": {}
}

(4)match 查询

match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析match一下查询字符

{
	"match": {
		"tweet": "About Search"
	}
}

(5)multi_match 查询

multi_match查询允许你做match查询的基础上同时搜索多个字段

{
	"multi_match": {
		"query": "full text search",
		"fields": [ "title", "body" ]
	}
}

(6)match_phrase

短语查询,full text search 是一个词组,意味着三个词的位置是连续且有顺序

{
	"match_phrase": {
		"title": "full text search",
	}
}

设置slop词组间隔

{
	"match_phrase": {
		"title": {
			"query": "full text search",
			"slop":1
		}
	}
}

(7)phrase_prefix 查询

与词组中最后一个词条进行前缀匹配。

{
"query": {
	"match_phrase_prefix": {
		"title": {
			"query": "全面屏"
		}
	}
},
"from":0,
"size":5
}

(8)regexp查询

通配符查询

{
    "query": {
        "regexp": {
            "title": "W[0-9].+"
        }
    }
}

(9)过滤查询

查询语句和过滤语句可以放在各自的上下文中,filtered已弃用,用bool代替。

{
    "query": {
        "bool": {
            "must": {
                "match": {
                    "text": "quick brown fox"
                }
            },
            "filter": {
                "term": {
                    "status": "published"
                }
            }
        }
    },
    "from": 0, // 从0开始
    "size": 10, // 显示条数
    "sort": {
        "publish_date": {
            "order": "desc"
        }
    }
}

4、Java实现通用结构化查询API

(1)Java实现

/*
 * @Description:结构化搜索
 * @Method: termQuery
 * @Param: [commonEntity]
 * @Update:
 * @since: 1.0.0
 * @Return: org.elasticsearch.action.search.SearchResponse
 * >>>>>>>>>>>>编写思路简短总结>>>>>>>>>>>>>
 * 1、构建远程查询
 * 2、定义响应
 * 3、定义查询请求
 * 3、定义查询构建器
 * 4、定义解析器--构建器解析
 * 5、定义高亮
 * 6、定义分页
 * 7、定义排序
 *    加入到SearchRequest
 * 8、高亮渲染
 */
public static SearchResponse termQuery(CommonEntity commonEntity) throws Exception {

    //构建查询响应
    SearchResponse response = null;
    //构建查询请求用来完成和搜索文档,聚合,建议等相关的任何操作同时也提供了各种方式来完成对查询结果的高亮操作。
    SearchRequest searchRequest = new SearchRequest(commonEntity.getIndexName());
    //构建DSL请求体trackTotalHits如果不设置true,查询数据最大值还是10000
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().trackTotalHits(true);
    //将前端的dsl查询转化为XContentParser
    XContentParser parser = SearchTools.getXContentParser(commonEntity);
    //将parser解析成功查询API
    searchSourceBuilder.parseXContent(parser);
    //高亮设置
    searchSourceBuilder.highlighter(SearchTools.getHighlightBuilder(commonEntity.getHighlight()));
    //前端页码
    int pageNumber = commonEntity.getPageNumber();
    //前端每页数量
    int pageSize = commonEntity.getPageSize();
    //计算查询的下标
    int dest = (pageNumber - 1) * pageSize;
    searchSourceBuilder.from(dest);
    //每页数量
    searchSourceBuilder.size(pageSize);
    //排序
    sort(commonEntity, searchSourceBuilder);
    //查询条件对象放入请求对象中
    searchRequest.source(searchSourceBuilder);
    //方法执行开始时间
    long startTime = System.currentTimeMillis();
    System.out.println("开始Elasticsearch查询...");
    //执行远程查询
    response = client.search(searchRequest, RequestOptions.DEFAULT);
    //计算远程查询耗时
    System.out.println("结束Elasticsearch查询总耗时:" + (System.currentTimeMillis() - startTime) + "毫秒");
    //处理高亮
    SearchTools.setHighResultForCleintUI(response, commonEntity.getHighlight());
    return response;
}

/*
 * @Description: 排序
 * @Method: sort
 * @Param: [commonEntity, searchSourceBuilder]
 */
private static void sort(CommonEntity commonEntity, SearchSourceBuilder searchSourceBuilder) {
    String sortField = commonEntity.getSortField();
    if (StringUtils.isNotEmpty(sortField)) {
        //排序,获取前端的order by子句,不区分大小写,参数为空则默认desc
        SortOrder sortOrder = SearchTools.getSortOrder(commonEntity.getSortOrder());
        //执行排序
        searchSourceBuilder.sort(new FieldSortBuilder(commonEntity.getSortField()).order(sortOrder));
    }
}

public static void main(String[] args) throws Exception {
    // 结构化查询
    CommonEntity queryEntity = new CommonEntity();
    SearchResponse result = termQuery(queryEntity);
    //查询数量除以每页数量  等于合计分页数量
    long aSize = result.getHits().getTotalHits().value;
    System.out.println(("总数据量:" + aSize + "条"));
    int cSize = result.getHits().getHits().length;
    System.out.println(("当前获取数据:" + cSize + "条"));
    //通过类型推断自动装箱(多个参数取交集)
    System.out.println(result.getHits().getHits()); // 结果
}

(2)排序方式一

{
    "pageNumber": 1,
    "pageSize": 1,
    "indexName": "product",
    "sortField": "evalcount",
    "sortOrder": "",
    "highlight": "name",
    "map": {
        "query": {
            "bool": {
                "must": [
                    {
                        "term": {
                            "storetype": "自营"
                        }
                    },
                    {
                        "term": {
                            "twolevel": "手机"
                        }
                    }
                ],
                "filter": {
                    "range": {
                        "price": {
                            "gte": 2000,
                            "lte": 3000
                        }
                    }
                }
            }
        }
    }
}

pageNumber:页码
pageSize:每页显示条数
indexName:不可以为空,查询的索引名称
sortField:排序列,可以为空
sortOrder:可以为空,默认DESC,排序规则【DESC/ASC】
highlight:高亮字段(注意:平台只接受被查询的字段名称)
map:里面的参数为动态DSL参数;可以随意增加(需符合ES规范)系统可自动解析文章来源地址https://www.toymoban.com/news/detail-655164.html

(3)排序方式二

{
    "pageNumber": 1,
    "pageSize": 1,
    "indexName": "product_list_info",
    "sortField": "",
    "sortOrder": "",
    "highlight": "productname",
    "map": {
        "query": {
            "bool": {
                "must": [
                    {
                        "term": {
                            "storetype": "自营"
                        }
                    },
                    {
                        "term": {
                            "twolevel": "手机"
                        }
                    }
                ],
                "filter": {
                    "range": {
                        "price": {
                            "gte": 2000,
                            "lte": 3000
                        }
                    }
                }
            }
        },
        "sort": [
            {
                "evalcount": {
                    "order": "desc"
                }
            }
        ]
    }
}

到了这里,关于Elasticsearch实战(一):Springboot实现Elasticsearch统一检索功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot+Elasticsearch实现word,pdf,txt内容抽取并高亮分词全文检索

    文章目录 需求 一、环境 二、功能实现 1.搭建环境 2.文件内容识别 三.代码         产品希望我们这边能够实现用户上传PDF,WORD,TXT之内得文本内容,然后用户可以根据附件名称或文件内容模糊查询文件信息,并可以在线查看文件内容         项目开发环境:           

    2023年04月09日
    浏览(47)
  • ElasticSearch 实战:ElasticSearch文档全文检索

    Elasticsearch 实战:Elasticsearch 文档全文检索 全文检索是 Elasticsearch 的核心功能之一,它允许用户对文本内容进行高效的模糊搜索、词组匹配、同义词处理、停用词过滤等操作。以下是如何进行文档全文检索的详细步骤: **1. **全文匹配查询(Match Query) 最基础的全文检索查询是

    2024年04月11日
    浏览(52)
  • Elasticsearch搜索功能的实现(五)-- 实战

    实战环境 elastic search 8.5.0 + kibna 8.5.0 + springboot 3.0.2 + spring data elasticsearch 5.0.2 + jdk 17 实现效果图片: 实际执行的DSL语句: 注意: 当指定排序条件时 _score 会被置空 加权前效果: 加权后效果: DSL 语句:

    2023年04月18日
    浏览(35)
  • 全文检索-Elasticsearch-整合SpringBoot

    前面记录了 Elasticsearch 全文检索的入门篇和进阶检索。这次我们来讲下 Spring Boot 中如何整合 ES,以及如何在 Spring Cloud 微服务项目中使用 ES 来实现全文检索,来达到商品检索的功能。 检索服务单独作为一个服务,就称作 gulimall-search 模块。 点击 Next 勾选 Spring Web 依赖,点击

    2024年02月08日
    浏览(50)
  • uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -全局异常统一处理实现

    锋哥原创的uniapp微信小程序投票系统实战: uniapp微信小程序投票系统实战课程 (SpringBoot2+vue3.2+element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibili uniapp微信小程序投票系统实战课程 (SpringBoot2+vue3.2+element plus ) ( 火爆连载更新中... )共计21条视频,包括:uniapp微信小程序投票系统实

    2024年02月03日
    浏览(42)
  • 【Elasticsearch】SpringBoot整合ES实现搜索功能 | 高亮显示

    先看代码: controller: serviceImpl: 小结 : 1、添加ES场景启动器 2、yaml配置ES 3、准备需要用到的变量 注:还有一个注入的RestHighLevelClient 结构如下: 具体调用的方法以及设置页码等参看代码。 加断点查看对应searchResponse数据结构: HighlightFields的数据结构: 对照kinaba结果: 3、根

    2024年02月11日
    浏览(46)
  • springboot集成elasticsearch,实现搜索提示补全功能

    注:在此之前需要下载elasticsearch以及拼音分词器。可以查看这篇文章,免费下载,下载完直接解压就行。 https://download.csdn.net/download/weixin_47874230/86514890 spring-data-elasticsearch版本需要与springboot版本对应,此处使用springboot版本为2.2.13.RELEASE

    2024年02月09日
    浏览(42)
  • Java实战:SpringBoot+ElasticSearch 实现模糊查询

    本文将详细介绍如何使用SpringBoot整合ElasticSearch,实现模糊查询、批量CRUD、排序、分页和高亮功能。我们将深入探讨ElasticSearch的相关概念和技术细节,以及如何使用SpringData Elasticsearch库简化开发过程。 ElasticSearch是一个基于Lucene构建的开源搜索引擎,它提供了一个分布式、多

    2024年04月25日
    浏览(36)
  • ElasticSearch 7.X系列之: 检索性能优化实战指南

    检索响应慢! 并发检索用户多时,响应时间不达标 卡死了! 怎么还没有出结果? 怎么这么慢? 为啥竞品产品的很快就返回结果了? 宕机了 等等...... 这些都与可能检索有关,确切的说和检索性能有关。 检索性能的优化涉及知识点比较零散,我以官方文档的检索性能优化部

    2023年04月08日
    浏览(51)
  • Langchain 与 Elasticsearch:创新数据检索的融合实战

    在信息爆炸的时代,有效地检索和处理数据变得至关重要。Langchain 和 Elasticsearch 的结合,为我们提供了一个强大的工具,以更智能的方式进行数据检索和分析。 作为一名拥有多年 Elasticsearch 实战经验的技术博主,我将在本文中详细介绍这两种技术的整合应用。 Langchain是一个

    2024年01月19日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包