摘要:写给自己的使用指南在处理大数据搜索方面拥有关系型数据库不可比拟的速度优势。什么是的文档中文档是以的数据包形式存储的,它有三个元数据。使用者无需关心。匹配为或摇了摇头的结果使用关键词匹配中文摇了摇头过滤过滤是按照指定范围查找数据。
写给自己的Elasticsearch使用指南
ES在处理大数据搜索方面拥有关系型数据库不可比拟的速度优势。这篇不是什么专业的ES指南,也不是ES分析,这里只是我在使用ES中遇到的一些问题,一些搜索方式。因为ES的文档和API查询起来比较困难,因此自己在查询翻译文档时总是耗费很多时间,于是就想把自己遇到过的问题和搜索记录下来,给自己归纳一个简单的常用的ES使用指南。
什么是ES的文档?ES中文档是以key-value的json数据包形式存储的,它有三个元数据。
_index:文档存储的地方
数据被存储和索引在分片「shards」中,索引只是把一个或多个分片组合在一起的逻辑空间。这些由ES实现。使用者无需关心。
_type:表示一种事务,在5.x中type被移除。
_id:文档的唯一标识符。
ES的映射是怎么回事?映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种特定的类型(string,number,booleans,date等)。
分析(analysis)机制用于进行全文文本(full text)分词,以建立供搜索用的反向索引。
查看索引(index)的 type的mappingES为对字段类型进行猜测,动态生成了字段和类型的映射关系。
date 类型的字段和 string 类型的字段的索引方式不同的,搜索的结果也是不同的。每一种数据类型都以不同的方式进行索引。
GET /user/_mapping/user --Response { "user": { "mappings": { "user": { "properties": { "height": { "type": "long" }, "nickname": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, } } } } }确切值(Exact values)和全文文本(Full text)
确切值是确定的,表示date或number等;Index参数控制字符串以何种方式被索引
全文文本指 非结构化数据,如 这是一个文本
值 | 解释 |
---|---|
analyzed | 首先分析这个字符串,然后索引。(以全文形式索引此字段) |
not_analyzed | 索引这个字段,使之可以被索引,但索引内容和指定值一样 |
no | 不索引这个字段 |
string 类型字段默认值是 analyzed。如果想映射字段为确切值,则设置为 not_analyzed
{ "nickname": { "type": "string", "index": "not_analyzed" } }从 空搜索 来看ES搜索的返回
GET /_search --Response { "took": 7, // 整个搜索的请求花费的时间(毫秒) "timed_out": false, // 是否请求超时 "_shards": { "total": 25, // 参与查询的分片 "successful": 25, // 成功的分片 "skipped": 0, // 跳过的 "failed": 0 // 失败的 }, "hits": { "total": 1291, // 匹配到到文档数量 "max_score": 1, // 查询结果 _score 中的最大值 "hits": [ { "_index": "feed", "_type": "feed", "_id": "1JNC42oB07Tkhuy89JSd", "_score": 1, "_source": { } } ] } }创建索引
创建一个名为 megacorp 的索引
PUT /user/ --Response { "megacorp": { "aliases": {}, "mappings": {}, "settings": { "index": { "creation_date": "1558956839146", // 创建时间(微秒时间戳) "number_of_shards": "5", "number_of_replicas": "1", "uuid": "pGBqNmxrR1S8I7_jAYdvBA", // 唯一id "version": { "created": "6060199" }, "provided_name": "megacorp" } } } }创建文档
PUT /user/user/1001 --Body { "nickname": "你有病啊", "height": 180, "expect": "我喜欢看_书", "tags": ["大学党", "求偶遇"] } --Response { "_index": "user", "_type": "user", "_id": "10001", "_version": 1, // 版本号,ES中每个文档都有版本号,每当文档变化(包括删除)_version会增加 "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 1, "_primary_term": 1 }使用过滤器(filter)
搜索 height 大于 30 且 nickname 为 threads 的文档
GET /user/user/_search --Body { "query": { "filtered": { "filter": { "range":{ "height":{ "gt": 170 } } }, "query": { "match": { "nickname": "threads" } } } } }结构化查询
结构化查询需要传递 query 参数。
GET /_search { "query": 子查询 } # 子查询 { "query_name": { "argument": value,... } } # 子查询指向特定字段 { "query_name": { "field_name": { "argument": value,... } } }合并多子句
查询子句可以合并简单的子句为一个复杂的查询语句。如:
简单子句用以在将查询字符串与一个字段(或多个字段)进行比较。
复合子句用以合并其他的子句。
GET /user/user/_search --Body { "query": { "bool": { "must": { // must:必须满足该子句的条件 "match": { "nickname": "threads" } }, "must_not": { // must_not: 必须不满足该子句条件 "match": { "height": 170 } }, "should": [ // should: 结果可能满足该数组内的条件 { "match": { "nickname": "threads" } }, { "match": { "expect": "我喜欢唱歌、跳舞、打游戏" } } ] } } } --Response { "hits": { "total": 1, "max_score": 0.91862875, "hits": [ { "_index": "user", "_type": "user", "_id": "98047", "_score": 0.91862875, "_source": { "nickname": "threads", "height": 175, "expect": "我喜欢唱歌、跳舞、打游戏", "tags": [ "工作党", "吃鸡" ] } } ] } }全文搜索
GET /user/user/_search --Body { "query":{ "match":{ "expect": "打游戏" } } } --Response { "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 0.8630463, "hits": [ { "_index": "user", "_type": "user", "_id": "98047", "_score": 0.8630463, // 匹配分 "_source": { "nickname": "threads", "height": 175, "expect": "我喜欢唱歌、跳舞、打游戏", "tags": [ "工作党", "吃鸡" ] } }, { "_index": "user", "_type": "user", "_id": "94302", "_score": 0.55900055, "_source": { "nickname": "摇了摇头", "height": 173, "expect": "我喜欢rap、跳舞、打游戏", "tags": [ "工作党", "吃饭" ] } }, { "_index": "user", "_type": "user", "_id": "91031", "_score": 0.53543615, "_source": { "nickname": "你有病啊", "height": 180, "expect": "我喜欢学习、逛街、打游戏", "tags": [ "大学党", "求偶遇" ] } } ] } }
ES根据结果相关性评分来对结果集进行排序。即文档与查询条件的匹配程度短语搜索
确切的匹配若干单词或短语。
GET /user/user/_search --Body { "query": { "match_phrase": { "expect": "学习" } } } --Response { "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 1.357075, "hits": [ { "_index": "user", "_type": "user", "_id": "91031", "_score": 1.357075, "_source": { "nickname": "你有病啊", "height": 180, "expect": "我喜欢学习、逛街、打游戏", "tags": [ "大学党", "求偶遇" ] } } ] } }结构化过滤
过滤语句和查询语句的性能对比一条过滤语句会询问每个文档的字段值是否包含着特定值:
是否 created 的日期范围在 xxx 到 xxx
是否 expect 包含 学习
是否 location 字段中的地理位置与目标点相距不超过 xxkm
使用过滤语句得到的结果集,快速匹配运算并存入内存是十分方便的,每个文档仅需要1个字节。
查询语句不仅要查询相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比过滤语句更耗时,并且查询结果也不可缓存。
原则上来说:使用查询语句做全文文本搜索或其他需要进行相关性评分的时候,剩下的全部使用过滤语句。
GET /user/user/_search --Body { "query": { "match_phrase": { "expect": "学习" } }, "highlight": { "fields": { "expect": {} } } }排序
GET /user/user/_search --Body { "query": { "match_all": { } }, "size": 1000, // 返回的数据集大小 "sort": { "nickname.keyword": { // 按nickname排序 "order": "desc" }, "height": { // 按height排序 "order": "desc" } } }搜索结果分页
GET /user/user/_search --Body { "from": 2, // 同mysql 的 offset "size": 1 // 同mysql 的 limit }分析
GET /user/user/_search --Body { "aggs": { "all_tags": { "terms": { "field": "tags" // 这种分词对于中文很不友好,会把“学习”分为“学”,“习” "field": "tags.keyword" // 5.x后的ES,使用这种写法可以完美分词 }, "aggs": { "avg_height": { "avg": { "field": "height" } } } } }查询过滤语句集合 term 过滤
term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串
{"term": {"height": 175}} // height值为175 (number类型) {"term": {"date": "2014-09-01"}} // date值为2014-09-01(date类型) {"term": {"public": true}} // public值为true(布尔类型) {"term": {"nickname": "threads"}} // nickname值为threads(full_text类型)terms 过滤
terms 跟 term 一样,但 terms 允许指定多个匹配条件。如果某个字段指定了多个值,那么文档需要一起去做匹配文档。
// 匹配 nickname 为 threads 或 摇了摇头 的结果(使用keyword关键词匹配中文) { "query": { "terms": { "nickname.keyword": ["threads", "摇了摇头"] } } }range 过滤
range 过滤是按照指定范围查找数据。
{ "query": {"range":{"height":{"gte": 150, "lt": 180}}} } // gt: 大于 // gte: 大于等于 // lt: 小于 // lte: 小于等于exists 和 missing 过滤
exists 和 missing 过滤可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL 条件。
// 查询存在 nickname 字段的结果 {"exists": {"field": "nickname"}}bool 过滤
bool 过滤可以用来合并多个过滤条件查询结果的布尔逻辑:
must:多个查询条件的完全匹配,相当于 and
must_not: 多个查询条件的相反匹配,相当于 not
should:至少有一个查询条件匹配,相当于 or
{ "bool": { "must": {"term": {"nickname": "threads"}}, "must_not": {"term": {"height": 165}}, "should": [ {"term": {"height": 175}}, {"term": {"nickname": "threads"}}, ] } }
使用 match_all 可以查询到所以文档,是没有查询条件下的默认语句。(常用于合并过滤条件)
{ "match_all":{} }match 查询
match 查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
{ "match": {"nickname": "threads"} }multi_match 查询
multi_match 查询允许你做 match 查询的基础上同时搜索多个字段:
// 搜索 nickname 或 expect 包含 threads 的结果 { "multi_match": { "query": "threads", "fields": ["nickname", "expect"] } }bool 查询
如果 bool 查询下没有 must 子句,那至少应该有一个 should 子句。但是如果有 must 子句,那么没有 should 子句也可以进行查询。
{ "query": { "bool": { "must": { "multi_match": {"query": "学习", "fields": ["nickname", "expect"]} }, "must_not": { "match": {"height": 175} }, "should": [ {"match": {"nickname": "threads"}} ] } } }使用 filter 带过滤的查询
使用 filter 来同时使一个语句中包含 查询 和 过滤
// nickname 为 threads 的结果,并在此结果集中筛选出 height 为 175 的结果 { "query": { "bool": { "must": {"match": {"nickname": "threads"}}, "filter": {"term": {"height":175}} } } }查询语句的分析 验证一个查询语句的对错?
GET /user/user/_validate/query { "query": { "match_all": {"nickname": "threads"} } } --Response { "valid": false, "error": "ParsingException[[4:13] [match_all] unknown field [nickname], parser not found]; nested: XContentParseException[[4:13] [match_all] unknown field [nickname], parser not found];; org.elasticsearch.common.xcontent.XContentParseException: [4:13] [match_all] unknown field [nickname], parser not found" }如何理解一个查询语句的执行?
GET /user/user/_validate/query { "query": { "match_all": {"nickname": "threads"} } } --Response { "valid": false, "error": "ParsingException[[4:13] [match_all] unknown field [nickname], parser not found]; nested: XContentParseException[[4:13] [match_all] unknown field [nickname], parser not found];; org.elasticsearch.common.xcontent.XContentParseException: [4:13] [match_all] unknown field [nickname], parser not found" } // 通过返回可以看出,验证结果是非法的ES查询时经常出现的异常 在使用聚合时关于fielddata的异常
{ "error": { "root_cause": [ { "type": "illegal_argument_exception", "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [tags] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead." } ], ... }, "status": 400 }
5.x后对排序,聚合这些操作用多带带的数据结构(fielddata)缓存到内存里了,需要多带带开启。
开启fielddata
PUT user/_mapping/user/ --Body { "properties": { "tags": { "type": "text", "fielddata": true } } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/34515.html
摘要:请回复这个帖子并注明组织个人信息来申请加入。版笔记等到中文字幕翻译完毕后再整理。数量超过个,在所有组织中排名前。网站日超过,排名的峰值为。主页归档社区自媒体平台微博知乎专栏公众号博客园简书合作侵权,请联系请抄送一份到赞助我们 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1...
引言 我们正处在一个知识爆炸的时代,伴随着信息量的剧增和人工智能的蓬勃发展,互联网公司越发具有强烈的个性化、智能化信息展示的需求。而信息展示个性化的典型应用主要包括搜索列表、推荐列表、广告展示等等。 很多人不知道的是,看似简单的个性化信息展示背后,涉及大量的数据、算法以及工程架构技术,这些足以让大部分互联网公司望而却步。究其根本原因,个性化信息展示背后的技术是排序学习问题(Learning to ...
引言 我们正处在一个知识爆炸的时代,伴随着信息量的剧增和人工智能的蓬勃发展,互联网公司越发具有强烈的个性化、智能化信息展示的需求。而信息展示个性化的典型应用主要包括搜索列表、推荐列表、广告展示等等。 很多人不知道的是,看似简单的个性化信息展示背后,涉及大量的数据、算法以及工程架构技术,这些足以让大部分互联网公司望而却步。究其根本原因,个性化信息展示背后的技术是排序学习问题(Learning to ...
摘要:请回复这个帖子并注明组织个人信息来申请加入。权限分配灵活,能者居之。数量超过个,在所有组织中排名前。网站日超过,排名的峰值为。导航归档社区自媒体平台微博知乎专栏公众号博客园简书合作侵权,请联系请抄送一份到赞助我们 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1760&h=...
摘要:主页暂时下线社区暂时下线知识库自媒体平台微博知乎简书博客园我们不是的官方组织机构团体,只是技术栈以及的爱好者合作侵权,请联系请抄送一份到基础编程思想和大数据中文文档中文文档中文文档中文文档中文文档中文文档中文文档中文文档中文文档中文文档区块 【主页】 apachecn.org 【Github】@ApacheCN 暂时下线: 社区 暂时下线: cwiki 知识库 自媒体平台 ...
阅读 1529·2021-08-13 15:03
阅读 1831·2019-08-30 15:54
阅读 3396·2019-08-26 10:30
阅读 897·2019-08-26 10:22
阅读 2629·2019-08-23 14:42
阅读 1702·2019-08-22 11:16
阅读 914·2019-08-21 18:33
阅读 3038·2019-08-21 17:28