想聊es的search必须先知道这三个东西!
映射(Mapping)
描述数据在每个字段内如何存储
分析(Analysis)
全文是如何处理使之可以被搜索的
领域特定查询语言(Query DSL)
Elasticsearch 中强大灵活的查询语言
查询的分类:
有两种形式的 搜索 API
- 一种是 “轻量的” 查询字符串 版本,要求在查询字符串中传递所有的参数
- 另一种是更完整的 请求体 版本,要求使用 JSON 格式和更丰富的查询表达式作为搜索语言。(重点)
简单的search
/_search
在所有的索引中搜索所有的类型
/gb/_search
在 gb 索引中搜索所有的类型
/gb,us/_search
在 gb 和 us 索引中搜索所有的文档
/g*,u*/_search
在任何以 g 或者 u 开头的索引中搜索所有的类型
/gb/user/_search
在 gb 索引中搜索 user 类型
/gb,us/user,tweet/_search
在 gb 和 us 索引中搜索 user 和 tweet 类型
/_all/user,tweet/_search
在所有的索引中搜索 user 和 tweet 类型
分页
size
显示应该返回的结果数量,默认是 10
from
显示应该跳过的初始结果数量,默认是 0
GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
映射和分析
什么叫映射?说白了就是你字段对应的类型
查看映射
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"
}
}
}
}
}
}
字段(域)以什么类型保存(核心数据类型 strings、numbers、Booleans 和 dates ),他们的索引方式是不同的。
Elasticsearch 中的数据可以概括的分为两类:精确值和全文。
精确值很容易查询。结果是二进制的:要么匹配查询,要么不匹配。
查询全文数据要微妙的多。我们问的不只是“这个文档匹配查询吗”,而是“该文档匹配查询的程度有多大?”换句话说,该文档与给定查询的相关性如何?
查询全文数据时:
- 搜索 UK ,会返回包含 United Kindom 的文档。
- 搜索 jump ,会匹配 jumped , jumps , jumping ,甚至是 leap 。
- 搜索 johnny walker 会匹配 Johnnie Walker , johnnie depp 应该匹配 Johnny Depp
fox news hunting 应该返回福克斯新闻( Foxs News )中关于狩猎的故事,同时, fox hunting news 应该返回关于猎狐的故事。
Elasticsearch 首先 分析 文档,之后根据结果创建 倒排索引 。
什么叫分析呢?
分词和标准化的过程称为 分析 .
谁来分析呢?分析器
分析器分析的过程
1首先,将一块文本分成适合于倒排索引的独立的 词条 ,
2之后,将这些词条统一化为标准格式以提高它们的“可搜索性”,或者 recall
当然es肯定有一些内置的分析器
标准分析器
简单分析器
空格分析器
词语分析器
当你查询一个 全文 域时, 会对查询字符串应用相同的分析器,以产生正确的搜索词条列表。
当你查询一个 精确值 域时,不会分析查询字符串,而是搜索你指定的精确值。
当然你肯定想知道es的分析器是怎么分词的
GET /_analyze
{
"analyzer": "standard",
"text": "Text to analyze"
}
比如我有一个员工好的域,(xxx003)我不想分词,想精准匹配怎么办呢?指定映射!
Elasticsearch 支持如下简单域类型:
字符串: string
整数 : byte, short, integer, long
浮点数: float, double
布尔型: boolean
日期: date
非string域:设置type就行
{
"number_of_clicks": {
"type": "integer"
}
}
string域:
string 域映射的两个最重要属性是 index 和 analyzer
index 属性控制怎样索引字符串。它可以是下面三个值:说白了就是你是不索引还是直接索引还是分析后索引
- analyzed首先分析字符串,然后索引它。换句话说,以全文索引这个域。
- not_analyzed索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
- no 不索引这个域。这个域不会被搜索到。
analyzed 字符串域,用 analyzer 属性指定在搜索和索引时使用的分析器
Elasticsearch 支持如下复杂域类型:
null 值,数组,和对象
数组:
{ "tag": [ "search", "nosql" ]}
查询字符串(粗略介绍)
GET /_all/tweet/_search?q=tweet:elasticsearch
请求体查询(重点)
相对于使用晦涩难懂的查询字符串的方式,一个带请求体的查询允许我们使用 查询领域特定语言(query domain-specific language) 或者 Query DSL 来写查询语句。
Query DSL
查询表达式(Query DSL)是一种非常灵活又富有表现力的 查询语言。在你的应用中,你应该用它来编写你的查询语句。它可以使你的查询语句更灵活、更精确、易读和易调试。
GET /_search
{
"query": YOUR_QUERY_HERE
}
一定要记住这个query
举个例子,你可以使用 match 查询语句 来查询 tweet 字段中包含 elasticsearch 的 tweet:
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
}
}
查询与过滤
当使用于 过滤情况 时,查询被设置成一个“不评分”或者“过滤”查询。即,这个查询只是简单的问一个问题:“这篇文档是否匹配?”。回答也是非常的简单,yes 或者 no ,二者必居其一。(不评分)
例子:status 字段是否包含 published 这个单词?
当使用于 查询情况 时,查询就变成了一个“评分”的查询。和不评分的查询类似,也要去判断这个文档是否匹配,同时它还需要判断这个文档匹配的有 多好(匹配程度如何)。(评分),表示相关性的字段 _score
例子:查找与 full text search 这个词语最佳匹配的文档
说白了过滤就是问你有没有,查询是问你哪个更好。
如何选择查询与过滤
通常的规则是,使用查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。
过滤要比查询的性能更高!
很简单的道理,过滤只是在倒排索引中找在不在,而且可以把结果缓存下来,因为结果就是 是或者否,但是查询要计算评分,稍微变化一点查询条件评分就有很大区别,比如第一次差 fuck you 第二次查询fuck you every day,两次评分肯定不一样,但是fuck 在不在肯定不变。
虽然 Elasticsearch 自带了很多的查询,但经常用到的也就那么几个:
match_all 查询简单的匹配所有文档
{ "match_all": {}}
match 查询
无论你在任何字段上进行的是全文搜索还是精确查询,match 查询是你可用的标准查询。
全文:
{ "match": { "tweet": "About Search" }}
精准:
{ "match": { "date": "2014-09-01" }}
multi_match 查询可以在多个字段上执行相同的 match 查询:
{
"multi_match": {
"query": "full text search",
"fields": [ "title", "body" ]
}
}
range查询
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
term 查询
term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串:
{ "term": { "date": "2014-09-01" }}
上面的match最好换成term
terms 查询
terms 查询和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件:
{ “terms”: { “tag”: [ “search”, “full_text”, “nosql” ] }}
exists 查询和 missing 查询
exists 查询和 missing 查询被用于查找那些指定字段中有值 (exists) 或无值 (missing) 的文档。这与SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 在本质上具有共性:
{
"exists": {
"field": "title"
}
}
bool 查询(多组合查询)
就和mysql一样,你不可能只写一个tweet = ‘elasticsearch’ ,你肯定在where后面还要包含and,or,组成复杂的sql语句
{
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }},
"filter": { "range": { "age" : { "gt" : 30 }} }
}
}
一条复合语句可以将多条语句 — 叶子语句和其它复合语句 — 合并成一个单一的查询语句。
must
文档 必须 匹配这些条件才能被包含进来。
must_not
文档 必须不 匹配这些条件才能被包含进来。
should
如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
constant_score 查询
{
"bool": {
"filter": {
"term": { "category": "ebooks" }
}
}
}
替换为
{
"constant_score": {
"filter": {
"term": { "category": "ebooks" }
}
}
}
可以使用它来取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。
如何验证我们的查询是否正确呢?有时候查询出了问题想知道那里错了?
validate-query API 可以用来验证查询是否合法
GET /gb/tweet/_validate/query
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
排序
对比mysql的order by
es中默认的是以相关性(_score)倒叙排序的,那我能不能用别的字段排序呢?比如我想按照时间进行排序?
GET /_search
{
"query" : {
"bool" : {
"filter" : { "term" : { "user_id" : 1 }}
}
},
"sort": { "date": { "order": "desc" }}
}
多字段排序:按照时间、相关性排序
GET /_search
{
"query" : {
"bool" : {
"must": { "match": { "tweet": "manage text search" }},
"filter" : { "term" : { "user_id" : 2 }}
}
},
"sort": [
{ "date": { "order": "desc" }},
{ "_score": { "order": "desc" }}
]
}
如何多string类型的域进行排序的?
比如 fine old art 会被分析器拆分成 find,old,art3个词元,你可以使用 min 和 max 排序模式(默认是 min ),但是这会导致排序以 art 或是 old ,任何一个都不是所希望的。你想要的是以find 开头,所以怎么办呢?
对字段用两种方式索引它。所有的 _core_field 类型 (strings, numbers, Booleans, dates) 接收一个 fields 参数。
"tweet": {
"type": "string",
"analyzer": "english"
}
变成
"tweet": {
"type": "string",
"analyzer": "english",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
}
}
}
看没看到多了一个fields字段!使用 tweet 字段用于搜索,tweet.raw 字段用于排序
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
},
"sort": "tweet.raw"
}
Doc Values
在 Elasticsearch 中,Doc Values 就是一种列式存储结构,默认情况下每个字段的 Doc Values 都是激活的,Doc Values 是在索引时创建的,当字段索引时,Elasticsearch 为了能够快速检索,会把字段的值加入倒排索引中,同时它也会存储该字段的 Doc Values。说白了就是对每一列的值在建立索引时候就进行了排序。