ElasticSearch学习总结(二)

1 Bulk批量操作

1.1 脚本操作

   #批量操作
    POST _bulk
    {"delete":{"_index":"person1","_id":"5"}}	#删除5{"create":{"_index":"person1","_id":"8"}}	#新增8(下边是新增的数据)
    {"name":"八号","age":18,"address":"北京"}
    {"update":{"_index":"person1","_id":"2"}}	#更新2号 name为2{"doc":{"name":"2号"}}

2 导入数据库数据

2.1 分析&创建索引

# 需要根据数据库字段信息,定义出来es的索引和映射配置,且手动执行一下
PUT goods
{
	"mappings": {
		"properties": {
			"title": {
				"type": "text",
				"analyzer": "ik_smart"
			},
			"price": { 
				"type": "double"
			},
			"createTime": {
				"type": "date"
			},
			"categoryName": {	
				"type": "keyword"
			},
			"brandName": {	
				"type": "keyword"
			},	
			"spec": {		
				"type": "object"
			},
			"saleNum": {	
				"type": "integer"
			},			
			"stock": {	
				"type": "integer"
			}
		}
	}
}

2.2 代码实现

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "goods")  //索引的名
public class Good {
    @Id
    private Double id;
    @Field(type = FieldType.Text ,analyzer = "ik_smart")
    private String title;
    private Double price;
    private Double stock;
    private Double saleNum;
    private Date createTime;
    @Field(analyzer = "keyword")
    private String categoryName;
    @Field(analyzer = "keyword")
    private String brandName;
    private LinkedHashMap spec;
    public void setSpec(String spec) {
        LinkedHashMap parse = JSON.parseObject(spec, LinkedHashMap.class);
        this.spec = parse;
    }
}

 

@SpringBootTest
class EsBulkTests {
    @Autowired
    private ElasticsearchRestTemplate restTemplate;

    @Autowired
    private GoodMapper goodMapper;

//批量添加
    @Test
    public  void  bulkAddTest(){
        //从数据库中获取所有的数据zho
        List<Good> list = goodMapper.findAll();
        //把数据批量添加到es中
        restTemplate.save(list);
    }

    //批量删除
    @Test
    public  void bulkDeleteTest(){
        //指定删除条件
        CriteriaQuery query = new CriteriaQuery(Criteria.where("id").notIn("-1"));
        //根据条件进行批量删除
        restTemplate.delete(query,Good.class);
    }

}

3 ElasticSearch查询

3.1 matchAll

脚本

  # matchAll: 查询所有
    # 默认情况下,es一次展示10条数据,通过from和size来控制分页
    # 查询结果详解
    GET goods/_search
    {
      "query": {
        "match_all": {}
      },
      "from": 0,
      "size": 100
    }

API

   /**
     * 查询所有
     *  1. matchAll
     *  2. 将查询结果封装为Goods对象,装载到List中
     *  3. 分页。默认显示10条
     */
     @Test
        public void matchAllQueryTest2() {
            QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
            Query query = new NativeSearchQueryBuilder().withQuery(queryBuilder)
                    .withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC))  //查询结果按price进行排序
                    .withPageable(Pageable.ofSize(10).withPage(0))  //进行分页操作size指定大小,page 指定页码
                    .build();
            SearchHits<Good> hits = restTemplate.search(query, Good.class);
            System.out.println(hits);
    
            long totalHits = hits.getTotalHits();
    
            System.out.println("查询到的总数:" + totalHits);
    
            //把查询到的结果,转换为List集合
            List<Good> list = hits.get().map(hit -> hit.getContent()).collect(Collectors.toList());
    
            for (Good good : list) {
                System.out.println(good);
            }
    
        }
    

    public void executeQuery(QueryBuilder queryBuilder) {
            Query query = new NativeSearchQueryBuilder().withQuery(queryBuilder)
                    .withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC))  //查询结果按price进行排序
                    .withPageable(Pageable.ofSize(10).withPage(0))  //进行分页操作size指定大小,page 指定页码
                    .build();
            SearchHits<Good> hits = restTemplate.search(query, Good.class);
            long totalHits = hits.getTotalHits();
            System.out.println("查询到的总数:" + totalHits);
    
            //把查询到的结果,转换为List集合
            List<Good> list = hits.get().map(hit -> hit.getContent()).collect(Collectors.toList());
    
            for (Good good : list) { //打印每条信息
                System.out.println(good);
            }
        }

3.2 termQuery

注意:因为term是“完整词条查询”,所以,这种查询适合keyword 、numeric、date

脚本

 GET 索引名称/_search
    {
        "query": {
            "term": {
                "字段名称": {
                    "value": "查询条件"
                }
            }
        }
    }

API

 //2.termQuery 词条等值查询
    @Test
    public void termQueryTest() {
        //如果指定的列不分词则,则要求,该列必须和“华为手机”一模一样
        //如果指定的列分词,则要求词条中有和“华为手机”一摸一样的词条
        QueryBuilder queryBuilder = QueryBuilders.termsQuery("title", "华为");
        executeQuery(queryBuilder);
    }

3.3 matchQuery

注意:match查询会对查询条件进行分词,然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR)

脚本

 # 语法一
    GET 索引名称/_search
    {
    	"query": {
    		"match": {
    			"字段名称": "查询条件"
    		}
    	}
    }
    
    # 语法二
    GET 索引名称/_search
    {
    	"query": {
    		"match": {
    			"字段名称": {
              		"query": "查询条件",
                    "operator": "操作(or或者and)"
    			}
    		}
    	}
    }

API

 //3.matchQuery先分词,然后等值查询. 只能查询已经分词的列
    @Test
    public void matchQueryTest() {
        //先对华为手机进行分词,然后根据分词后的的词条查询执行的列
        //operator指分别查询寻后的操作 or表示去并集,and 表示取并集
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "华为手机").operator(Operator.OR);
        executeQuery(queryBuilder);
    }								   //求并集

3.4 模糊查询

3.4.1 wildcard查询

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

   # wildcard 查询。查询条件分词,模糊查询
    GET goods/_search
    {
      "query": {
        "wildcard": {
          "title": {
            "value": "华*"
          }
        }
      }
    }

API

   //1.wildcard对词条进行模糊查询
    @Test
    public void wildcardQueryTest() {
        // 如果指定的列, 不分词, 则要求, 该列的值, 必须满足条件.  *代表任意多个任意字符, ?代表一个任意字符
        // 判断指定的列, 分词了, 则要求, "词条"中, 必须满足条件. 有就获取, 没有就不要
        QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("title", "华*");
        executeQuery(queryBuilder);
    }

3.4.2 regexp正则查询

正则查询取决于正则表达式的效率

   GET goods/_search
    {
      "query": {
        "regexp": {
          "title": "\\w+(.)*"
        }
      }
    }

API

 //2.正则查询
    @Test
    public void regexQueryTest() {
        // 如果指定的列, 不分词, 则要求, 该列的值, 必须满足正则表达式
        // 判断指定的列, 分词了, 则要求, "词条"中, 必须满足正则表达式. 有就获取, 没有就不要
        QueryBuilder queryBuilder = QueryBuilders.regexpQuery("title", "\\w+(.)*");
        executeQuery(queryBuilder);
    }

3.4.3 prefix前缀查询

对keyword类型支持比较好,其他类型数据不太适合

  # 前缀查询 对keyword类型支持比较好
    GET goods/_search
    {
      "query": {
        "prefix": {
          "brandName": {
            "value": "三"
          }
        }
      }
    }

API

//3.前缀查询
    @Test
    public void prefixQueryTest() {
        // prefixQuery: 前缀匹配. 其实就是wildcardQuery关键字后边加*
        // 如果指定的列, 不分词, 则要求, 该列的值, 必须满足正则表达式
        // 判断指定的列, 分词了, 则要求, "词条"中, 必须满足正则表达式. 有就获取, 没有就不要
        QueryBuilder queryBuilder = QueryBuilders.prefixQuery("brandName", "三");
        executeQuery(queryBuilder);
    }

3.5 范围&排序查询

脚本

  # 范围查询
    GET goods/_search
    {
      "query": {
        "range": {
          "price": {		# 按照价格price查询
            "gte": 2000,	# 大于等于2000,gt:大于
            "lte": 3000		# 小于等于3000,lt:小于
          }
        }
      },
      "sort": [					# 排序,可选
        {
          "price": {
            "order": "desc"		# 按照价格,降序排列
          }
        }
      ]
    }

API

   //1.范围&排序查询
        @Test
        public void rangedQueryTest() {
            QueryBuilder queryBuilder = QueryBuilders.rangeQuery("price").gte(1000).lte(5000);
            executeQuery(queryBuilder);
        }

3.6 QueryString查询

3.6.1 概述

QueryString是多条件查询。会对查询条件进行分词,然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR)。

QueryString可以指定多个查询字段

QueryString的查询分两种"query_string"和"simple_query_string"

3.6.2 query_string

   GET goods/_search
    {
      "query": {
        "query_string": {
          "fields": ["title","categoryName","brandName"], 
          "query": "华为 AND 手机"
        }
      }
    }
    
    # 注意:
    # 	query可以书写多个词条,这些词条也会被默认分词,词条之间,默认“OR”
    
    # "华为 AND 手机": 
    # 检查一整条数据中的"title","categoryName","brandName"字段,"华为""手机"词条同时存在。
    # 注意:"华为""手机"词条可以在不同字段中,但整条数据中必须全部存在
    
    # "华为 OR 手机": 
    # 检查一整条数据中的"title","categoryName","brandName"字段,任意字段,只要包含"华为""手机"中的任何词条,都满足要求

3.6.3 simple_query_string

   GET goods/_search
    {
      "query": {
        "simple_query_string": {
          "fields": ["title","categoryName","brandName"], 
          "query": "华为 AND 手机"
        }
      }
    }
    # 注意:
    # 	query可以书写多个词条,这些词条也会被默认分词,词条之间,使用OR
    #	simple_query_string不支持"AND"连接符。如:"华为 AND 手机",查询时会将 “华为”、"and"、“手机”分别进行查询

3.6.4 default_operator

GET goods/_search
{
  "query": {
    "query_string": {
      "fields": ["title","brandName","categoryName"],
      "query": "华为手机",
      "default_operator": "AND"
    }
  }
}

# 注意:
# 	1.default_operator: 默认为"OR"
#   2.default_operator一般不和query中的关键字同时使用

# "default_operator": "AND" 
# 求满足词条要求的交集

# "query": "华为 手机""default_operator": "OR" 
# 求满足词条要求的并集

3.6.5 API

   QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机")
        .field("titl //1.queryString查询(多条件查询)
        @Test
        public void queryStringTest() {
            //查询: 一条数据中,title/categoryName/brandName三列中,必须同时包含"华为"和"手机"
            QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("华为 AND 手机").field("title").field("categoryName").field("bandName");
            executeQuery(queryBuilder);
        }e").field("categoryName").field("brandName")
        .defaultOperator(Operator.AND);
              
      //2.
        @Test
        public  void  simpleQueryStringQuery(){
            //查询: match查询title, match查询categoryName, match查询brandName, 求并集
            //注意: 使用该种查询的时候,要保证"查询的所有列"都是"分词列"
            QueryBuilder queryBuilder = QueryBuilders.simpleQueryStringQuery("华为手机").field("title").field("categoryName").field("bandName").defaultOperator(Operator.AND);
            executeQuery(queryBuilder);
        }

simple_query_string:有default_operator连接符的脚本

GET goods/_search
{
  "query": {
    "simple_query_string": {
      "fields": ["title","brandName","categoryName"],
      "query": "华为手机 "
      , "default_operator": "OR"
    }
  }
}

注意:query中的or and 是查询时 匹配条件是否同时出现----or 出现一个即可,and 两个条件同时出现

default_operator的or and 是对结果进行 并集(or)、交集(and)

3.7 布尔查询

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

  • must(and):条件必须成立,条件可以有多个
  • must_not(not):条件必须不成立,条件可以有多个
  • should(or):条件可以成立,条件可以有多个
  • filter:条件必须成立,性能比must高。不会计算得分,条件可以有多个。(得分: 即条件匹配度,匹配度越高,得分越高)

脚本

  GET goods/_search
    {
      "query": {
        "bool": {
          "must": [
           {
              "term": {
                "brandName": {
                  "value": "华为"
                }
              }
            }
          ],
          "filter":[ 
           {
               "term": {
                  "title": "手机"
               }
           },
           {
               "range":{
                     "price": {
                        "gte": 2000,
                        "lte": 3000
                     }
                }
            }      
          ]
        }
      }
    }
    
    #filter 单独使用   filter可以是单个条件,也可多个条件(数组形式)
    GET goods/_search
    {
      "query": {
        "bool": {
          "filter": [
            {
              "term": {
                "brandName": {
                  "value": "华为"
                }
              }
            }
          ]
        }
      }
    }

API

 //布尔查询(即多条条件一并成立)
        @Test
        public void testBoolQuery() {
            //多条件查询
            QueryBuilder queryBuilder =  QueryBuilders.boolQuery()
                    .must(QueryBuilders.termQuery("brandName","华为"))
                    .filter(QueryBuilders.matchQuery("title","手机"))
                    .filter(QueryBuilders.rangeQuery("price").gte(2000).lte(3000));
            executeQuery(queryBuilder);
    
            //Query query = new NativeSearchQueryBuilder()
            //        .withQuery(QueryBuilders.termQuery("brandName","华为"))
            //        .withFilter(QueryBuilders.matchQuery("title","手机"))
            //        .withFilter(QueryBuilders.rangeQuery("price").gte(2000).lte(3000))
            //        .build();
        }
    

3.8 聚合查询

3.8.1 指标聚合

相当于MySQL的聚合函数。max、min、avg、sum等

脚本

 # 指标聚合-聚合函数
    GET goods/_search
    {  
      "query": {			// 基础查询
        "match": {
          "title": "手机"
        }
      },
      "aggs": {					// 在基础查询的基础上,开始聚合
        "max_price": {			// 结果所存储的字段名,名称自定义
          "max": {				// 聚合类型
            "field": "price"	// 聚合依据(也就是按照price列进行聚合)
          }
        }
      }
    }

API

  @Autowired
    private ElasticsearchRestTemplate restTemplate;

    /**
     * 聚合函数-指标聚合
     */
    @Test
    public void maxTest() {
        //准备查询条件
        QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "手机");
        //编译查询条件
        NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(AggregationBuilders.max("price_max").field("price")).build();//对brandName进行分组统计
        //执行查询条件
        SearchHits<Good> hits = restTemplate.search(query, Good.class);
        long totalHits = hits.getTotalHits();
        System.out.println("查询到的总数:"+totalHits);
        Aggregations aggregations = hits.getAggregations();
        Max price_max = aggregations.get("price_max");
        double price_maxValue = price_max.getValue();
        System.out.println(price_maxValue);
    }

3.8.2 指标聚合

相当于MySQL的 group by 分组查询。

注意:不要对text类型的数据进行分组,会失败。

脚本

  # 桶聚合-分组查询
    GET goods/_search
    {
      "query": {
        "match": {
          "title": "手机"
        }
      },
      "aggs": {
        "goods_brands": {
          "terms": {
            "field": "brandName",
            "size": 100				// 查询结果数量(默认分页显示,只显示前10条)
          }
        }
      }
    }

API

  /**
         *   分组查询-桶聚合
         */
        @Test
        public void GroupByTest() {
            //准备查询条件
            QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "手机");
            //编译查询条件
            NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(AggregationBuilders.terms("brands").field("brandName")).build();
            //执行查询条件
            SearchHits<Good> hits = restTemplate.search(query, Good.class);
            long totalHits = hits.getTotalHits();
            System.out.println("查询到的总数:"+totalHits);
            Aggregations aggregations = hits.getAggregations();
            Terms brands = aggregations.get("brands");
            List<? extends Terms.Bucket> buckets = brands.getBuckets();
            for (Terms.Bucket bucket : buckets) {
                String keyAsString = bucket.getKeyAsString();
                long docCount = bucket.getDocCount();
                System.out.println(keyAsString+"....."+docCount);
            }
        }
    }

3.9 高亮查询

所谓高亮查询,其实就是把查询结果中的"关键词"的前后,加上前后缀。

脚本

  # 对title查询,对查询到的结果中的"电视",加上前后缀
    GET goods/_search
    {
      "query": {
        "match": {
          "title": "电视"
        }
      },
      "highlight": {
        "fields": {
          "title": {
            "pre_tags": "<font color='red'>",	// 前缀
            "post_tags": "</font>"				// 后缀
          }
        }
      }
    }
    
    # 注意:
    # 	如果不加前后缀,默认前缀时<em>,后缀</em>
    #	查询结果并不是在原字段上加前后缀,而是会有一个新的列,叫做highlight,高亮处理后的文本在那里边

API

   @Autowired
        private ElasticsearchRestTemplate restTemplate;
    
        /**
         * 高亮查询
         */
        @Test
        public void highlightTest() {
            //1.准备查询条件(title包含电视的)
            QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "电视");
            //2.编译查询条件
            NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(queryBuilder).withHighlightBuilder(new HighlightBuilder().field("title").preTags("。。前面。。").postTags("..后面..")).withPageable(Pageable.ofSize(10).withPage(0)).build();
            //3.执行查询
            SearchHits<Good> hits = restTemplate.search(query, Good.class);
            long totalHits = hits.getTotalHits();
            System.out.println("查询到的总数:" + totalHits);
    
            //4.获取高亮
            List<Good> list = new ArrayList<>();
            for (SearchHit<Good> hit : hits) {
                Good good = hit.getContent();
                List<String> title_list = hit.getHighlightField("title");
                String titleStr = "";
                for (String s : title_list) {
                    titleStr += s;
                }
                good.setTitle(titleStr);
                list.add(good);
            }
            System.out.println(list);
        }
    }

3.10 重建索引&索引别名

3.10.1 查询别名

#查询别名 默认别名无法查看,默认别名同索引名
GET goods/_alias/
#结果
{
  "goods" : {
    "aliases" : { }
  }
}

3.10.2 添加别名

POST 索引名/_alias/别名

注意:

  • 索引可以有多个别名
  • 多个索引可以指向同一个别名,查询时查询的是并集,然不能做添加。强烈不建议让多个索引指向同一个别名
  • 删除索引后,别名自动删除。
  • 当别名没有被索引引用时,自动删除
  • 当索引有别名后,操作别名和操作索引没区别。但不能执行删除索引库操作。

3.10.3 删除别名

   POST _aliases
    {
        "actions": [
            { 
                "remove": { "index": "student_index_v1", "alias": "student_index_v11" }
            }
        ]
    }

3.10.4 修改别名

POST /_aliases
{
    "actions": [
        { "remove": { "index": "索引名", "alias": "旧别名" }},
        { "add":    { "index": "索引名", "alias": "新别名" }}
    ]
}

3.10.5 拷贝数据

   # 拷贝数据
    POST _reindex
    {
      "source": {
        "index": "原索引"
      },
      "dest": {
        "index": "目标索引"
      }
    }

3.10.6 API

   /**
     * 获取所有的别名
     * @throws IOException
     */
    @Test
    public void getAliases() throws IOException {
        IndicesClient indicesClient = client.indices();
        GetAliasesRequest getAliasesRequest = new GetAliasesRequest();
        GetAliasesResponse response = indicesClient.getAlias(getAliasesRequest,RequestOptions.DEFAULT);
        Map<String, Set<AliasMetaData>> aliases = response.getAliases();
        for (String s : aliases.keySet()) {
            System.out.println(s+":"+aliases.get(s));
        }
    }
    
    /**
     * 获取指定索引的别名
     * @throws IOException
     */
    @Test
    public void getAliasesByIndex() throws IOException {
        IndicesClient indicesClient = client.indices();
        GetIndexRequest getIndexRequest = new GetIndexRequest("student_index_v1");
        GetIndexResponse response = indicesClient.get(getIndexRequest, RequestOptions.DEFAULT);
        Map<String, List<AliasMetaData>> aliases = response.getAliases();
        for (String s : aliases.keySet()) {
            System.out.println(s+":"+aliases.get(s));
        }
    }
    
    /**
     * 给索引添加别名
     * @throws IOException
     */
    @Test
    public void addAliasesByIndex() throws IOException {
        IndicesClient indicesClient = client.indices();
        IndicesAliasesRequest request = new IndicesAliasesRequest();
        IndicesAliasesRequest.AliasActions alias = IndicesAliasesRequest.AliasActions.add();
        alias.index("student_index_v1");    //索引
        alias.alias("student_index_v12");   //别名
        request.addAliasAction(alias);
        AcknowledgedResponse response = indicesClient.updateAliases(request, RequestOptions.DEFAULT);
        System.out.println(response.isAcknowledged());
    }
    
    /**
     * 删除索引的别名
     * @throws IOException
     */
    @Test
    public void removeAliasesByIndex() throws IOException {
        IndicesClient indicesClient = client.indices();
        IndicesAliasesRequest request = new IndicesAliasesRequest();
        IndicesAliasesRequest.AliasActions alias = IndicesAliasesRequest.AliasActions.remove();
        alias.index("student_index_v1");    //索引
        alias.alias("student_index_v12");   //别名
        request.addAliasAction(alias);
        AcknowledgedResponse response = indicesClient.updateAliases(request, RequestOptions.DEFAULT);
        System.out.println(response.isAcknowledged());
    }
    
    
    /**
     * 数据迁移
     * @throws IOException
     */
    @Test
    public void sourceToDest() throws IOException {
        IndicesClient indicesClient = client.indices();
    
        //1.获取操作文档的对象
        ReindexRequest reindexRequest = new ReindexRequest();
        reindexRequest.setSourceIndices("student_index_v1");
        reindexRequest.setDestIndex("student_index_v2");
        //添加数据,获取结果
        BulkByScrollResponse response = client.reindex(reindexRequest, RequestOptions.DEFAULT);
        //打印响应结果
        System.out.println(response.getTotal());
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大梦谁先觉i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值
>