ES的简单前缀提示搜索(Movies案例)
要根据电影的名字做前缀搜索,前缀搜索的字段属性应该要使用
completion
, 这个时候需要我们自定义mapping,但是mapping定义过于复杂。所以我们的处理方式,就是先用少量的样本数据导入到ES中去,然后ES会自动的帮我们生成一个mapping, 然后可以获取到该mapping信息,再将之前的索引删除,根据之前的mapping进行修改,然后在导入数据。
第一步, 使用logstash正常的导入数据,
第二步,获取mapping信息:
GET movies
{
"movies" : {
"aliases": {},
"mappings" : {
"properties" : {
"@version" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"genre" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above" : 256
}
}
},
"year" : {
"type" : "long"
}
}
},
"settings" : {
"index" : {
"creation_date" : "1625041865073",
"number_of_shards" : "1",
"number_of_replicas" : "1",
"uuid" : "NCAfXDw3RBCRPt-RT436XA",
"version" : {
"created" : "7080199"
},
"provided_name" : "movies"
}
}
}
}
第三步,将movies索引删除掉:
delete movies
第四步,根据 “第二步” 获取到mapping信息,重新创建mapping.
PUT movies
{
"aliases" : { },
"mappings" : {
"properties" : {
"@version" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"genre" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"title" : {
# 将title的类型改成 completion, 其他额外信息删除
"type" : "completion"
},
"year" : {
"type" : "long"
}
}
}
}
第五步,重新到如 movies.csv 数据集,还是最开始的导入方式
第六步,在kibana中实现前缀的搜索提示
// 建议搜索
GET movies/_search
{
// "_source": "" 表示不查询任何的字段的数据
"_source": "",
// 这种前缀搜索相当于建议一样
"suggest": {
// 自己取的建议的名字,名字随意
"title_prefix_suggest": {
"prefix": "We",
"completion": {
"field": "title",
"size": 10
}
}
}
}
第七步,编写Java代码的实现
// 往IOC容器中注入ES的客户端工具
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
// 创建一个ES的客户端
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}
@RestController
@RequestMapping("/mv")
public class MovieController {
private RestHighLevelClient restHighLevelClient;
/*
* @Qualifier("elasticsearchClient") 主要是针对容器中有多个 bean, 可以通过 @Qualifier 将那个bean注入进来
* @param elasticsearchClient
*/
public MovieController(@Qualifier("elasticsearchClient") RestHighLevelClient restHighLevelClient) {
this.restHighLevelClient = restHighLevelClient;
}
@GetMapping("/pre")
public List<String> prefixSuggest(String prefix) throws IOException {
// 使用Request构建在kibana中写的 查询
Request request = new Request("GET", "movies/_search");
String suggestJson = String.format("{" +
" \"_source\": \"\", " +
" \"suggest\": {" +
" \"title_prefix_suggest\": {" +
" \"prefix\": \"%s\"," +
" \"completion\": {" +
" \"field\": \"title\"" +
" }" +
" }" +
" }" +
"}", prefix);
request.setJsonEntity(suggestJson);
// 发送请求, 返回结果
Response response = restHighLevelClient.getLowLevelClient().performRequest(request);
// 返回的json字符串, 就是于 kibana中得到的json数据
String responseJson = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSONObject.parseObject(responseJson);
// 获取 kibana 中的 "suggest" 对应的json对象
JSONObject suggest = jsonObject.getJSONObject("suggest");
// 就是搜索的信息与结构
JSONObject resultInfo = suggest.getJSONArray("title_prefix_suggest").getJSONObject(0);
JSONArray options = resultInfo.getJSONArray("options");
// 给定长度, 是一种优化的策略
List<String> results = new ArrayList<>(options.size());
for(int i = 0; i < options.size(); i++) {
// opt 就是我们需要的数据
JSONObject opt = options.getJSONObject(i);
results.add(opt.getString("text"));
}
return results;
}
}