• ElasticSearch从入门到精通(二)


    ElasticSearch 高级操作

    bulk批量操作

    批量操作-脚本

    1. #批量操作
    2. #1.删除5
    3. #新增8
    4. #更新2号 name为2
    5. POST _bulk
    6. {"delete":{"_index":"person1","_id":"5"}}
    7. {"create":{"_index":"person1","_id":"8"}}
    8. {"name":"八号","age":18,"address":"北京"}
    9. {"update":{"_index":"person1","_id":"2"}}
    10. {"doc":{"name":"2号"}}

    批量操作-java

    1. /**
    2. * Bulk 批量操作
    3. */
    4. @Test
    5. public void test2() throws IOException {
    6. //创建bulkrequest对象,整合所有操作
    7. BulkRequest bulkRequest =new BulkRequest();
    8. /*
    9. # 1. 删除5号记录
    10. # 2. 添加6号记录
    11. # 3. 修改3号记录 名称为 “三号”
    12. */
    13. //添加对应操作
    14. //1. 删除5号记录
    15. DeleteRequest deleteRequest=new DeleteRequest("person1","5");
    16. bulkRequest.add(deleteRequest);
    17. //2. 添加6号记录
    18. Map map=new HashMap<>();
    19. map.put("name","六号");
    20. IndexRequest indexRequest=new IndexRequest("person1").id("6").source(map);
    21. bulkRequest.add(indexRequest);
    22. //3. 修改3号记录 名称为 “三号”
    23. Map mapUpdate=new HashMap<>();
    24. mapUpdate.put("name","三号");
    25. UpdateRequest updateRequest=new UpdateRequest("person1","3").doc(mapUpdate);
    26. bulkRequest.add(updateRequest);
    27. //执行批量操作
    28. BulkResponse response = client.bulk(bulkRequest,
    29. RequestOptions.DEFAULT);
    30. System.out.println(response.status());
    31. }

    导入数据(从mysql导入ES)

    第一步:创建索引
    1. PUT goods
    2. {
    3. "mappings": {
    4. "properties": {
    5. "title": {
    6. "type": "text",
    7. "analyzer": "ik_smart"
    8. },
    9. "price": {
    10. "type": "double"
    11. },
    12. "createTime": {
    13. "type": "date"
    14. },
    15. "categoryName": {
    16. "type": "keyword"
    17. },
    18. "brandName": {
    19. "type": "keyword"
    20. },
    21. "spec": {
    22. "type": "object"
    23. },
    24. "saleNum": {
    25. "type": "integer"
    26. },
    27. "stock": {
    28. "type": "integer"
    29. }
    30. }
    31. }
    32. }

    第二步:java代码进行数据导入

    1. /**
    2. * 从Mysql 批量导入 elasticSearch
    3. */
    4. @Test
    5. public void test3() throws IOException {
    6. //1.查询所有数据,mysql
    7. List goodsList = goodsMapper.findAll();
    8. //2.bulk导入
    9. BulkRequest bulkRequest=new BulkRequest();
    10. //2.1 循环goodsList,创建IndexRequest添加数据
    11. for (Goods goods : goodsList) {
    12. //2.2 设置spec规格信息 Map的数据 specStr:{}
    13. String specStr = goods.getSpecStr();
    14. //将json格式字符串转为Map集合
    15. Map map = JSON.parseObject(specStr, Map.class);
    16. //设置spec map
    17. goods.setSpec(map);
    18. //将goods对象转换为json字符串
    19. String data = JSON.toJSONString(goods);
    20. IndexRequest indexRequest=new
    21. IndexRequest("goods").source(data,XContentType.JSON);
    22. bulkRequest.add(indexRequest);
    23. }
    24. BulkResponse response = client.bulk(bulkRequest,
    25. RequestOptions.DEFAULT);
    26. System.out.println(response.status());
    27. }

    ElasticSearch查询

    matchAll

    脚本

    1. # 默认情况下,es一次展示10条数据,通过from和size来控制分页
    2. # 查询结果详解
    3. GET goods/_search
    4. {
    5. "query": {
    6. "match_all": {}
    7. },
    8. "from": 0,
    9. "size": 100
    10. }
    11. GET g

    java

    1. /**
    2. * 查询所有
    3. * 1. matchAll
    4. * 2. 将查询结果封装为Goods对象,装载到List中
    5. * 3. 分页。默认显示10条
    6. */
    7. @Test
    8. public void matchAll() throws IOException {
    9. //2. 构建查询请求对象,指定查询的索引名称
    10. SearchRequest searchRequest=new SearchRequest("goods");
    11. //4. 创建查询条件构建器SearchSourceBuilder
    12. SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
    13. //6. 查询条件
    14. QueryBuilder queryBuilder= QueryBuilders.matchAllQuery();
    15. //5. 指定查询条件
    16. sourceBuilder.query(queryBuilder);
    17. //3. 添加查询条件构建器 SearchSourceBuilder
    18. searchRequest.source(sourceBuilder);
    19. // 8 . 添加分页信息 不设置 默认10条
    20. // sourceBuilder.from(0);
    21. // sourceBuilder.size(100);
    22. //1. 查询,获取查询结果
    23. SearchResponse searchResponse = client.search(searchRequest,
    24. RequestOptions.DEFAULT);
    25. //7. 获取命中对象 SearchHits
    26. SearchHits hits = searchResponse.getHits();
    27. //7.1 获取总记录数
    28. Long total= hits.getTotalHits().value;
    29. System.out.println("总数:"+total);
    30. //7.2 获取Hits数据 数组
    31. SearchHit[] hits1 = hits.getHits();
    32. //获取json字符串格式的数据
    33. List goodsList = new ArrayList<>();
    34. for (SearchHit searchHit : hits1) {
    35. String sourceAsString = searchHit.getSourceAsString();
    36. //转为java对象
    37. Goods goods = JSON.parseObject(sourceAsString, Goods.class);
    38. goodsList.add(goods);
    39. }
    40. for (Goods goods : goodsList) {
    41. System.out.println(goods);
    42. }
    43. }
    设置条件的疑问点

    termQuery和matchQuery
    term 查询和字段类型有关系,首先回顾一下 ElasticSearch 两个数据类型
    ElasticSearch 两个数据类型(做映射时候用于区别是否对这个字段的内容进行分词)
    text :会分词,不支持聚合
    keyword :不会分词,将全部内容作为一个词条,支持聚合

    termQuery和matchQuery作用类似,但是是用在查询条件上的,分别代表的不同的查询方式,termQuery不会对查询条件进行分词,matchQuery则会对查询条件进行分词

    term 查询:会将华为手机作为一个整体查询
    1. GET goods/_search
    2. {
    3. "query": {
    4. "term": {
    5. "title": {
    6. "value": "华为手机"
    7. }
    8. }
    9. }
    10. }
    match查询:会将华为手机分为:华为和手机进行查询
    1. # match查询
    2. GET goods/_search
    3. {
    4. "query": {
    5. "match": {
    6. "title": "华为手机"
    7. }
    8. },
    9. "size": 500
    10. }
    总结:
    term query 会去倒排索引中寻找确切的 term ,它并不知道分词器的存在。这种查询适合 keyword 、numeric date类型
    match query 知道分词器的存在。并且理解是如何被分词的
    模糊查询
    wildcard 查询:会对查询条件进行分词。还可以使用通配符 ? (任意单个字符) 和 * 0 个或多个字符)
    "* *" 包含华字的
    " *" 华字后边多个字符
    " ?" 华字后边多个字符
    "* " "? " 会引发全表(全索引)扫描 注意效率问题
    1. # wildcard 查询。查询条件分词,模糊查询
    2. GET goods/_search
    3. {
    4. "query": {
    5. "wildcard": {
    6. "title": {
    7. "value": "华*"
    8. }
    9. }
    10. }
    11. }
    正则查询
    \W :匹配包括下划线的任何单词字符,等价于 [A-Z a-z 0-9_] 开头的反斜杠是转义符
    + 号多次出现
    (.)* 为任意字符
    正则查询取决于正则表达式的效率
    1. GET goods/_search
    2. {
    3. "query": {
    4. "regexp": {
    5. "title": "\\w+(.)*"
    6. }
    7. }
    8. }
    前缀查询
    keyword 类型支持比较好
    1. GET goods/_search
    2. {
    3. "query": {
    4. "prefix": {
    5. "brandName": {
    6. "value": "三"
    7. }
    8. }
    9. }
    10. }
    模糊查询-JavaAPI
    1. //模糊查询
    2. WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华*");//华后多
    3. 个字符
    4. //正则查询
    5. RegexpQueryBuilder query = QueryBuilders.regexpQuery("title", "\\w+(.)*");
    6. //前缀查询
    7. PrefixQueryBuilder query = QueryBuilders.prefixQuery("brandName", "三");
    范围&排序查询
    1. # 范围查询
    2. GET goods/_search
    3. {
    4. "query": {
    5. "range": {
    6. "price": {
    7. "gte": 2000,
    8. "lte": 3000
    9. }
    10. }
    11. },
    12. "sort": [
    13. {
    14. "price": {
    15. "order": "desc"
    16. }
    17. }
    18. ]
    19. }
    queryString查询
    queryString 多条件查询
    会对查询条件进行分词。
    然后将分词后的查询条件和词条进行等值匹配
    默认取并集( OR
    可以指定多个查询字段
    query_string :识别 query 中的连接符( or and
    1. # queryString
    2. GET goods/_search
    3. {
    4. "query": {
    5. "query_string": {
    6. "fields": ["title","categoryName","brandName"],
    7. "query": "华为 AND 手机"
    8. }
    9. }
    10. }
    simple_query_string :不识别 query 中的连接符( or and ),查询时会将 华为 "and" 手机 分别进行查询
    1. GET goods/_search
    2. {
    3. "query": {
    4. "simple_query_string": {
    5. "fields": ["title","categoryName","brandName"],
    6. "query": "华为 AND 手机"
    7. }
    8. }
    9. }
    query_string :有 default_operator 连接符的脚本
    1. GET goods/_search
    2. {
    3. "query": {
    4. "query_string": {
    5. "fields": ["title","brandName","categoryName"],
    6. "query": "华为手机 "
    7. , "default_operator": "AND"
    8. }
    9. }
    10. }
    java 代码
    1. QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手
    2. 机").field("title").field("categoryName")
    3. .field("brandName").defaultOperator(Operator.AND);
    simple_query_string :有 default_operator 连接符的脚本
    1. GET goods/_search
    2. {
    3. "query": {
    4. "simple_query_string": {
    5. "fields": ["title","brandName","categoryName"],
    6. "query": "华为手机 "
    7. , "default_operator": "OR"
    8. }
    9. }
    10. }
    注意: query 中的 or and 是查询时 匹配条件是否同时出现 ----or 出现一个即可, and 两个条件同时出现 default_operator or and 是对结果进行 并集( or )、交集( and
    布尔查询
    boolQuery :对多个查询条件连接。连接方式:
    •must and ):条件必须成立
    •must_not not ):条件必须不成立
    •should or ):条件可以成立
    •filter :条件必须成立,性能比 must 高。不会计算得分
    得分 : 即条件匹配度 , 匹配度越高,得分越高
    1. # boolquery
    2. #must和filter配合使用时,max_score(得分)是显示的
    3. #must 默认数组形式
    4. GET goods/_search
    5. {
    6. "query": {
    7. "bool": {
    8. "must": [
    9. {
    10. "term": {
    11. "brandName": {
    12. "value": "华为"
    13. }
    14. }
    15. }
    16. ],
    17. "filter": [
    18. {
    19. "term": {
    20. "title": "手机"
    21. }
    22. },
    23. {
    24. "range": {
    25. "price": {
    26. "gte": 2000,
    27. "lte": 3000
    28. }
    29. }
    30. }
    31. ]
    32. }
    33. }
    34. }
    35. #filter 单独使用 filter可以是单个条件,也可多个条件(数组形式)
    36. {
    37. "query": {
    38. "bool": {
    39. "filter": [
    40. {
    41. "term": {
    42. "brandName": {
    43. "value": "华为"
    44. }
    45. }
    46. }
    47. ]
    48. }
    49. }
    50. }
    布尔查询 -JavaAPI
    布尔查询: boolQuery
    1. 查询品牌名称为 : 华为
    2. 查询标题包含:手机
    3. 查询价格在: 2000-3000
    must filter 为连接方式
    term match 为不同的查询方式
    1. //1.构建boolQuery
    2. BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    3. //2.构建各个查询条件
    4. //2.1 查询品牌名称为:华为
    5. TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brandName","华为");
    6. boolQuery.must(termQueryBuilder);
    7. //2.2. 查询标题包含:手机
    8. MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("title", "手机");
    9. boolQuery.filter(matchQuery);
    10. //2.3 查询价格在:2000-3000
    11. RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
    12. rangeQuery.gte(2000);
    13. rangeQuery.lte(3000);
    14. boolQuery.filter(rangeQuery);
    15. sourceBuilder.query(boolQuery);
    聚合查询
    指标聚合:相当于 MySQL 的聚合函数。 max min avg sum
    桶聚合:相当于 MySQL group by 操作。不要对 text 类型的数据进行分组,会失败。

    1. # 聚合查询
    2. # 指标聚合 聚合函数
    3. GET goods/_search
    4. {
    5. "query": {
    6. "match": {
    7. "title": "手机"
    8. }
    9. },
    10. "aggs": {
    11. "max_price": {
    12. "max": {
    13. "field": "price"
    14. }
    15. }
    16. }
    17. }
    18. # 桶聚合 分组
    19. GET goods/_search
    20. {
    21. "query": {
    22. "match": {
    23. "title": "手机"
    24. }
    25. },
    26. "aggs": {
    27. "goods_brands": {
    28. "terms": {
    29. "field": "brandName",
    30. "size": 100
    31. }
    32. }
    33. }
    34. }
    聚合查询 -JavaAPI
    聚合查询:桶聚合,分组查询
    1. 查询 title 包含手机的数据
    2. 查询品牌列表
    1. /**
    2. * 聚合查询:桶聚合,分组查询
    3. * 1. 查询title包含手机的数据
    4. * 2. 查询品牌列表
    5. */
    6. @Test
    7. public void testAggQuery() throws IOException {
    8. SearchRequest searchRequest=new SearchRequest("goods");
    9. SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
    10. //1. 查询title包含手机的数据
    11. MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "手机");
    12. sourceBuilder.query(queryBuilder);
    13. //2. 查询品牌列表 只展示前100条
    14. AggregationBuilder
    15. aggregation=AggregationBuilders.terms("goods_brands").field("brandName").size(100);
    16. sourceBuilder.aggregation(aggregation);
    17. searchRequest.source(sourceBuilder);
    18. SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);
    19. //7. 获取命中对象 SearchHits
    20. SearchHits hits = searchResponse.getHits();
    21. //7.1 获取总记录数
    22. Long total= hits.getTotalHits().value;
    23. System.out.println("总数:"+total);
    24. // aggregations 对象
    25. Aggregations aggregations = searchResponse.getAggregations();
    26. //将aggregations 转化为map
    27. Map aggregationMap = aggregations.asMap();
    28. //通过key获取goods_brands 对象 使用Aggregation的子类接收 buckets属性在Terms接口中体现
    29. // Aggregation goods_brands1 = aggregationMap.get("goods_brands");
    30. Terms goods_brands =(Terms) aggregationMap.get("goods_brands");
    31. //获取buckets 数组集合
    32. Listextends Terms.Bucket> buckets = goods_brands.getBuckets();
    33. Mapmap=new HashMap<>();
    34. //遍历buckets key 属性名,doc_count 统计聚合数
    35. for (Terms.Bucket bucket : buckets) {
    36. System.out.println(bucket.getKey());
    37. map.put(bucket.getKeyAsString(),bucket.getDocCount());
    38. }
    39. System.out.println(map);
    40. }
    高亮查询
    高亮三要素:
    高亮字段
    前缀
    后缀
    默认前后缀 : em
    手机
    1. GET goods/_search
    2. {
    3. "query": {
    4. "match": {
    5. "title": "电视"
    6. }
    7. },
    8. "highlight": {
    9. "fields": {
    10. "title": {
    11. "pre_tags": "",
    12. "post_tags": ""
    13. }
    14. }
    15. }
    高亮查询 -JavaAPI
    实施步骤:
    高亮查询:
    1. 设置高亮
            高亮字段
            前缀
            后缀
    2. 将高亮了的字段数据,替换原有数据
    1. /**
    2. *
    3. * 高亮查询:
    4. * 1. 设置高亮
    5. * * 高亮字段
    6. * * 前缀
    7. * * 后缀
    8. * 2. 将高亮了的字段数据,替换原有数据
    9. */
    10. @Test
    11. public void testHighLightQuery() throws IOException {
    12. SearchRequest searchRequest = new SearchRequest("goods");
    13. SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
    14. // 1. 查询title包含手机的数据
    15. MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");
    16. sourceBulider.query(query);
    17. //设置高亮
    18. HighlightBuilder highlighter = new HighlightBuilder();
    19. //设置三要素
    20. highlighter.field("title");
    21. //设置前后缀标签
    22. highlighter.preTags("");
    23. highlighter.postTags("");
    24. //加载已经设置好的高亮配置
    25. sourceBulider.highlighter(highlighter);
    26. searchRequest.source(sourceBulider);
    27. SearchResponse searchResponse = client.search(searchRequest,
    28. RequestOptions.DEFAULT);
    29. SearchHits searchHits = searchResponse.getHits();
    30. //获取记录数
    31. long value = searchHits.getTotalHits().value;
    32. System.out.println("总记录数:"+value);
    33. List goodsList = new ArrayList<>();
    34. SearchHit[] hits = searchHits.getHits();
    35. for (SearchHit hit : hits) {
    36. String sourceAsString = hit.getSourceAsString();
    37. //转为java
    38. Goods goods = JSON.parseObject(sourceAsString, Goods.class);
    39. // 获取高亮结果,替换goods中的title
    40. Map highlightFields = hit.getHighlightFields();
    41. HighlightField HighlightField = highlightFields.get("title");
    42. Text[] fragments = HighlightField.fragments();
    43. //highlight title替换 替换goods中的title
    44. goods.setTitle(fragments[0].toString());
    45. goodsList.add(goods);
    46. }
    47. for (Goods goods : goodsList) {
    48. System.out.println(goods);
    49. }
    50. }
    重建索引&索引别名
    1. #查询别名 默认别名无法查看,默认别名同索引名
    2. GET goods/_alias/
    3. #结果
    4. {
    5. "goods" : {
    6. "aliases" : { }
    7. }
    8. }
    1. 新建 student_index_v1 索引
    1. # -------重建索引-----------
    2. # 新建student_index_v1。索引名称必须全部小写
    3. PUT student_index_v1
    4. {
    5. "mappings": {
    6. "properties": {
    7. "birthday":{
    8. "type": "date"
    9. }
    10. }
    11. }
    12. }
    13. #查看 student_index_v1 结构
    14. GET student_index_v1
    15. #添加数据
    16. PUT student_index_v1/_doc/1
    17. {
    18. "birthday":"1999-11-11"
    19. }
    20. #查看数据
    21. GET student_index_v1/_search
    22. #添加数据
    23. PUT student_index_v1/_doc/1
    24. {
    25. "birthday":"1999年11月11日"
    26. }
    2. 重建索引 : student_index_v1 数据拷贝到 student_index_v2
    1. # 业务变更了,需要改变birthday字段的类型为text
    2. # 1. 创建新的索引 student_index_v2
    3. # 2. 将student_index_v1 数据拷贝到 student_index_v2
    4. # 创建新的索引 student_index_v2
    5. PUT student_index_v2
    6. {
    7. "mappings": {
    8. "properties": {
    9. "birthday":{
    10. "type": "text"
    11. }
    12. }
    13. }
    14. }
    15. # 将student_index_v1 数据拷贝到 student_index_v2
    16. # _reindex 拷贝数据
    17. POST _reindex
    18. {
    19. "source": {
    20. "index": "student_index_v1"
    21. },
    22. "dest": {
    23. "index": "student_index_v2"
    24. }
    25. }
    26. GET student_index_v2/_search
    27. PUT student_index_v2/_doc/2
    28. {
    29. "birthday":"1999年11月11日"
    30. }
    3. 创建索引库别名:
    注意: DELETE student_index_v1 这一操作将删除 student_index_v1 索引库,并不是删除别名
    1. # 思考: 现在java代码中操作es,还是使用的实student_index_v1老的索引名称。
    2. # 1. 改代码(不推荐)
    3. # 2. 索引别名(推荐)
    4. # 步骤:
    5. # 0. 先删除student_index_v1
    6. # 1. 给student_index_v2起个别名 student_index_v1
    7. # 先删除student_index_v1
    8. #DELETE student_index_v1 这一操作将删除student_index_v1索引库
    9. #索引库默认的别名与索引库同名,无法删除
    10. # 给student_index_v1起个别名 student_index_v11
    11. POST student_index_v2/_alias/student_index_v11
    12. #测试删除命令
    13. POST /_aliases
    14. {
    15. "actions": [
    16. {"remove": {"index": "student_index_v1", "alias": "student_index_v11"}}
    17. ]
    18. }
    19. # 给student_index_v2起个别名 student_index_v1
    20. POST student_index_v2/_alias/student_index_v1
    21. #查询别名
    22. GET goods/_alias/
    23. GET student_index_v1/_search
    24. GET student_index_v2/_search

  • 相关阅读:
    【提问募集】向世界级软件开发大师“Bob 大叔”Robert C. Martin 提出你的疑虑!
    计算机竞赛 题目:基于LSTM的预测算法 - 股票预测 天气预测 房价预测
    Vue2+Vue3笔记(尚硅谷张天禹老师)day03
    uniapp EventChannel 页面跳转参数事件传递navigateBack,navigateTo 成功后通知事件区别
    通过 Elasticsearch 和 Go 使用混合搜索进行地鼠狩猎
    代码随想录二刷 |数组 | 螺旋矩阵II
    【菜鸟教程】 C++学习笔记
    Redux学习与使用
    C++11 move和forward实现原理
    第五届浙江省大学生网络与信息安全竞赛-技能挑战赛WP
  • 原文地址:https://blog.csdn.net/weixin_44680802/article/details/133173566