通过java代码实现ES中的常用搜索
目录
不分词模糊搜索:wildcardQuery与matchPhraseQuery
后续待补充:queryStringQuery,minimumShouldMatch,对检索结果中的关键词进行高亮
测试环境准备
测试环境:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.3.0</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.3.0</version>
</dependency>
2配置 application.yml
spring:
application:
name: service-search
eslearn:
elasticsearch:
hostlist: 127.0.0.1:9200 #多个结点中间用逗号分隔
3主类代码
@SpringBootApplication
public class SearchApplication {
public static void main(String[] args) {
SpringApplication.run(SearchApplication.class,args);
}
}
配置类:
package com.learn.es.cofig;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author LJM
* @create 2022/12/10
*/
@Configuration
public class ElasticSearchConfig {
@Value("${eslearn.elasticsearch.hostlist}")
private String hostList;
@Bean(destroyMethod = "close") //表示连接使用完成后需要关闭
public RestHighLevelClient restHighLevelClient(){
String[] split = hostList.split(",");
//这种写法是考虑到可能会配置多个es节点
HttpHost[] httpHosts = new HttpHost[split.length];
for (int i = 0; i < split.length; i++) {
String item = split[i];
httpHosts[i] = new HttpHost(item.split(":")[0],Integer.parseInt(item.split(":")[1]),"http");
}
return new RestHighLevelClient(RestClient.builder(httpHosts));
}
}
测试项目结构:
在kibana中把数据插入es中:
delete book //先删除索引
PUT /book //往索引中插入数据进行测试,后面在api实现搜索的小节,可以使用从数据库中读取数据 然后把数据插入es库指定的索引
PUT /book/_doc/1
{
"name": "Bootstrap开发",
"description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel": "201002",
"price":38.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "bootstrap", "dev"]
}
PUT /book/_doc/2
{
"name": "java编程思想",
"description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
"studymodel": "201001",
"price":68.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "java", "dev"]
}
PUT /book/_doc/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"price":88.6,
"timestamp":"2019-08-24 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "spring", "java"]
}
测试类代码结构:
/**
* @author LJM
* @create 2022/12/13
* 测试使用java代码实现es的各种搜索
*/
@SpringBootTest(classes = SearchApplication.class)
@RunWith(SpringRunner.class)
public class TestSearch {
@Autowired
RestHighLevelClient client;
// 后面的测试方法会全部写在这个类中 .....
}
在指定索引下搜索全部(可以指定字段)
在kibana中 GET /book/_search 先获取一下本地es库中的book索引下有多少数据。
/**
* 搜索全部
* GET book/_search
* {
* "query": {
* "match_all": {}
* }
* }
* @throws IOException
*/
@Test
public void testSearchAll() throws IOException {
//1构建搜索请求 实际生产环境中这个索引名称的获取:①把这个索引名称写在枚举类中,然后从枚举类中获取②从配置文件中获取
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//获取某些字段
searchSourceBuilder.fetchSource(new String[]{"name"}, new String[]{});
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("score:" + score);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
通过ids进行搜索
/**
* 通过ids进行搜索 有的就查询出来,没有的也不会报错
* GET /book/_search
* {
* "query": {
* "ids" : {
* "values" : ["1", "2", "6"]
* }
* }
* }
* @throws IOException
*/
@Test
public void testSearchIds() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.idsQuery().addIds("1","2","6"));
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
对搜索结果进行分页
/**
* 对搜索结果进行分页
* GET book/_search
* {
* "query": {
* "match_all": {}
* },
* "from": 0,
* "size": 2
* }
*/
@Test
public void testSearchPage() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//搜索第几页的数据
int page=1;
//每页展示几个数据
//int size=2; //因为本地es一共就插入了三条数据进行测试,所以可以把大小分别设置为2和3看一下输出效果
int size=3;
//下标计算
int from = (page-1) / size;
searchSourceBuilder.from(from);
searchSourceBuilder.size(size);
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
match分词搜索
/**
* match搜索 这个是会进行分词搜索的
* GET /book/_search
* {
* "query": {
* "match": {
* "description": "java程序员"
* }
* }
* }
* @throws IOException
*/
@Test
public void testSearchMatch() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("description", "java程序员"));
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
不分词模糊搜索:wildcardQuery与matchPhraseQuery
/**
* 不分词模糊搜索 like '%检索词%'
* @throws IOException
*/
@Test
public void testSearchLike() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//使用matchPhraseQuery 需要对检索的关键词的前后加 * 否则不是完全的模糊匹配 可以对检索的结果中的检索关键词进行高亮
searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("description", "*"+"程序员"+"*"));
//还可以使用wildcardQuery 但是这种就会导致检索的结果不能高亮(这个不太确定,但是我自己试的时候,这样确实是不能对检索结果中的检索词进行高亮)
//需要在检索的字段名后拼接 ".keyword" 并且也需要对检索词前后添加 *
// searchSourceBuilder.query(QueryBuilders.wildcardQuery("description"+".keyword","*"+"程序员"+"*"));
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
term 搜索(精确匹配)
/**
* term 搜索 如果字段为keyword那么【存储】和【搜索】都不分词。 搜索的时候相当于在使用 = 符号进行搜索
* GET / book / _search
* {
* "query":{
* "term":{
* "description":"java程序员"
* }
* }
* }
* @throws IOException
*/
@Test
public void testSearchTerm() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//如果字段的映射是text但是也想要精确匹配,可以这样操作:QueryBuilders.termQuery("description" + ".keyword","java程序员")
searchSourceBuilder.query(QueryBuilders.termQuery("description", "java语言"));
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
multi_match搜索
/**
* multi_match搜索
* GET /book/_search
* {
* "query": {
* "multi_match": {
* "query": "java程序员",
* "fields": ["name", "description"]
* }
* }
* }
* @throws IOException
*/
@Test
public void testSearchMultiMatch() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//第二个参数才是 字段 第一个参数是匹配的内容
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("java程序员","name","description"));
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
bool搜索 多条件匹配
/**
* bool搜索 多条件匹配 and or !=
* GET / book / _search
* {
* "query":{
* "bool":{
* "must": [
* {
* "multi_match":{
* "query":"java程序员",
* "fields": ["name", "description"]
* }
* }
* ],
* "should": [
* {
* "match":{
* "studymodel":"201001"
* }
* }
* ]
* }
* }
* }
* @throws IOException
*/
@Test
public void testSearchBool() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//构建multiMatch请求 第一个参数是检索内容 第二个参数是检索的字段
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("java程序员", "name", "description");
//构建match请求
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "测试没有的字段");
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(multiMatchQueryBuilder);
boolQueryBuilder.should(matchQueryBuilder);
// boolQueryBuilder.must(matchQueryBuilder); //把should改成must就相当于用 and进行了再一次的过滤 就会查询不到数据
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
}
filter过滤搜索
过滤搜索和范围搜索的区别:
filter,仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响。
query,会去计算每个document相对于搜索条件的相关度,并按照相关度进行排序。
/**
* filter过滤搜索
* GET /book/_search
* {
* "query": {
* "bool": {
* "must": [
* {
* "multi_match": {
* "query": "java程序员",
* "fields": ["name","description"]
* }
* }
* ],
* "should": [
* {
* "match": {
* "studymodel": "201001"
* }
* }
* ],
* "filter": {
* "range": {
* "price": {
* "gte": 50,
* "lte": 90
* }
* }
*
* }
* }
* }
* }
* @throws IOException
*/
@Test
public void testSearchFilter() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//构建multiMatch请求
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("java程序员", "name", "description");
//构建match请求
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("studymodel", "201001");
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(multiMatchQueryBuilder);
boolQueryBuilder.should(matchQueryBuilder);
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(50).lte(90));
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}
sort排序搜索
/**
* sort排序搜索
* GET /book/_search
* {
* "query": {
* "bool": {
* "must": [
* {
* "multi_match": {
* "query": "java程序员",
* "fields": ["name","description"]
* }
* }
* ],
* "should": [
* {
* "match": {
* "studymodel": "201001"
* }
* }
* ],
* "filter": {
* "range": {
* "price": {
* "gte": 50,
* "lte": 90
* }
* }
*
* }
* }
* },
* "sort": [
* {
* "price": {
* "order": "asc"
* }
* }
* ]
* }
* @throws IOException
*/
@Test
public void testSearchSort() throws IOException {
//1构建搜索请求
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//构建multiMatch请求
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("java程序员", "name", "description");
//构建match请求
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("studymodel", "201001");
BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
boolQueryBuilder.must(multiMatchQueryBuilder);
boolQueryBuilder.should(matchQueryBuilder);
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(50).lte(90));
searchSourceBuilder.query(boolQueryBuilder);
//按照价格升序
searchSourceBuilder.sort("price", SortOrder.ASC);
searchRequest.source(searchSourceBuilder);
//2执行搜索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//3获取结果
SearchHits hits = searchResponse.getHits();
//数据数据
SearchHit[] searchHits = hits.getHits();
System.out.println("--------------------------");
for (SearchHit hit : searchHits) {
String id = hit.getId();
float score = hit.getScore();
//这个source里面就是我们存储的数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String description = (String) sourceAsMap.get("description");
Double price = (Double) sourceAsMap.get("price");
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("description:" + description);
System.out.println("price:" + price);
System.out.println("==========================");
}
}