elasticsearch-java api之搜索(一)

1、全文搜索两个最重要的方面是:
1)相关性(Relevance)
它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是 TF/IDF 方法(参见 相关性的介绍)、地理位置邻近、模糊相似,或其他的某些算法。
2)分析(Analysis)
它是将文本块转换为有区别的、规范化的 token 的一个过程,(参见 分析的介绍) 目的是为了(a)创建倒排索引以及(b)查询倒排索引。

官方各种实例:https://www.elastic.co/guide/cn/elasticsearch/guide/current/full-text-search.html

2、文本查询可以划分成两大家族:

1)基于词项的查询

如 term 或 fuzzy 这样的底层查询不需要分析阶段,它们对单个词项进行操作。用 term 查询词项 Foo 只要在倒排索引中查找 准确词项 ,并且用 TF/IDF 算法为每个包含该词项的文档计算相关度评分 _score 。

记住 term 查询只对倒排索引的词项精确匹配,这点很重要,它不会对词的多样性进行处理(如, foo 或 FOO )。这里,无须考虑词项是如何存入索引的。如果是将 ["Foo","Bar"] 索引存入一个不分析的( not_analyzed )包含精确值的字段,或者将 Foo Bar 索引到一个带有 whitespace 空格分析器的字段,两者的结果都会是在倒排索引中有 Foo 和 Bar 这两个词。

2)基于全文的查询
像 match 或 query_string 这样的查询是高层查询,它们了解字段映射的信息:
如果查询 日期(date) 或 整数(integer) 字段,它们会将查询字符串分别作为日期或整数对待。
如果查询一个( not_analyzed )未分析的精确值字符串字段, 它们会将整个查询字符串作为单个词项对待。
但如果要查询一个( analyzed )已分析的全文字段, 它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表。
一旦组成了词项列表,这个查询会对每个词项逐一执行底层的查询,再将结果合并,然后为每个文档生成一个最终的相关度评分。

我们很少直接使用基于词项的搜索,通常情况下都是对全文进行查询,而非单个词项。

注:

当我们想要查询一个具有精确值的 not_analyzed 未分析字段之前, 需要考虑,是否真的采用评分查询,或者非评分查询会更好。单词项查询通常可以用是、非这种二元问题表示,所以更适合用过滤, 而且这样做可以有效利用缓存

GET /_search
{
    "query": {
        "constant_score": {
            "filter": {
                "term": { "gender": "female" }
            }
        }
    }
}


一、全文检索:

1、match 匹配查询

1)match all:匹配所有文档

public static void matchAll(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchAllQuery();
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			//sh.getId();
			System.out.println(source);
		}
	}

2)match query:

public static void matchQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchQuery("title","java hadoop").operator(Operator.OR);
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}

1.说明:

A、对query的词使用分词,然后去指定字段的倒排索引中去搜索;

B、可以对query的词指定operator逻辑,默认是or

2.执行步骤:

  • 检查字段类型 :标题 title 字段是一个 string 类型( analyzed )已分析的全文字段,这意味着查询字符串本身也应该被分析。
  • 分析查询字符串 :将查询的字符串 QUICK! 传入标准分析器中,输出的结果是单个项 quick 。因为只有一个单词项,所以 match 查询执行的是单个底层 term 查询。
  • 查找匹配文档 :用 term 查询在倒排索引中查找 quick 然后获取一组包含该项的文档。
  • 为每个文档评分 :用 term 查询计算每个文档相关度评分 _score ,这是种将 词频(term frequency,即词 quick 在相关文档的 title 字段中出现的频率)和反向文档频率(inverse document frequency,即词 quick 在所有文档的 title 字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。

3)multi match query:对于query内容,可以同时指定多个field,不同field之间是或的关系

public static void matchMultiQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.multiMatchQuery("java code","title","name").operator(Operator.OR);
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}

2、query String:

public static void queryString(String indexName,String indexType) {
		QueryStringQueryBuilder queryBuilders = QueryBuilders.queryStringQuery("java hadoop")
				.field("title");  
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(queryBuilders).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}
和match一样。

二、词项查询:

1、term 短语查询:

public static void termQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.termsQuery("title", "hadoop","lucene");
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}
对query的词不经过分词,去指定字段的倒排中准确的匹配,一般对于该类查询建议使用过滤器,应为过滤器有缓存工程,可以提高性能。

2、前缀查询:

public static void prefixQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.prefixQuery("other", "学");
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}
对query的词不经过分词,去指定字段的倒排中匹配符合query词的前缀文档

3、短语匹配:

1)查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含 全部 搜索词项,且 位置 与搜索词项相同的文档。默认中间不夹杂其他词项,可以通过slop设置位置距离(位置距离可以循环)

public static void matchPhraseQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchPhraseQuery("title", "kafka java").slop(3);
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}

4、短语前缀匹配:

1)查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含 全部 搜索词项,且 位置 与搜索词项相同的文档。默认中间不夹杂其他词项,可以通过slop设置位置距离(位置距离可以循环)
2)还可以设置max_expansion 来决定前缀匹配的最大数量

public static void matchPhrasePrefixQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchPhrasePrefixQuery("title", "elasticsearch java s").prefixLength(2).slop(3).maxExpansions(2);
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		//long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			float score = sh.getScore();
			System.out.println("source:"+source+",id:"+id+",score:"+score);
		}
	}
5、模糊查询(fuzzy):

对查询的词进行模糊处理(例如:输入jiva,但实际想查询java),es使用编辑距离来做模糊查询的条件,可以设置如下参数:

1)fuzziness:模糊度,推荐auto
2)prefixLength:不会“模糊化”的初始字符数,这有助于减少必须检查的术语数量,默认为0。
3)maxExpansions:模糊查询将扩展到的最大项数,默认为50。越小越有助于性能

对于字符串,使用编辑距离进行模糊;对于数值或者日期类型,使用加减一个模糊范围进行查询。

public static void fuzzyQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.fuzzyQuery("title", "jaui").fuzziness(Fuzziness.fromEdits(2)).prefixLength(2).maxExpansions(50);
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		//long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			float score = sh.getScore();
			System.out.println("source:"+source+",id:"+id+",score:"+score);
		}
	}

6、通配符查询(wildcard):

wildcard 和 regexp 查询的工作方式与 prefix 查询完全一样,它们也需要扫描倒排索引中的词列表才能找到所有匹配的词,然后依次获取每个词相关的文档 ID ,与 prefix 查询的唯一不同是:它们能支持更为复杂的匹配模式。

public static void wildCardQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.wildcardQuery("title", "el*");
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		//long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			float score = sh.getScore();
			System.out.println("source:"+source+",id:"+id+",score:"+score);
		}
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值