文章目录
DSL搜索
DSL(Domain Specific Language) 是 ES 提出的基于 json 的搜索方式,在搜索时传入特定的 json 格式的数据来完成不同的搜索需求。DSL 比 URI (在url传递搜索参数) 搜索方式功能强大,在项目中建议使用 DSL 方式来完成搜索。
查询所有文档
查询所有索引库的文档。
发送:post http://localhost:9200/_search
查询指定索引库 指定类型 下的文档。(通过使用此方法)
{
"query": {
"match_all": {}
},
"_source" : ["name","studymodel"]
}
_source:source 源过虑设置,指定结果中所包括的字段有哪些。
{
"took": 98,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1.0,
"hits": [
{
"_index": "xc_course",
"_type": "doc",
"_id": "1",
"_score": 1.0,
"_source": {
"studymodel": "201002",
"name": "Bootstrap开发"
}
},
{
"_index": "xc_course",
"_type": "doc",
"_id": "2",
"_score": 1.0,
"_source": {
"studymodel": "201001",
"name": "java编程基础"
}
},
{
"_index": "xc_course",
"_type": "doc",
"_id": "3",
"_score": 1.0,
"_source": {
"studymodel": "201001",
"name": "spring开发基础"
}
}
]
}
}
使用JAVA 客户端实现
1、创建搜索请求对象
2、指定类型(部分版本不需要指定类型,这里以 6.2.1 为例)
3、构建搜索源对象
4、配置搜索方式,设置需要过滤字段
5、向搜索请求中设置搜索源
6、执行搜索,向ES发起 http 请求
7、搜索结果 asd as
8、匹配到的总记录数
9、得到匹配度高的文档
10、遍历结果,获取 SearchHit 对象中的属性,输出或者存档。
/**
* 查询所有文档
*/
@Test
public void testSearchAll() throws IOException {
//创建搜索请求对象,并设置类型
SearchRequest searchRequest = new SearchRequest("xc_course");
searchRequest.types("doc");
//构建搜索源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//配置source源字段过虑,1显示的,2排除的
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","description"},new String[]{});
//将搜索源配置到搜索请求中,执行搜索,获取搜索响应结果
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
//获取所有搜索结果、总匹配数量
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
//遍历结果
for(SearchHit searchHit:searchHits){
String index = searchHit.getIndex();
String type = searchHit.getType();
String id = searchHit.getId();
float score = searchHit.getScore();
String sourceAsString = searchHit.getSourceAsString();
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
分页查询
发送:post http://localhost:9200/xc_course/doc/_search
ES 支持分页查询,传入两个参数:from 和 size。
form:表示起始文档的下标,从0开始。
size:查询的文档数量。
{
"from" : 0,
"size" : 1,
"query": {
"match_all": {}
},
"_source" : ["name","studymodel"]
}
使用java客户端实现
/**
* 分页查询
*/
@Test
public void testSearchPage() throws IOException {
//创建搜索请求对象,并设置类型
SearchRequest searchRequest = new SearchRequest("xc_course");
searchRequest.types("doc");
//构建搜索源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//ES这里是按起始坐标来实现分页查询,所以我们要指定一个页码
int form = 0;
int size = 1;
searchSourceBuilder.from(form);
searchSourceBuilder.size(size);
//配置source源字段过虑,1显示的,2排除的
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","description"},new String[]{});
//将搜索源配置到搜索请求中,执行搜索,获取搜索响应结果
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
//获取所有搜索结果、总匹配数量
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
//遍历结果
for(SearchHit searchHit:searchHits){
String index = searchHit.getIndex();
String type = searchHit.getType();
String id = searchHit.getId();
float score = searchHit.getScore();
String sourceAsString = searchHit.getSourceAsString();
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
Term Query
Term Query 为精确查询,在搜索时会整体匹配关键字,不再将关键字分词。
发送:post http://localhost:9200/xc_course/doc/_search
{
"query": {
"term" : {
"name": "spring"
}
},
"_source" : ["name","studymodel"]
}
上边的搜索会查询 name 包括 spring 这个词的文档。
使用java客户端实现
/**
* Term Query 精确查询
*/
@Test
public void testSearchTermQuery() throws IOException {
//创建搜索请求对象,并设置类型
SearchRequest searchRequest = new SearchRequest("xc_course");
searchRequest.types("doc");
//构建搜索源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("name","spring"));
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","description"}, new String[]{});
//将搜索源配置到搜索请求中,执行搜索,获取搜索响应结果
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
//获取所有搜索结果、总匹配数量
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
//遍历结果
for(SearchHit searchHit:searchHits){
String index = searchHit.getIndex();
String type = searchHit.getType();
String id = searchHit.getId();
float score = searchHit.getScore();
String sourceAsString = searchHit.getSourceAsString();
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
根据id精确匹配
ES提供根据多个id值匹配的方法:
post: http://127.0.0.1:9200/xc_course/doc/_search
使用java客户端实现
/**
* 根据id精确匹配
*/
@Test
public void testSearchById() throws IOException {
//创建搜索请求对象,并设置类型
SearchRequest searchRequest = new SearchRequest("xc_course");
searchRequest.types("doc");
//构建搜索源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
String[] ids = {"1", "2"};
searchSourceBuilder.query(QueryBuilders.termsQuery("_id",ids));
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","description"}, new String[]{});
//将搜索源配置到搜索请求中,执行搜索,获取搜索响应结果
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
//获取所有搜索结果、总匹配数量
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
//遍历结果
for(SearchHit searchHit:searchHits){
String index = searchHit.getIndex();
String type = searchHit.getType();
String id = searchHit.getId();
float score = searchHit.getScore();
String sourceAsString = searchHit.getSourceAsString();
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
match query (匹配单个字段)
基本使用
match query 即全文检索,它的搜索方式是先将搜索字符串分词,再使用各各词条从索引中搜索。match query 与 Term query 区别是 match query 在搜索前先将搜索关键字分词,再拿各各词语去索引中搜索。
需求:检索 name 字段中包含 spring开发 的文档,并且结果只显示该文档的 name 字段
发送:post http://localhost:9200/xc_course/doc/_search
{
"query": {
"match": {
"name": {
"query": "spring开发",
"operator": "or"
}
}
},
"_source" : ["name"]
}
query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用逗号分隔也可以不用。
operator:or 表示 只要有一个词在文档中出现则就符合条件, and 表示每个词都在文档中出现则才符合条件
1、将“spring开发”分词,分为spring、开发两个词
2、再使用spring和开发两个词去匹配索引中搜索。
3、由于设置了operator为or,只要有一个词匹配成功则就返回该文档。
java客户端实现
/**
* match query (匹配单个字段)
*/
@Test
public void testMatchQuery() throws IOException {
//创建搜索请求对象,并设置类型
SearchRequest searchRequest = new SearchRequest("xc_course");
searchRequest.types("doc");
//构建搜索源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("name","spring开发").operator(Operator.OR));
searchSourceBuilder.fetchSource(new String[]{"name"}, new String[]{});
//将搜索源配置到搜索请求中,执行搜索,获取搜索响应结果
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
//获取所有搜索结果、总匹配数量
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
//遍历结果
for(SearchHit searchHit:searchHits){
String index = searchHit.getIndex();
String type = searchHit.getType();
String id = searchHit.getId();
float score = searchHit.getScore();
String sourceAsString = searchHit.getSourceAsString();
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
minimum_should_match
上边使用的 operator = or 表示只要有一个词匹配上就得分,如果实现三个词至少有两个词匹配如何实现?使用 minimum_should_match 可以指定文档匹配词的占比:比如搜索语句如下:
{
"query": {
"match": {
"description": {
"query": "spring开发框架",
"minimum_should_match": "80%"
}
}
}
}
spring开发框架 会被分为三个词:spring、开发、框架
设置 minimum_should_match:80% 表示,三个词在文档的匹配占比为 80%,即 3 * 0.8=2.4,向上取整得2,表示至少有 两个词 在文档中要匹配成功。
java客户端实现
searchSourceBuilder.query(QueryBuilders.matchQuery("description","spring开发框架").minimumShouldMatch("80%"));
multi query (匹配多个字段)
上边学习的 termQuery 和 matchQuery 一次只能匹配一个 Field,本节学习 multiQuery,一次可以匹配多个字段。
基本使用
单项匹配是在一个 field 中去匹配,多项匹配是拿关键字去多个 Field 中匹配,例子如下:
发送:post http://localhost:9200/xc_course/doc/_ search
拿关键字 spring css去匹配 name 和 description 字段。
{
"query": {
"multi_match": {
"query": "spring css",
"minimum_should_match": "50%",
"fields": [
"name",
"description"
]
}
}
}
提升权重
匹配多个字段时可以提升字段的 boost(权重)来提高得分
例子:提升 boost之前,执行下边的查询:
通过查询发现 Bootstrap 排在前边。提升 boost,通常关键字匹配上 name 的权重要比匹配上 description 的权重高,这里可以对name 的权重提升。
{
"query": {
"multi_match": {
"query": "spring框架",
"minimum_should_match": "50%",
"fields": [
"name^10",
"description"
]
}
}
}
name^10 表示权重提升 10 倍,执行上边的查询,发现 name 中包括 spring 关键字的文档排在前边。
java客户端实现
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring框架", "name", "description").minimumShouldMatch("50%");
multiMatchQueryBuilder.field("name",10);
searchSourceBuilder.query(multiMatchQueryBuilder);
布尔查询
布尔查询对应于 Lucene 的 BooleanQuery 查询,实现将多个查询组合起来。
三个参数:
must:文档必须匹配 must 所包括的查询条件,相当于 ANDshould:文档应该匹配
should 所包括的查询条件其中的一个或多个,相当于 ORmust_not:文档不能匹配
must_not 所包括的该查询条件,相当于 NOT
分别使用 must、should、must_not 测试下边的查询:
发送:POST http://localhost:9200/xc_course/doc/_ search
{
"_source": [
"name",
"studymodel",
"description"
],
"from": 0,
"size": 1,
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "spring框架",
"minimum_should_match": "50%",
"fields": [
"name^10",
"description"
]
}
},
{
"term": {
"studymodel": "201001"
}
}
]
}
}
}
must:表示必须,多个查询条件必须都满足。(通常使用must)
should:表示或者,多个查询条件只要有一个满足即可。
must_not:表示非。
java客户端实现
//创建multiMatch查询
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring框架", "name", "description").minimumShouldMatch("50%");
multiMatchQueryBuilder.field("name",10);
//创建term查询
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel", 201001);
//创建布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(multiMatchQueryBuilder);
boolQueryBuilder.must(termQueryBuilder);
过滤器
过虑是针对 搜索的结果 进行过虑,过虑器主要判断的是文档是否匹配,不去 计算和判断文档的匹配度得分,所以过虑器的 性能 比查询要高,且方便缓存,推荐尽量使用过虑器去实现查询或者 过虑器 和 查询 共同使用。
过虑器在布尔查询中使用,下边是在搜索结果的基础上进行过滤
发送:POST http://localhost:9200/xc_course/doc/_ search
{
"_source": [
"name",
"studymodel",
"description",
"price"
],
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "spring框架",
"minimum_should_match": "50%",
"fields": [
"name^10",
"description"
]
}
}
],
"filter": [
{
"term": {
"studymodel": "201001"
}
},
{
"range": {
"price": {
"gte": 60,
"lte": 100
}
}
}
]
}
}
}
range:范围过虑,保留大于等于 60 并且小于等于 100 的记录。
term:项匹配过虑,保留 studymodel 等于 201001 的记录。
注意:range 和 term 一次只能对一个 Field 设置范围过虑。
java客户端实现
/**
* 过滤器查询
*/
@Test
public void testFilterQuery() throws IOException {
//创建搜索请求对象,并设置类型
SearchRequest searchRequest = new SearchRequest("xc_course");
searchRequest.types("doc");
//构建搜索源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//创建multiMatch查询
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring框架", "name", "description").minimumShouldMatch("50%");
multiMatchQueryBuilder.field("name",10);
//布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(multiMatchQueryBuilder);
//过虑条件
boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel", "201001"));
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","description"}, new String[]{});
//向搜索请求对象中设置搜索源
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
//获取所有搜索结果、总匹配数量
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
//遍历结果
for(SearchHit searchHit:searchHits){
String index = searchHit.getIndex();
String type = searchHit.getType();
String id = searchHit.getId();
float score = searchHit.getScore();
String sourceAsString = searchHit.getSourceAsString();
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
排序
可以在字段上添加一个或多个排序,支持在 keyword、date、float 等类型上添加,text 类型的字段上不允许添加排序。
需求:过虑 0–10 元价格范围的文档,并且对结果进行排序,先按 studymodel 降序,再按价格升序
发送 POST http://localhost:9200/xc_course/doc/_ search
{
"_source": [
"name",
"studymodel",
"description",
"price"
],
"query": {
"bool": {
"filter": [
{
"range": {
"price": {
"gte": 0,
"lte": 100
}
}
}
]
}
},
"sort": [
{
"studymodel": "desc"
},
{
"price": "asc"
}
]
}
dest 表示降序,从大到小,asc 表示升序,从小到大
java客户端实现
/**
* 排序查询
*/
@Test
public void testSortQuery() throws IOException {
//创建搜索请求对象,并设置类型
SearchRequest searchRequest = new SearchRequest("xc_course");
searchRequest.types("doc");
//构建搜索源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//boolQuery搜索方式
//定义一个boolQuery
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//定义过虑器
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
searchSourceBuilder.query(boolQueryBuilder);
//添加排序
searchSourceBuilder.sort("studymodel", SortOrder.DESC);
searchSourceBuilder.sort("price", SortOrder.ASC);
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","description"}, new String[]{});
//向搜索请求对象中设置搜索源
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
//获取所有搜索结果、总匹配数量
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
//遍历结果
for(SearchHit searchHit:searchHits){
String index = searchHit.getIndex();
String type = searchHit.getType();
String id = searchHit.getId();
float score = searchHit.getScore();
String sourceAsString = searchHit.getSourceAsString();
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
高亮显示
高亮显示可以将搜索结果一个或多个字突出显示,以便向用户展示匹配关键字的位置。
在搜索语句中添加highlight即可实现,如下:
Post: http://127.0.0.1:9200/xc_course/doc/_ search
java实现
//Highlight
@Test
public void testHighlight() throws IOException, ParseException {
//搜索请求对象
SearchRequest searchRequest = new SearchRequest("xc_course");
//指定类型
searchRequest.types("doc");
//搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//boolQuery搜索方式
//先定义一个MultiMatchQuery
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("开发框架", "name", "description")
.minimumShouldMatch("50%")
.field("name", 10);
//定义一个boolQuery
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(multiMatchQueryBuilder);
//定义过虑器
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
searchSourceBuilder.query(boolQueryBuilder);
//设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<tag>");
highlightBuilder.postTags("</tag>");
highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
// highlightBuilder.fields().add(new HighlightBuilder.Field("description"));
searchSourceBuilder.highlighter(highlightBuilder);
//向搜索请求对象中设置搜索源
searchRequest.source(searchSourceBuilder);
//执行搜索,向ES发起http请求
SearchResponse searchResponse = client.search(searchRequest);
//搜索结果
SearchHits hits = searchResponse.getHits();
//匹配到的总记录数
long totalHits = hits.getTotalHits();
//得到匹配度高的文档
SearchHit[] searchHits = hits.getHits();
//日期格式化对象
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for(SearchHit hit:searchHits){
//文档的主键
String id = hit.getId();
//源文档内容
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//源文档的name字段内容
String name = (String) sourceAsMap.get("name");
//取出高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(highlightFields!=null){
//取出name高亮字段
HighlightField nameHighlightField = highlightFields.get("name");
if(nameHighlightField!=null){
Text[] fragments = nameHighlightField.getFragments();
StringBuffer stringBuffer = new StringBuffer();
for(Text text:fragments){
stringBuffer.append(text);
}
name = stringBuffer.toString();
}
}
//由于前边设置了源文档字段过虑,这时description是取不到的
String description = (String) sourceAsMap.get("description");
//学习模式
String studymodel = (String) sourceAsMap.get("studymodel");
//价格
Double price = (Double) sourceAsMap.get("price");
//日期
Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}