最近在学习es;
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
交互方式
基于HTTP协议,以JSON为数据格式的RESTful API
Get 'http://localhost:3000/web/people'
{
"query": {
}
}
存储的结构和基本概念.
_index
:相当于关系型数据库中的数据库的概念,上一个查询的index就是web_type
:相当于关系型数据库中表的概念,上一个查询的type就是people_id
: 数据的唯一id_source
:该参数声明了你要查询哪些字段,不传默认显示全部字段。
_mapping 映射
- 相当于字段的数据类型,并且告诉ES如何索引数据,有时候查询语句是对的,查不出结果,可能是mapping不对
- Get http://localhost:3000/articles/magazine/_mapping?pretty
{
"articles": {
"mappings": {
"magazine": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"title": {
"type": "text"
},
"article_id": {
"type": "long"
},
"type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
这是查询magazine这个表的mapping,如果想要查询整个数据库的mapping,去掉表名即可
说一下常用的几个类型
query查询和filter查询区别
如何选择查询和过滤
简单查询实例
从people表中查询type等于12的前10条数据,并且更根据view进行desc排序
Get 'http://localhost:3000/web/people'
{
"query":{
"bool":{
"filter":[
{
"term":{
"type":12
}
}
]
}
},
"sort":[
{
"view":{
"order":"desc"
}
}
],
"from":0,
"size":10,
"_source":[
"people_id",
"title"
]
}
等价sql:
SELECT
people_id, title
FROM
people
WHERE
type = 12
ORDER BY
view DESC
LIMIT 10 OFFSET 0
当我们想有更多的过滤条件时,就可以在filter中添加更多的查询条件(子查询)
term
是一个值的精确查询,这些精确值可能是数字、时间、布尔或者那些 not_analyzed(例如keyword类型) 的字符串terms
是多个值的精确查询range
- 是范围查询
- gt: > 大于(greater than)
- lt: < 小于(less than)
- gte: >= 大于或等于(greater than or equal to)
- lte: <= 小于或等于(less than or equal to)
Get 'http://localhost:3000/web/people'
{
"query": {
"bool": {
"filter": [
{
"term": {
"type": 11
}
},
{
"range": {
"times": {
"gte": 5,
"lte": 10
}
}
},
{
"terms": {
"car_type": [
1,
2,
4
]
}
}
]
}
}
}
我们往往会需要更复杂的查询,就需要用到bool组合查询支持下面四个分类数:
must
文档 必须 匹配这些条件才能被包含进来。
must_not
文档 必须不 匹配这些条件才能被包含进来。
should
如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
{
"query":{
"bool": {
"must": { "match": { "title": "学习Elasticsearch" }},
"must_not": { "match": { "comment": "写的不好" }},
"should": [
{ "match": { "comment": "写的很棒" }}
],
"filter": {
"bool": {
"must": [
{ "range": { "date": { "gte": "2018-01-01" }}}
],
"must_not": [
{ "term": { "category": "education" }}
]
}
}
}
}
}
这是一个很好的简单易懂的例子:
它查询了时间大于等于2018-01-01、分类不是education的、并且title中含有"学习Elasticsearch"、comment中一定不含有"写的不好"的文章
,看起来其中的should好像并没有什么用呢,其实不是,should条件参加相关度的评分,让更符合目标的结果(commen中含有"写的很棒")排在前面。
这其中有个小点要注意下:
如果没有 must 语句,那么至少需要能够匹配其中的一条 should 语句。但,如果存在至少一条 must 语句,则对 should 语句的匹配没有要求。
刚才对这个查询的描述其实并不是很准确,我们需要了解一下这个match查询
如果你在一个全文字段上使用 match
查询,在执行查询前,它将用正确的分析器去分析查询字符串,这里涉及到一个新的概念分析器
分析器
一个分析器(analyzer)包含了一个或多个字符过滤器(char_filter)、一个分词器(tokenizer)、一个或多个词单元过滤器(filter)
重点说一下分词器es默认的分析器是standard,以后关于分析器还需要详细讲一下,这里标准的分析器会把字符串分割成单独的字词,如果检索的是英文的话,倒是没有什么问题,如果是中文的话,会把检索字符串的每一个汉字分成一个词,如下:
Post http://localhost:3000/web/_analyze?analyzer=standard
{
"tokens": [
{
"token": "学",
"start_offset": 0,
"end_offset": 1,
"type": "",
"position": 0
},
{
"token": "习",
"start_offset": 1,
"end_offset": 2,
"type": "",
"position": 1
},
{
"token": "elasticsearch",
"start_offset": 2,
"end_offset": 15,
"type": "",
"position": 2
}
]
}
{ "match": { "title": "学习Elasticsearch" }
所以这个代表是在title中任意包含“学”、“习”、‘elasticsearch’中任意一个的都被检索了出来,所以需要match_phrase
才能满足我们的需求,代表的是整个短语的匹配,要求 “学”、“习”、‘elasticsearch’三个词都必须出现,“习”的position比“学”大1,‘elasticsearch’比“习”的position大2,必须同时满足这3个条件。所以把上面的match换成match_phrase才是符合刚才的释义
相关度评分
- 词频:词在文档中出现的频度是多少? 频度越高,权重 越高 。 5 次提到同一词的字段比只提到 1 次的更相关.如果不在意词在某个字段中出现的频次,而只在意是否出现过,则可以在字段映射中禁用词频统计:
"index_options": "docs"
- 逆向文档频率:词在集合所有文档里出现的频率是多少?频次越高,权重 越低 。 常用词如 and 或 the 对相关度贡献很少,因为它们在多数文档中都会出现
- 字段长度归一值:字段越短,字段的权重 越高 。如果词出现在类似标题 title 这样的字段,要比它出现在内容 body 这样的字段中的相关度更高。可以通过修改字段映射禁用归一值
"norms": { "enabled": false }
- 结合使用:以上三个因素——词频(term frequency)、逆向文档频率(inverse document frequency)和字段长度归一值(field-length norm)——是在索引时计算并存储的。 最后将它们结合在一起计算单个词在特定文档中的 权重
中文检索【中文分析器】,为什么要用中文分词器
假设有一个文档【这几天天气变热,风里也透着热浪】,采用默认分词器,它会把每一个汉字分成一个词,当你用match_phrase进行检索「热风」的时候,该文档也会被检索出来,这明显不是我们想要。不仅仅是这个原因,才有中文分析器可以分析出更符合汉语的词。
- 这个GitHub上比较火的ik分析器
- ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
- ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。
自定义分词器
一个分析器(analyzer
)包含了0个或多个字符过滤器(char_filter
)、一个分词器(tokenizer
)、一个或多个词单元过滤器(filter)
分词器详细介绍
{
"settings":{
"analysis":{
"analyzer":{
"keywordAnalyzer":{
"type":"pattern",
"pattern":",",
"filter":[
"standard",
"stop",
"trim"
]
}
}
}
}
}
该分析器是以","作为分隔符进行分词,【"standard","stop","trim"】这个三个作为过滤器
关键词推荐
现在关键词字段内容格式如下,以英文逗号分开的,多个词语
帆船,美女,红酒,大海,海螺,腰带
期望检索出来和关键最相近的数据,设置如下的mapping,禁用了词频统计和长度归一值的计算
{
"keywords":{
"type":"text",
"norms":false,
"index_options":"docs",
"analyzer":"keywordAnalyzer"
}
}
采用如下检索
{
"query":{
"match":{
"keywords":{
"query":"帆船,美女,红酒,大海,海螺,腰带"
}
}
}
}
中文多词检索
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
],
"tie_breaker": 0.3
}
}
}
我们回想一下,如果是英文这样是不是就OK了?那么中文呢?
和上面不相干,说另外一个东西,看下面
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
]
}
}
}
------------------------
{
"multi_match": {
"query": "Quick pets",
"type": "best_fields",
"fields": [ "title", "body" ]
}
}
上面这个查询两个相等
但是上面的和下面的却完全不一样
{
"query":{
"bool":{
"should":[
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
]
}
}
}
回到刚才的问题 如果是英文刚才那个查询dis_max
(multi_match
)一定程度是ok的,但是中文却是不行的,为啥?因为match,虽然可以是100%匹配,但是他并没有像match_phrase
字和字之间紧紧相连。所以只能拿出最终的神奇了query_string
{
"query":{
"bool":{
"should":[
{
"query_string":{
"fields":[
"article_content"
],
"analyzer":"ik_smart",
"query":"迪奥 香水",
"auto_generate_phrase_queries":true,
"default_operator":"OR",
"use_dis_max":true,
"boost":1
}
}
]
}
}
}
这个查询代表了用ik_smart
分析器对迪奥 香水
进行分词(mapping是ik_max_word
),然后对article_content字段进行检索,要求迪奥和香水这个两个词必须出现一个或者出现两个,采用"use_dis_max":true
使同时出现两个词的结果排在前面。
查看检索逻辑
添加 "profile": true
这个参数,可以相对清楚看出es是按照什么逻辑来检索的
{
"query": {
"match": {
"relevant_keywords": {
"query": "米歇尔,美国,奥巴马,人物,女儿,总统,家庭"
}
}
},
"size": 7,
"from": 0,
"_source": [
"article_id",
"title",
"relevant_keywords",
"belong_program"
],
"profile": true
}