本文目录
elasticsearch简介
Elastic 的底层是开源库 Lucene。但是,你没法直接用 Lucene,必须自己写代码去调用它的接口。Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。
中文文档链接: https://es.xiaoleilu.com/010_Intro/30_Tutorial_Search.html
官方网站:https://www.elastic.co/cn/
elasticsearch的Type区别
核心数据类型:
1.字符串类型:text、keyworde(不分词)
2.数值型:long、integer、short、byte、dobule、float、half_float等
3.日期类型: date
4.布尔类型: boolean
5.二进制类型: binary
6.范围类型:integer_range、float_range、long_range、double_range、date_range
复杂数据类型
1.数组类型:array
2.对象类型:object
3.嵌套类型:nested object
4.地理位置数据类型:geo_point、geo_shape
5.专用类型:ip(记录ip地址)、completion(实现自动补全)、token_count(记录分词数)、murmur3(记录字符串hash值)
倒排索引
index内部模型
自动识别规则
在不指定type值的情况需下,String默认为text并且有一个子字段keyword。
指定分词器创建索引:
PUT test4_bean
{
"settings": {
"analysis": {
"analyzer":{
"ik_max_word":{
"tokenizer":"ik_max_word"
}
}
}
},
"mappings": {
"test4_bean": {
"properties": {
"testStringKW": {
"type": "keyword"
},
"categoryId": {
"type": "long"
},
"testStringIk": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
public class TestBean {
@Id
private Long categoryId;
/**
* 类别名称
*/
private String testString;
@Field(type = FieldType.Keyword)
private String testStringKW;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
private String testStringIk;
}
{
"mapping": {
"test_bean": {
"properties": {
"categoryId": {
"type": "long"
},
"testString": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"testString123": {
"type": "text",
"analyzer": "ik_max_word"
},
"testStringIk": {
"type": "text",
"analyzer": "ik_max_word"
},
"testStringKW": {
"type": "keyword"
}
}
}
}
}
匹配查询
elasticsearch 中term与match区别
term是精确查询
match是模糊查询
使用QueryBuilder
* termQuery(“key”, obj) 完全匹配
* termsQuery(“key”, obj1, obj2…) 一次匹配多个值
* matchQuery(“key”, Obj) 单个匹配, field不支持通配符, 前缀具高级特性(会分词)
* matchPhraseQuery(“key”, Obj) 单个匹配, field不支持通配符, 前缀具高级特性(短语 不分词)
* multiMatchQuery(“text”, “field1”, “field2”…); 匹配多个字段, field有通配符忒行
* matchAllQuery(); 匹配所有文件
* fuzzyQuery 模糊查询(个人尝试,结果并不如意)
* wildcardQuery模糊查询 (类似mysql的like匹配,属于精确查询)
multiMatchQuery
中文单字:默认>ik>keyword
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("测", "testString");
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(multiMatchQueryBuilder);
MultiMatchQueryBuilder multiMatchQueryBuilderIk = QueryBuilders.multiMatchQuery("测","testStringIk");
BoolQueryBuilder queryBuilderIk = QueryBuilders.boolQuery().must(multiMatchQueryBuilderIk);
MultiMatchQueryBuilder multiMatchQueryBuilderKw = QueryBuilders.multiMatchQuery("测", "testStringKW");
BoolQueryBuilder queryBuilderKw = QueryBuilders.boolQuery().must(multiMatchQueryBuilderKw);
结果:
CategoryBean=[TestBean(categoryId=255357, testString=预测未知, testStringKW=预测未知, testStringIk=预测未知), TestBean(categoryId=276288, testString=预测未
size=4
Total=4
CategoryBeanIk=[TestBean(categoryId=281402, testString=策测解金8, testStringKW=策测解金8, testStringIk=策测解金8)]
sizeIk=1
TotalIk=1
CategoryBeanKw=[]
sizeKw=0
TotalKw=0
纯英文:ik>默认>keyword
主要是引文ik会将英文跟数字进行分词处理,而默认的不会
由于纯英文不会分词,所以这种查询方法并不推荐。(还有许多匹配数据没出来 例如:dskfjggg)
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("gg", "testString");
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(multiMatchQueryBuilder);
MultiMatchQueryBuilder multiMatchQueryBuilderIk = QueryBuilders.multiMatchQuery("gg","testStringIk");
BoolQueryBuilder queryBuilderIk = QueryBuilders.boolQuery().must(multiMatchQueryBuilderIk);
MultiMatchQueryBuilder multiMatchQueryBuilderKw = QueryBuilders.multiMatchQuery("gg", "testStringKW");
BoolQueryBuilder queryBuilderKw = QueryBuilders.boolQuery().must(multiMatchQueryBuilderKw);
结果:
CategoryBean=[TestBean(categoryId=246141, testString=范德萨gg, testStringKW=范德萨gg, testStringIk=范德萨gg), TestBean(categoryId=259385, testString=GG思密达, testStringKW=GG思密达, testStringIk=GG思密达)]
size=2
Total=2
CategoryBeanIk=[TestBean(categoryId=259385, testString=GG思密达, testStringKW=GG思密达, testStringIk=GG思密达), TestBean(categoryId=206079, testString=gg0117, testStringKW=gg0117, testStringIk=gg0117), TestBean(categoryId=246141, testString=范德萨gg, testStringKW=范德萨gg, testStringIk=范德萨gg)]
sizeIk=3
TotalIk=3
CategoryBeanKw=[]
sizeKw=0
TotalKw=0
wildcardQuery
相当于mysql的like匹配
默认=ik>keyword
我做的测试里面默认的是等于ik因为这个跟分词无关,但是keyword会少一点,是因为不区分大小写。
ik与默认分词其实也不能够区分大小写,在我用“GG”测试的时候,默认与ik都没有找到数据,keyword能找到数据。
解决此问题可以参考 链接 :https://blog.csdn.net/wessiyear/article/details/79281181
WildcardQueryBuilder wildcardQueryBuilder1 = QueryBuilders.wildcardQuery("testString","*gg*");
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(wildcardQueryBuilder1);
WildcardQueryBuilder wildcardQueryBuilderIk = QueryBuilders.wildcardQuery("testStringIk","*gg*");
BoolQueryBuilder queryBuilderIk = QueryBuilders.boolQuery().must(wildcardQueryBuilderIk);
WildcardQueryBuilder wildcardQueryBuilderKw = QueryBuilders.wildcardQuery("testStringKW","*gg*");
BoolQueryBuilder queryBuilderKw = QueryBuilders.boolQuery().must(wildcardQueryBuilderKw);
结果:
CategoryBean=[TestBean(categoryId=248259, testString=GGYY社长, testStringKW=GGYY社长, testStringIk=GGYY社长), TestBean(categoryId=251580, testString=GGQQ, testStringKW=GGQQ, testStringIk=GGQQ),....
size=10
Total=24
CategoryBeanIk=[TestBean(categoryId=248259, testString=GGYY社长, testStringKW=GGYY社长, testStringIk=GGYY社长), TestBean(categoryId=251580, testString=GGQQ, testStringKW=GGQQ, testStringIk=GGQQ).....
sizeIk=10
TotalIk=24
CategoryBeanKw=[TestBean(categoryId=249214, testString=aggie, testStringKW=aggie, testStringIk=aggie), TestBean(categoryId=206079, testString=gg0117, testStringKW=gg0117, testStringIk=gg0117).....
sizeKw=10
TotalKw=21
should用法
相当于or查询
WildcardQueryBuilder wildcardQueryBuilder1 = QueryBuilders.wildcardQuery("testString","*aa*");
WildcardQueryBuilder wildcardQueryBuilder2 = QueryBuilders.wildcardQuery("testString","*gg*");
WildcardQueryBuilder wildcardQueryBuilder3 = QueryBuilders.wildcardQuery("testString","*123*");
// BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(wildcardQueryBuilder1);
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().should(wildcardQueryBuilder1).should(wildcardQueryBuilder2).should(wildcardQueryBuilder3);
结果:
CategoryBean=[TestBean(categoryId=249161, testString=agg123, testStringKW=agg123, testStringIk=agg123), TestBean(categoryId=257418, testString=wzq123, testStringKW=wzq123, testStringIk=wzq123), TestBean(categoryId=257468, testString=liaowen123, testStringKW=liaowen123, testString.....
size=10
Total=180
keyword解决大小写问题
生成:
PUT test_bean
{
"settings": {
"analysis": {
"normalizer": {
"lowercase": {
"type": "custom",
"filter": [
"lowercase"
]
}
}
}
},
"mappings": {
"test_bean": {
"properties": {
"testStringKW": {
"type": "keyword"
},
"categoryId": {
"type": "long"
},
"testString": {
"type": "text",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
},
"testStringIk": {
"type": "text",
"analyzer": "ik_max_word"
},
"test_bean": {
"type": "keyword",
"normalizer": "lowercase"
}
}
}
}
}
或者代码中
json文件放到 resource下:
{
"index": {
"analysis": {
"normalizer": {
"lower_case_normalizer": {
"type": "custom",
"char_filter": [],
"filter": [
"lowercase"
]
}
}
}
}
}
mapping:
```java
{
"mapping": {
"test_bean": {
"properties": {
"categoryId": {
"type": "long"
},
"testString": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"testStringIk": {
"type": "text",
"analyzer": "ik_max_word"
},
"testStringKW": {
"type": "keyword"
},
"test_bean": {
"type": "keyword",
"normalizer": "lowercase"
}
}
}
}
}
api使用
分词效果测试
POST test_bean/_analyze
{
"field": "testStringIk",
"text": "你弘法寺方艾欧是"
}
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "Hello world"
}
POST _analyze
{
"analyzer": "ik_max_word",
"text": "你好中国,我爱北京"
}
POST _analyze
{
"tokenizer": "keyword",
"char_filter": ["html_strip"],
"text": "<p>I'm s"
}
POST _analyze
{
"tokenizer": "ngram",
"text": "sjfjkajfskljfsal"
}
POST _analyze
{
"tokenizer": "path_hierachy",
"text":"/abc/oijo/jgbad"
}
POST _analyze
{
"text":"拜仑财经真的很不错",
"tokenizer": "standard",
"filter": [
"stop",
"lowercase",
{
"type":"ngram",
"min_gram":1,
"max_gram":2
}
]
}
自定义分词器结构
无论是内置的分析器(analyzer),还是自定义的分析器(analyzer),都由三种构件块组成的:character filters , tokenizers , token filters。
内置的analyzer将这些构建块预先打包到适合不同语言和文本类型的analyzer中。
Character filters (字符过滤器)
字符过滤器以字符流的形式接收原始文本,并可以通过添加、删除或更改字符来转换该流。
举例来说,一个字符过滤器可以用来把阿拉伯数字(٠١٢٣٤٥٦٧٨٩)转成成Arabic-Latin的等价物(0123456789)。
一个分析器可能有0个或多个字符过滤器,它们按顺序应用。
(PS:类似Servlet中的过滤器,或者拦截器,想象一下有一个过滤器链)
Tokenizer (分词器)
一个分词器接收一个字符流,并将其拆分成单个token (通常是单个单词),并输出一个token流。例如,一个whitespace分词器当它看到空白的时候就会将文本拆分成token。它会将文本“Quick brown fox!”转换为[Quick, brown, fox!]
(PS:Tokenizer 负责将文本拆分成单个token ,这里token就指的就是一个一个的单词。就是一段文本被分割成好几部分,相当于Java中的字符串的 split )
分词器还负责记录每个term的顺序或位置,以及该term所表示的原单词的开始和结束字符偏移量。(PS:文本被分词后的输出是一个term数组)
一个分析器必须只能有一个分词器
Token filters (token过滤器)
token过滤器接收token流,并且可能会添加、删除或更改tokens。
例如,一个lowercase token filter可以将所有的token转成小写。stop token filter可以删除常用的单词,比如 the 。synonym token filter可以将同义词引入token流。
不允许token过滤器更改每个token的位置或字符偏移量。
一个分析器可能有0个或多个token过滤器,它们按顺序应用。
stop 接受以下参数:
stopwords : 一个预定义的停止词列表(比如,englisht)或者是一个包含停止词的列表。默认是 english
stopwords_path : 包含停止词的文件路径。这个路径是相对于Elasticsearch的config目录的一个路径
stopwords指定停词
PUT test4_bean
{
"settings": {
"analysis": {
"analyzer":{
"ik_max_word":{
"tokenizer":"ik_max_word",
"type":"stop",
"stopwords":["你爸爸","他哥"]
}
}
}
},
"mappings": {
"test4_bean": {
"properties": {
"testStringKW": {
"type": "keyword"
},
"categoryId": {
"type": "long"
},
"testStringIk": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
stopwords_path指定停词(好像只能识别txt文件)
PUT test4_bean
{
"settings": {
"analysis": {
"analyzer":{
"ik_max_word":{
"tokenizer":"ik_max_word",
"filter":["extra_stop"]
}
},
"filter": {
"extra_stop":{
"type":"stop",
"stopwords_path":"analysis-ik/extra_stopword.txt"
}
}
}
},
"mappings": {
"test4_bean": {
"properties": {
"testStringKW": {
"type": "keyword"
},
"categoryId": {
"type": "long"
},
"testStringIk": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
POST test4_bean/_analyze
{
"analyzer": "ik_max_word",
"text": "你好中国,你爸爸,我也爱北京a你是谁"
}
ik添加分词器
IKAnalyzer.cfg.xml的配置例如以下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户能够在这里配置自己的扩展字典 <entry key="ext_dict">ext.dic;</entry>假设有多个扩展词典。那么以分号分隔就可以,如以下的两个扩展停止词字典 -->
<!--用户能够在这里配置自己的扩展停止词字典 -->
<entry key="ext_stopwords">stopword.dic;chinese_stopwords.dic</entry>
</properties>
注意点:
1、停用词词典必须是UTF-8编码。
2、这里非常多跟我一样的新手没办法成功的原因就是被无bom的UTF-8格式给折磨的,IK作者自己也这样说了。
3、假设你不知道啥叫无BOM,也不确定自己的文件是不是UTF-8无bom,那么请在第一行使用回车换行,从第二行開始加入停止词。
4、该配置文件以及停用词词典均存放在src文件夹以下就可以。
示例
示例结果
自定义示例
效果示例:
深度分页
from/size 需要实时获取顶部的部分文档,且需要自由翻页
scroll 快照 需要全部文档,如到处所有数据的功能
search_after 下一页 需要全部文档,不需要自由翻页
参考资料:
elasticsearch使用指南 : https://www.cnblogs.com/yueshutong/p/9381543.html
elasticsearch的mapping: https://blog.csdn.net/sinat_35930259/article/details/80354732