es学习笔记

想聊es的search必须先知道这三个东西!

映射(Mapping)

描述数据在每个字段内如何存储

分析(Analysis)

全文是如何处理使之可以被搜索的

领域特定查询语言(Query DSL)

Elasticsearch 中强大灵活的查询语言

查询的分类:

有两种形式的 搜索 API

  1. 一种是 “轻量的” 查询字符串 版本,要求在查询字符串中传递所有的参数
  2. 另一种是更完整的 请求体 版本,要求使用 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 中的数据可以概括的分为两类:精确值和全文。

精确值很容易查询。结果是二进制的:要么匹配查询,要么不匹配。
查询全文数据要微妙的多。我们问的不只是“这个文档匹配查询吗”,而是“该文档匹配查询的程度有多大?”换句话说,该文档与给定查询的相关性如何?

查询全文数据时:

  1. 搜索 UK ,会返回包含 United Kindom 的文档。
  2. 搜索 jump ,会匹配 jumped , jumps , jumping ,甚至是 leap 。
  3. 搜索 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 属性控制怎样索引字符串。它可以是下面三个值:说白了就是你是不索引还是直接索引还是分析后索引

  1. analyzed首先分析字符串,然后索引它。换句话说,以全文索引这个域。
  2. not_analyzed索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
  3. 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。说白了就是对每一列的值在建立索引时候就进行了排序。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值