个人专题目录](https://www.jianshu.com/p/140e2a59db2c)
elasticsearch文档复合查询及排序
1. 复合查询
1.1 bool查询
bool (布尔)过滤器。 这是个复合过滤器(compound filter) ,它可以接受多个其他过滤器作为参数,并将这些过滤器结合成各式各样的布尔(逻辑)组合。
格式
一个 bool 过滤器由三部分组成:
{
"bool" : {
"must" : [],
"should" : [],
"must_not" : [],
}
}
参数定义:
must
所有的语句都 必须(must) 匹配,与 AND 等价。
must_not
所有的语句都不能(must not) 匹配,与 NOT 等价。
should
至少有一个语句要匹配,与 OR 等价。
例如查询条件:
POST /book-index/_search
{
"from": 0,
"size": 100,
"query": {
"bool": {
"must": [
{
"match": {
"title": {
"query": "三星",
"operator": "OR",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1
}
}
}
],
"must_not": [
{
"term": {
"brandName": {
"value": "诺基亚",
"boost": 1
}
}
}
],
"should": [
{
"term": {
"categoryName": {
"value": "手机",
"boost": 1
}
}
},
{
"term": {
"categoryName": {
"value": "平板电视",
"boost": 1
}
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
}
}
@Override
public void boolQuery(String indexName) throws Exception {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.should(QueryBuilders.termQuery("categoryName", "手机"));
queryBuilder.should(QueryBuilders.termQuery("categoryName", "平板电视"));
queryBuilder.must(QueryBuilders.matchQuery("title", "三星"));
queryBuilder.mustNot(QueryBuilders.termQuery("brandName", "诺基亚"));
builder(indexName, queryBuilder);
}
@Test
public void testBoolQuery() throws Exception {
baseQuery.boolQuery(Constants.INDEX_NAME);
}
1.2 booting查询
该查询用于将两个查询封装在一起,并降低其中一个查询所返回文档的分值。
它接受一个positive查询和一个negative查询。只有匹配了positive查询的文档才会被包含到结果集中,但是同时匹配了negative查询的文档会被降低其相关度,通过将文档原本的_score和negative_boost参数进行相乘来得到新的_score。因此,negative_boost参数必须小于1.0。在上面的例子中,任何包含了指定负面词条的文档的_score都会是其原本_score的一半。
例如:
在互联网上搜索"苹果"也许会返回,水果或者各种食谱的结果。我们可以通过排除“水果 乔木 维生素”和这类单词,结合bool查询中的must_not子句,将结果范围缩小到只剩苹果手机。
POST /book-index/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"title": {
"query": "三星 手机 联通",
"operator": "AND",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1.0
}
}
},
"negative": {
"match": {
"title": {
"query": "白色 黑色",
"operator": "OR",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1.0
}
}
},
"negative_boost": 0.1,
"boost": 1.0
}
}
}
@Override
public void boostingQuery(String indexName, String positiveField, String positiveKeyWord, String negativeField, String negativeKeyWord) throws Exception {
MatchQueryBuilder matchQueryPositiveBuilder = QueryBuilders.matchQuery(positiveField, positiveKeyWord);
MatchQueryBuilder matchQueryNegativeBuilder = QueryBuilders.matchQuery(negativeField, negativeKeyWord);
BoostingQueryBuilder boostingQueryBuilder = QueryBuilders.boostingQuery(matchQueryPositiveBuilder,
matchQueryNegativeBuilder).negativeBoost(0.1f);
builder(indexName, boostingQueryBuilder);
}
/**
* 它接受一个positive查询和一个negative查询。只有匹配了positive查询的文档才会被包含到结果集中,
* 但是同时匹配了negative查询的文档会被降低其相关度,通过将文档原本的_score和negative_boost参数进行相乘来得到新的_score。
* 因此,negative_boost参数必须小于1.0。在上面的例子中,任何包含了指定负面词条的文档的_score都会是其原本_score的一半。
*
* @throws Exception
*/
@Test
public void testBoostingQuery() throws Exception {
//都可以查出来,只是SCORE值减少,可以通过SCORE值来去掉排名在后面的
baseQuery.boostingQuery(Constants.INDEX_NAME, "title", "三星 手机 联通", "title", "白色 黑色");
}
2. 排序(Sort)
默认情况下,结果集会按照相关性进行排序 -- 相关性越高,排名越靠前。
为了使结果可以按照相关性进行排序,我们需要一个相关性的值。在ElasticSearch的查询结果中, 相关性分值会用_score
字段来给出一个浮点型的数值,所以默认情况下,结果集以_score
进行倒序排列。
注意:在Elasticsearch中,如果使用text类型的字段作为排序依据,会有问题。Elasticsearch需要对text类型字段数据做分词处理。如果使用text类型字段做排序,Elasticsearch给出的排序结果未必友好,毕竟分词后,先使用哪一个单词做排序都是不合理的。所以Elasticsearch中默认情况下不允许使用text类型的字段做排序,如果需要使用字符串做结果排序,则可使用keyword类型字段作为排序依据,因为keyword字段不做分词处理。
如果对一个text field进行排序,结果往往不准确,因为分词后是多个单词,再排序就不是我们想要的结果了。
通常解决方案是,将一个text field建立两次索引,一个分词,用来进行搜索;一个不分词,用来进行排序。
fielddate:true
{
"query": {
"match_all": {}
},
"sort": [
{
"title.keyword": {
"order": "desc"
}
}
]
}
POST /book-index/_search
{
"query": {
"match": {
"brandName": {
"query": "三星",
"operator": "OR",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1
}
}
},
"sort": [
{
"id": {
"order": "desc"
}
}
]
}
@Override
public void sortQuery(String indexName, String field, String keyWord, String sort, SortOrder sortOrder) throws IOException {
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery(field, keyWord));
searchSourceBuilder.sort(sort, sortOrder);
searchRequest.source(searchSourceBuilder);
log.info("source:" + searchRequest.source());
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
log.info("count:" + hits.getTotalHits());
SearchHit[] h = hits.getHits();
for (SearchHit hit : h) {
log.info("结果" + hit.getSourceAsMap() + ",score:" + hit.getScore());
}
}
@Test
public void testSortQueryBySort() throws IOException {
sortQuery.sortQuery(Constants.INDEX_NAME, "brandName", "三星", "id", SortOrder.DESC);
}
网友评论