JAVAEE细细看 开发日常 03 (五) - ElasticSearch 搜索管理

搜索管理

1、准备环境
创建xc_course索引库。

创建如下映射
post:http://localhost:9200/xc_course/doc/_mapping

{
                "properties": {
                    "description": {
                        "type": "text",
                        "analyzer": "ik_max_word",
                        "search_analyzer": "ik_smart"
                    },
                    "name": {
                        "type": "text",
                        "analyzer": "ik_max_word",
                        "search_analyzer": "ik_smart"
                    },
					"pic":{
						"type":"text",
						"index":false
					},
                    "price": {
                        "type": "float"
                    },
                    "studymodel": {
                        "type": "keyword"
                    },
                    "timestamp": {
                        "type": "date",
                        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
                    }
                }
            }
插入原始数据
http://localhost:9200/xc_course/doc/1
{
"name": "Bootstrap开发",
"description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel": "201002",
"price":38.6,
"timestamp":"2018-04-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}
http://localhost:9200/xc_course/doc/2
{
"name": "java编程基础",
"description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
"studymodel": "201001",
"price":68.6,
"timestamp":"2018-03-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}
http://localhost:9200/xc_course/doc/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"price":88.6,
"timestamp":"2018-02-24 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}
简单搜索

简单搜索就是通过url进行查询,以get方式请求ES。
格式:get …/_search?q=…
q:搜索字符串。
例子:
?q=name:spring 搜索name中包括spring的文档。

2. DSL搜索

DSL(Domain Specific Language)是ES提出的基于json的搜索方式,在搜索时传入特定的json格式的数据来完成不同的搜索需求。

DSL比URI搜索方式功能强大,在项目中建议使用DSL方式来完成搜索。

查询所有文档

发送:post http://localhost:9200/xc_course/doc/_search

{
  "query": {
      "match_all": {}
  },
   "_source" : ["name","studymodel"]
}

结果说明:
took:本次操作花费的时间,单位为毫秒。
timed_out:请求是否超时
_shards:说明本次操作共搜索了哪些分片
hits:搜索命中的记录
hits.total : 符合条件的文档总数
hits.hits :匹配度较高的前N个文档
hits.max_score:文档匹配得分,这里为最高分
_score:每个文档都有一个匹配度得分,按照降序排列。
_source:显示了文档的原始内容。

_source:source源过虑设置,指定结果中所包括的字段有哪些

JavaClient:

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestSearch {

    @Autowired
    RestHighLevelClient client;

    @Autowired
    RestClient restClient;

	//搜索type下的全部记录
   @Test
    public void testSearchAll() throws IOException, ParseException {
        //搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xc_course");
        //设置类型
        searchRequest.types("doc");
        //搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //搜索全部
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        //source源字段过虑
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        //设置搜索源
        searchRequest.source(searchSourceBuilder);
        //执行搜索
        SearchResponse searchResponse = client.search(searchRequest);
        //搜索匹配结果
        SearchHits hits = searchResponse.getHits();
        //搜索总记录数
        long totalHits = hits.totalHits;
        //匹配度较高的前N个文档
        SearchHit[] searchHits = hits.getHits();
        //日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit hit:searchHits){
            //文档id
            String id = hit.getId();
            //源文档内容
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            //获取源文档name
            String name = (String) sourceAsMap.get("name");
            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);
        }
    }
分页查询

发送:post http://localhost:9200/xc_course/doc/_search

{
"from" : 0, "size" : 1,
"query": {
   "match_all": {}
 },
"_source" : ["name","studymodel"]

}

JavaClient:

...
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//设置分页参数
//当前页码
int page = 2;//页码
int size = 1;//每页显示个数
int from = (page - 1) * size;//起记录下标
searchSourceBuilder.from(from);
searchSourceBuilder.size(size);
...
Term Query

Term Query为精确查询,在搜索时会整体匹配关键字,不再将关键字分词。

发送:post http://localhost:9200/xc_course/doc/_search

{
   "query": {
      "term" : {
          "name": "spring"
      }
	},
 	"_source" : ["name","studymodel"]
 }

JavaClient:

...
//搜索请求对象
SearchRequest searchRequest = new SearchRequest("xc_course");
//设置类型
searchRequest.types("doc");
//搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//TermQuery
searchSourceBuilder.query(QueryBuilders.termQuery("name","spring"));
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
//设置搜索源
searchRequest.source(searchSourceBuilder);
//执行搜索
SearchResponse searchResponse = client.search(searchRequest);
...
根据id精确匹配

ES提供根据多个id值匹配的方法:
post: http://127.0.0.1:9200/xc_course/doc/_search

{
    "query": {
        "ids" : {
            "type" : "doc",
            "values" : ["3", "4", "100"]
        }
    }
}

JavaClient:
通过termsQuery进行查询,代码如下:

//搜索请求对象
SearchRequest searchRequest = new SearchRequest("xc_course");
//设置类型
searchRequest.types("doc");
//搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//主键
String[] ids = new String[]{"1","2"};
//TermQuery
searchSourceBuilder.query(QueryBuilders.termsQuery("_id", ids));
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
//设置搜索源
searchRequest.source(searchSourceBuilder);
//执行搜索
SearchResponse searchResponse = client.search(searchRequest);
match Query

1、基本使用

​ match Query即全文检索,它的搜索方式是先将搜索字符串分词,再使用各各词条从索引中搜索。

match query与Term query区别是match query在搜索前先将搜索关键字分词,再拿各各词语去索引中搜索。

发送:post http://localhost:9200/xc_course/doc/_search

{
	"query": {
    "match" : {
        "description" : {
            "query" : "spring开发",
            "operator" : "or"
        }
    }
  }
}

query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用逗号分隔也可以不用。
operator:or 表示 只要有一个词在文档中出现则就符合条件,and表示每个词都在文档中出现则才符合条件。
上边的搜索的执行过程是:
1、将“spring开发”分词,分为spring、开发两个词
2、再使用spring和开发两个词去匹配索引中搜索。
3、由于设置了operator为or,只要有一个词匹配成功则就返回该文档。
JavaClient:

//根据关键字搜索
@Test
    public void testMatchQuery() throws IOException, ParseException {
        //搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xc_course");
        //设置类型
        searchRequest.types("doc");
        //搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //MatcherQuery
        searchSourceBuilder.query(QueryBuilders.matchQuery("description","spring开发").operator(Operator.OR));
        //source源字段过虑
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        //设置搜索源
        searchRequest.source(searchSourceBuilder);
        //执行搜索
        SearchResponse searchResponse = client.search(searchRequest);
        //搜索匹配结果
        SearchHits hits = searchResponse.getHits();
        //搜索总记录数
        long totalHits = hits.totalHits;
        //匹配度较高的前N个文档
        SearchHit[] searchHits = hits.getHits();
        //日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit hit:searchHits){
            //文档id
            String id = hit.getId();
            //源文档内容
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            //获取源文档name
            String name = (String) sourceAsMap.get("name");
            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);
        }
    }
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,表示至少有两个词在文档中要匹配成功。
对应的RestClient如下:

//MatcherQuery
        searchSourceBuilder.query(QueryBuilders.matchQuery("description","spring开发框架")
                                    .operator(Operator.OR)
                                    .minimumShouldMatch("80%"));
     
 
multi Query

termQuery和matchQuery一次只能匹配一个Field,本节学习multiQuery,一次可以匹配多个字段。

1、基本使用
单项匹配是在一个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(权重)来提高得分

例子:
提升boost之前,执行下边的查询:

{
   "query": {
    "multi_match" : {
            "query" : "spring css",
            "minimum_should_match": "50%",
            "fields": [ "name", "description" ]
     }
    }
}

通过查询发现Bootstrap排在前边。

提升boost,通常关键字匹配上name的权重要比匹配上description的权重高,这里可以对name的权重提升。

{
   "query": {
    "multi_match" : {
            "query" : "spring css",
            "minimum_should_match": "50%",
            "fields": [ "name^10", "description" ]
     }
    }
}

“name^10” 表示权重提升10倍,执行上边的查询,发现name中包括spring关键字的文档排在前边。

JavaClient:

//搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//MultiMatcherQuery
MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("spring css", "name", "description")
        .minimumShouldMatch("50%")
        .field("name", 10);
searchSourceBuilder.query(matchQueryBuilder);

布尔查询

布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来。

三个参数:

must:文档必须匹配must所包括的查询条件,相当于 “AND”
should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
must_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 css",
            	"minimum_should_match": "50%",
            	"fields": [ "name^10", "description" ]
    			}
   			},
   			{
   				"term":{
   					"studymodel" : "201001"
   				}
   			}
   		]
   	 }
    }
}

must:表示必须,多个查询条件必须都满足。(通常使用must)

should:表示或者,多个查询条件只要有一个满足即可。

must_not:表示非。
JavaClient:

//BoolQuery,将搜索关键字分词,拿分词去索引库搜索
//搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//MultiMatcherQuery
MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("spring css", "name", "description")
        .minimumShouldMatch("50%")
        .field("name", 10);
//TermQuery
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel", "201001");
//boolQueryBuilder
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//将MultiMatcherQuery和TermQuery组织在一起
boolQueryBuilder.must(matchQueryBuilder);
boolQueryBuilder.must(termQueryBuilder);
searchSourceBuilder.query(boolQueryBuilder);
过虑器

过虑是针对搜索的结果进行过虑,过虑器主要判断的是文档是否匹配,不去计算和判断文档的匹配度得分,所以过虑器性能比查询要高,且方便缓存,推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用。

过虑器在布尔查询中使用,下边是在搜索结果的基础上进行过虑:

{
	"_source" : [ "name", "studymodel", "description","price"],
   "query": {
   	 "bool" : {
   		"must":[
   			{
   				"multi_match" : {
            	"query" : "spring css",
            	"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设置范围过虑。

client:

//搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//MultiMatcherQuery
MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("spring css", "name", "description")
        .minimumShouldMatch("50%")
        .field("name", 10);

//boolQueryBuilder
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//将MultiMatcherQuery和TermQuery组织在一起
boolQueryBuilder.must(matchQueryBuilder);
//添加过虑器
//项过虑
boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel","201001"));
//范围过虑
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
searchSourceBuilder.query(boolQueryBuilder);
排序

可以在字段上添加一个或多个排序,支持在keyword、date、float等类型上添加,text类型的字段上不允许添加排序。

发送 POST http://localhost:9200/xc_course/doc/_search

过虑0–10元价格范围的文档,并且对结果进行排序,先按studymodel降序,再按价格升序

{
	"_source" : [ "name", "studymodel", "description","price"],
   "query": {
   	 "bool" : {
   		"filter": [ 
        	{ "range": { "price": { "gte": 0 ,"lte" : 100}}} 
		]
   	 }
    },
   "sort" : [
   	  {
         "studymodel" : "desc"
      },
   		{
         "price" : "asc"
      }
   	]
}

client:

@Test
public void testSort() throws IOException, ParseException {
    //搜索请求对象
    SearchRequest searchRequest = new SearchRequest("xc_course");
    //设置类型
    searchRequest.types("doc");
    //搜索源构建对象
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //source源字段过虑
    searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
    //boolQueryBuilder
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    //添加过虑器
    //范围过虑
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
    searchSourceBuilder.query(boolQueryBuilder);
    //设置搜索源
    searchRequest.source(searchSourceBuilder);
    //设置排序
    searchSourceBuilder.sort("studymodel", SortOrder.DESC);
    searchSourceBuilder.sort("price", SortOrder.ASC);
    //执行搜索
    SearchResponse searchResponse = client.search(searchRequest);
    //搜索匹配结果
    SearchHits hits = searchResponse.getHits();
    //搜索总记录数
    long totalHits = hits.totalHits;
    //匹配度较高的前N个文档
    SearchHit[] searchHits = hits.getHits();
    //日期格式化对象
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    for(SearchHit hit:searchHits){
        //文档id
        String id = hit.getId();
        //源文档内容
        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
        //获取源文档name
        String name = (String) sourceAsMap.get("name");
        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);
    }
}
高亮显示

高亮显示可以将搜索结果一个或多个字突出显示,以便向用户展示匹配关键字的位置。

在搜索语句中添加highlight即可实现,如下:

Post: http://127.0.0.1:9200/xc_course/doc/_search

{
	"_source" : [ "name", "studymodel", "timestamp","price"],
   "query": {
   	 "bool" : {
   		"must":[
   			{
   				"multi_match" : {
            	"query" : "开发框架",
            	"minimum_should_match": "50%",
            	"fields": [ "name^10", "description" ]
    			}
   			}
   		],
   		"filter": [ 
        	{ "range": { "price": { "gte": 0 ,"lte" : 100}}} 
		]
   	 }
    },
   "sort" : [
   		{
         "price" : "asc"
      }
   	],
   	"highlight": {
    "pre_tags": ["<tag1>"],
    "post_tags": ["</tag2>"], 
    "fields": {
      "name": {},
      "description":{}
    }
  }
}

client代码如下:

@Test
public void testHighLight() throws IOException, ParseException {
    //搜索请求对象
    SearchRequest searchRequest = new SearchRequest("xc_course");
    //设置类型
    searchRequest.types("doc");
    //搜索源构建对象
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //source源字段过虑
    searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
    //MultiMatcherQuery
    MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("开发框架", "name", "description")
            .minimumShouldMatch("80%")
            .field("name", 10);
    //boolQueryBuilder
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.must(matchQueryBuilder);
    //添加过虑器
    //范围过虑
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
    searchSourceBuilder.query(boolQueryBuilder);
    //设置搜索源
    searchRequest.source(searchSourceBuilder);
    //设置排序
    searchSourceBuilder.sort("studymodel", SortOrder.DESC);
    searchSourceBuilder.sort("price", SortOrder.ASC);
    //设置高亮
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.preTags("<tag>");
    highlightBuilder.postTags("</tag>");
    highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
    searchSourceBuilder.highlighter(highlightBuilder);
    //执行搜索
    SearchResponse searchResponse = client.search(searchRequest);
    //搜索匹配结果
    SearchHits hits = searchResponse.getHits();
    //搜索总记录数
    long totalHits = hits.totalHits;
    //匹配度较高的前N个文档
    SearchHit[] searchHits = hits.getHits();
    //日期格式化对象
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    for(SearchHit hit:searchHits){
        //文档id
        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 nameField = highlightFields.get("name");
            if(nameField!=null){
                Text[] fragments = nameField.fragments();
                StringBuffer stringBuffer = new StringBuffer();
                for(Text text:fragments){
                    stringBuffer.append(text);
                }
                name = stringBuffer.toString();
            }

        }


        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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值