Table of contents
Search all under the specified index (fields can be specified)
Wordless fuzzy search: wildcardQuery and matchPhraseQuery
bool search multi-condition matching
To be added later: queryStringQuery, minimumShouldMatch, highlight keywords in the search results
Test environment preparation
test environment:
<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 configure application.yml
spring:
application:
name: service-search
eslearn:
elasticsearch:
hostlist: 127.0.0.1:9200 #多个结点中间用逗号分隔
3 main class code
@SpringBootApplication
public class SearchApplication {
public static void main(String[] args) {
SpringApplication.run(SearchApplication.class,args);
}
}
Configuration class:
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));
}
}
Test project structure:
Insert data into es in kibana:
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"]
}
Test class code structure:
/**
* @author LJM
* @create 2022/12/13
* 测试使用java代码实现es的各种搜索
*/
@SpringBootTest(classes = SearchApplication.class)
@RunWith(SpringRunner.class)
public class TestSearch {
@Autowired
RestHighLevelClient client;
// 后面的测试方法会全部写在这个类中 .....
}
Search all under the specified index (fields can be specified)
In kibana, GET /book/_search first gets how much data there is under the book index in the local es library.
/**
* 搜索全部
* 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("==========================");
}
}
Search by 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("==========================");
}
}
Paginate search results
/**
* 对搜索结果进行分页
* 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 participle search
/**
* 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("==========================");
}
}
Wordless fuzzy search: wildcardQuery and 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 search (exact match)
/**
* 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 search
/**
* 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 search multi-condition matching
/**
* 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 search
The difference between filter search and range search:
filter , just filter out the required data according to the search criteria, does not calculate any correlation score, and has no effect on the correlation.
query, will calculate the relevance of each document relative to the search criteria, and sort according to the relevance.
/**
* 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 search
/**
* 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("==========================");
}
}