映射和分析
精确值VS全文
- 精确值查询: 判断值与检索条件是匹配还是不匹配的关系。
- 全文: 判断值与检索条件是否匹配,匹配度是多少的关系。
倒排索引
一个倒排索引由文档中所有的不重复词的列表构成,对于其中每个词,有一个包含他的文档。
词条 | 文档1 | 文档2 | 文档3 |
---|---|---|---|
北京 | 1 | 0 | 1 |
上海 | 1 | 1 | 1 |
广州 | 0 | 0 | 1 |
重庆 | 0 | 1 | 1 |
实例搜索北京,文档1和文档2将被返回。
分析与分析器
分析过程
- 首先,将文档分成适合与倒排索引的独立词条。
- 之后,将这些词条按照标准规则进行转化,用于提高检索率。
分词器的功能
- 字符过滤器
- 不同的字符过滤器规则不同,比如去除各种符号。
- 分词器
- 不同的分词器拆分规则不同,比如遇到空格就拆分。
- token过滤器
- 不同的token过滤器规则不同,比如统一小写化,近义词转换等等。
ES支持自定义分词器
内置分词器
- 标准分词器
- 简单分词器
- 空格分词器
- 语言分析器
分词器的使用点
- 进行全文检索时,先经过分词器对条件做标准处理。
- 进行精确值检索时,不会进行分词器标准处理。
测试分词器
GET /_analyze
{
"analyzer": "standard",
"text": "Text to analyze"
}
结果中每个元素代表一个单独的词条:
{
"tokens": [
{
"token": "text",
"start_offset": 0,
"end_offset": 4,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "to",
"start_offset": 5,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "analyze",
"start_offset": 8,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 3
}
]
}
指定分词器
在建立映射的时候。
映射
映射不可更改,可以任意增加。
核心简单类型
- 字符串 string
- 整数 byte,short,integer,long
- 浮点型 folat,double
- 布尔型 true,false
- 日期 date
动态映射
当未创建映射时,ES会根据值格式推断类型。
查看映射
GET /gb/_mapping/tweet
{
"gb": {
"mappings": {
"tweet": {
"properties": {
"date": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
},
"name": {
"type": "string"
},
"tweet": {
"type": "string"
},
"user_id": {
"type": "long"
}
}
}
}
}
}
自定义索引
- 全文字符串域和精确值字符串域的区别
- 使用特定语言分析器
- 优化域以适应部分匹配
- 指定自定义数据格式
- 还有更多
暂时不用 略
测试映射
GET /gb/_mapping/tweet
以下内容带规整
复杂核心类型
JSON.NULL,对象,数组
多值域(数组)
数组内元素类型要求一致
{ "tag": [ "search", "nosql" ]}
空域(NULL)
空域不能被索引
多层级对象(嵌套Object)
{
"gb": {
"tweet": {
"properties": {
"tweet": { "type": "string" },
"user": {
"type": "object",
"properties": {
"id": { "type": "string" },
"gender": { "type": "string" },
"age": { "type": "long" },
"name": {
"type": "object",
"properties": {
"full": { "type": "string" },
"first": { "type": "string" },
"last": { "type": "string" }
}
}
}
}
}
}
}
}
内部管理方式
{
"tweet": [elasticsearch, flexible, very],
"user.id": [@johnsmith],
"user.gender": [male],
"user.age": [26],
"user.name.full": [john, smith],
"user.name.first": [john],
"user.name.last": [smith]
}
对象数组
{
"followers": [
{ "age": 35, "name": "Mary White"},
{ "age": 26, "name": "Alex Jones"},
{ "age": 19, "name": "Lisa Smith"}
]
}
{
"followers.age": [19, 26, 35],
"followers.name": [alex, jones, lisa, smith, mary, white]
}
文档中35岁的Mary
对象,被索引年龄和索引姓名分别记录,破坏了相关性
请求体查询
GET请求带BODY体,可能被一些服务器拒绝。可以把GET改成POST,效果一样。
空查询
GET /_search
{}
查询表达式
GET /_search
{
"query": YOUR_QUERY_HERE
}
查询语句的结构
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
实例
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
}
}
合并查询语句的结构
查询语句就像一些简单的组合块,这些组合块可以彼此之间合并组成更复杂的查询。
- 叶子语句 被执行的检索条件
- 复合语句 用于关联各检索条件之间关系的部分
- must 匹配
- must_not 不匹配
- should 或者匹配
- filters 不评分过滤器
{
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }},
"filter": { "range": { "age" : { "gt" : 30 }} }
}
}
{
"bool": {
"must": { "match": { "email": "business opportunity" }},
"should": [
{ "match": { "starred": true }},
{ "bool": {
"must": { "match": { "folder": "inbox" }},
"must_not": { "match": { "spam": true }}
}}
],
"minimum_should_match": 1
}
}
查询与过滤
Elasticsearch 使用的查询语言(DSL)拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。这套组件可以在以下两种情况下使用:过滤情况(filtering context)和查询情况(query context)。
- 当DSL用于过滤情况时,查询被设置成一个“不评分”或者“过滤”的查询。
这个查询只是简单的问是否匹配,结果得到是或者否 - 当DSL用于查询情况时,查询呗设置成一个“评分”的查询。
这个查询不只是简单的问是否匹配,还要计算匹配度。
性能
不评分查询性能高于评分查询
最重要的查询(查询单元)
- match_all
匹配所有 - match
是否匹配,支持精准和全文查询。 - multi_match
一种match的简写,可以将同一条件指定给多个字段 - range
范围匹配,支持数字和时间类型。gt 大于 gte 大于等于 lt小于 lte 小于等于 - term
是否匹配,仅用于精准匹配。 - terms
一种merm的简写,可以将同一条件指定给多个字段 - exists和missing
查询被用于查找那些指定字段中有值 (exists
) 或无值 (missing
) 的文档
组合多查询(组合查询单元)
将查询单元按规则组装
- must
文档 必须 匹配这些条件才能被包含进来。 - must_not
文档 必须 匹配这些条件才能被包含进来。 - should
如果满足这些语句中的任意语句,将增加_score
,否则,无任何影响。它们主要用于修正每个文档的相关性得分。 - filters
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
各种复杂组合格式参考示例
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}
}
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"range": { "date": { "gte": "2014-01-01" }}
}
}
}
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"bool": {
"must": [
{ "range": { "date": { "gte": "2014-01-01" }}},
{ "range": { "price": { "lte": 29.99 }}}
],
"must_not": [
{ "term": { "category": "ebooks" }}
]
}
}
}
}
constant_score
只有过滤的查询语句格式
{
"constant_score": {
"filter": {
"term": { "category": "ebooks" }
}
}
}
验证查询
验证查询语句是否合法
GET /gb/tweet/_validate/query
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
理解错误信息
添加?explain参数,显示错误信息
GET /gb/tweet/_validate/query?explain
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
{
"valid" : false,
"_shards" : { ... },
"explanations" : [ {
"index" : "gb",
"valid" : false,
"error" : "org.elasticsearch.index.query.QueryParsingException:
[gb] No query registered for [tweet]"
} ]
}
解读查询语句
添加?explain参数,显示错误信息
GET /gb/tweet/_validate/query?explain
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
{
"valid" : true,
"_shards" : { ... },
"explanations" : [ {
"index" : "us",
"valid" : true,
"explanation" : "tweet:really tweet:powerful"
}, {
"index" : "gb",
"valid" : true,
"explanation" : "tweet:realli tweet:power"
} ]
}
排序与相关性
默认排序
默认情况下ES采用相关性降序排序,相关性采用浮点型记录,通过_sorce字段展示。
当指定特定字段进行排序,则_sorce为空,并且不会进行相关性计算(算了没有用处)。
多级排序
GET /_search
{
"query" : {
"bool" : {
"must": { "match": { "tweet": "manage text search" }},
"filter" : { "term" : { "user_id" : 2 }}
}
},
"sort": [
{ "date": { "order": "desc" }},
{ "_score": { "order": "desc" }}
]
}
多值字段排序
先根据指定的排序模式对数组内部进行排序,在用结果值参与整体排序。
数字或时间类型的多值字段,可选以下模式进行内部排序
- min(默认)
- max
- avg
- sum
- 等等
"sort": {
"dates": {
"order": "asc",
"mode": "min"
}
}
一个字段配置多种索引类型(虚拟字段)
当字符串类型采用全文索引的情况下,不能再进行精准查询,通过配置虚拟字段来解决。
虚拟字段不会存两份原数据,而是存两份索引结果。
所有的 _core_field 类型 (strings, numbers, Booleans, dates) 接收一个 fields
参数,该参数允许你转化一个简单的映射如:
原始
"tweet": {
"type": "string",
"analyzer": "english"
}
虚拟字段
"tweet": {
"type": "string",
"analyzer": "english",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
}
}
}
此时支持tweet全文检索,也支持tweet.raw精准查询
相关性
词频(DF)
定义:一词语出现的次数除以总词语数。
实例
假设一篇文档总共100个词语,JAVA词语出现了10次。则频率=10%(10/100)
文档频率
定义:包含一词语的文件数除以文件总数
实例
假设共有10000份分档,包含JAVA词语的有100份。则文件频率=1%(100/10000)
逆文档频率(反向文档频率)(IDF)
定义:词频/文件频率
实例
逆文档频率=10(0.1/0.01)
字段长度准则
假设一个词语长度为2,出现在一个长度100的长语句中。
假设一个词语长度为2,出现在一个长度10的段语句中。
则认为 第二项的准确度比第一项的高
理解评分标准
通过添加explain参数,在返回的每个文档后追加评分依据信息。
GET /_search?explain
{
"query" : { "match" : { "tweet" : "honeymoon" }}
}
正常内容
{
"_index" : "us",
"_type" : "tweet",
"_id" : "12",
"_score" : 0.076713204,
"_source" : { ... trimmed ... },
展示文档来源,节点和分片。词频率和文档频率是在分片中计算的。
"_shard" : 1,
"_node" : "mzIVYCsqSWCG_M_ZffSs9Q",
_explanation
部分展示了所有细节。
例如相关性评分总结,检索词频率,反向文档频率,字段长度频率等
"_explanation": {
"description": "weight(tweet:honeymoon in 0)
[PerFieldSimilarity], result of:",
"value": 0.076713204,
"details": [
{
"description": "fieldWeight in 0, product of:",
"value": 0.076713204,
"details": [
{
"description": "tf(freq=1.0), with freq of:",
"value": 1,
"details": [
{
"description": "termFreq=1.0",
"value": 1
}
]
},
{
"description": "idf(docFreq=1, maxDocs=1)",
"value": 0.30685282
},
{
"description": "fieldNorm(doc=0)",
"value": 0.25,
}
]
}
]
}
理解某文档是如何匹配到的,或者如何被排除掉的
GET /us/tweet/12/_explain
{
"query" : {
"bool" : {
"filter" : { "term" : { "user_id" : 2 }},
"must" : { "match" : { "tweet" : "honeymoon" }}
}
}
}
Doc values
Doc Values 是在索引时与 倒排索引 同时生成。
Doc Values 和 倒排索引 都是经过分词器处理后保存的,所以不可变更。
倒排索引结构
词条 | 文档1 | 文档2 | 文档3 |
---|---|---|---|
北京 | 1 | 0 | 1 |
上海 | 1 | 1 | 1 |
广州 | 0 | 0 | 1 |
重庆 | 0 | 1 | 1 |
Doc Values结构
文档 | 词条 |
---|---|
文档1 | 北京,上海 |
文档2 | 上海,重庆 |
文档3 | 北京,上海,广州,重庆 |
Doc Values的持久化与压缩
Doc Values保存在磁盘上,并且是压缩保存的。因为cpu性能远高于磁盘。
Doc Values应用场景
- 对一个字段进行排序
- 对一个字段进行聚合
- 某些过滤,比如地理位置过滤
- 某些与字段相关的脚本计算
举例
假设计算两个文档的交集,不适合用倒排索引计算,而Doc Values更适合。
文章中部分内容来源于:
Elasticsearch: The Definitive Guide by Clinton Gormley and Zachary Tong (O’Reilly). Copyright 2015 Elasticsearch BV, 978-1-449-35854-9。