在系列的第一篇文章中我们介绍了ElasticSearch的基本概念和操作,本文将继续介绍ElasticSearch查询和索引功能。

目录:

查询

结构化搜索将查询条件以json的形式包含在http请求的body中,通常情况下搜索请求应该使用GET方法但不是所有的客户端和服务端都支持GET请求包含body。 因此,ElasticSearch支持使用GET或POST进行搜索。

本节示例均使用默认analysismapping配置, 此状态下ElasticSearch与普通的文档数据库几乎无异。我们将在下一节中介绍如何进行模糊查询,使它成为真正的搜索引擎。

精确查询

精确查询通常起到过滤的作用,因为不使用分析器解析通常非常迅速。

查询可以使用from和size参数对结果进行分页:

  1. {
  2. "from": 0,
  3. "size": 10,
  4. "query": {
  5. "term": {
  6. "nickname": "easy"
  7. }
  8. }
  9. }

term查询

term查询用于进行精确匹配:

  1. POST /blog/user/_search
  2. {
  3. "query": {
  4. "term": {
  5. "nickname": "easy"
  6. }
  7. }
  8. }
  9. response:
  10. {
  11. "took": 23,
  12. "timed_out": false,
  13. "_shards": {
  14. "total": 5,
  15. "successful": 5,
  16. "failed": 0
  17. },
  18. "hits": {
  19. "total": 2,
  20. "max_score": 0.45532417,
  21. "hits": [
  22. {
  23. "_index": "blog",
  24. "_type": "user",
  25. "_id": "1",
  26. "_score": 0.45532417,
  27. "_source": {
  28. "nickname": "easy",
  29. "username": "user1",
  30. "status": "normal"
  31. }
  32. },
  33. {
  34. "_index": "blog",
  35. "_type": "user",
  36. "_id": "2",
  37. "_score": 0.43648314,
  38. "_source": {
  39. "nickname": "easy",
  40. "username": "user2"
  41. "status": "normal"
  42. }
  43. }
  44. ]
  45. }
  46. }

在响应的hits.hits字段中我们可以看到匹配的文档,文档_score字段是采用TF-IDF算法得出匹配程度得分,结果集中的文档按照得分降序排列。

上述查询可以用sql表示为:

  1. SELECT * FROM user
  2. WHERE nickname = "easy";

terms查询

terms查询可以视为多个term查询的组合:

  1. POST /blog/user/_search
  2. {
  3. "query": {
  4. "terms": {
  5. "nickname": ["easy", "ease"]
  6. }
  7. }
  8. }
  9. response:
  10. {
  11. "took": 18,
  12. "timed_out": false,
  13. "_shards": {
  14. "total": 5,
  15. "successful": 5,
  16. "failed": 0
  17. },
  18. "hits": {
  19. "total": 2,
  20. "max_score": 1.0970675,
  21. "hits": [
  22. {
  23. "_index": "blog",
  24. "_type": "user",
  25. "_id": "2",
  26. "_score": 1.5779335,
  27. "_source": {
  28. "nickname": "ease",
  29. "status": "normal"
  30. }
  31. },
  32. {
  33. "_index": "blog",
  34. "_type": "user",
  35. "_id": "4",
  36. "_score": 1.0970675,
  37. "_source": {
  38. "nickname": "easy",
  39. "content": "simple",
  40. "status": "normal"
  41. }
  42. }
  43. ]
  44. }
  45. }

range查询

range查询用于数值等类型进行过滤,比如示例中过滤粉丝数大于等于2的用户:

  1. GET /user/naive/_search
  2. {
  3. "query": {
  4. "range": {
  5. "followerCount": {
  6. "gte": 2
  7. },
  8. "registerTime": {
  9. "gt": "2017-01-01 00:00:00",
  10. "lt": "2018-01-01 00:00:00"
  11. }
  12. }
  13. }
  14. }

示例中的range查询可以用SQL描述为:

  1. SELECT * FROM user_naive
  2. WHERE followerCount >= 2
  3. AND unix_timestamp(registerTime) > unix_timestamp('2017-01-01 00:00:00')
  4. AND unix_timestamp(registerTime) < unix_timestamp('2018-01-01 00:00:00');

可用的范围表达式有:

  • lt: 小于(less than)
  • lte: 小于等于(less than / equals)
  • gt: 大于(greater than)
  • gte: 大于等于(greater than / equals)

可以进行范围查询的字段:

  • 数值
  • 日期
  • 字符串: 按照词典顺序排列

全文查询

用户输入字符串通常无法进行精确查询,全文查询可以使用分析器解析查询字符串然后根据相关度筛选查询结果。

match查询

match查询全文字段(类型为analyzed)时,会使用分析器将查询字符串解析成词条列表,然后进行匹配。

查询字符串"easy simple"会被standard分析器解析为[easy, simple], 这个查询等价于:

  1. {
  2. "terms": {
  3. "nickname": ["easy", "simple"]
  4. }
  5. }
  1. POST /blog/user/_search
  2. {
  3. "query": {
  4. "match": {
  5. "nickname": "easy simple"
  6. }
  7. }
  8. }
  9. response:
  10. {
  11. "took": 19,
  12. "timed_out": false,
  13. "_shards": {
  14. "total": 5,
  15. "successful": 5,
  16. "failed": 0
  17. },
  18. "hits": {
  19. "total": 3,
  20. "max_score": 1.1382749,
  21. "hits": [
  22. {
  23. "_index": "blog",
  24. "_type": "user",
  25. "_id": "3",
  26. "_score": 1.1382749,
  27. "_source": {
  28. "nickname": "simple",
  29. "status": "normal"
  30. }
  31. },
  32. {
  33. "_index": "blog",
  34. "_type": "user",
  35. "_id": "1",
  36. "_score": 1.049597,
  37. "_source": {
  38. "nickname": "easy",
  39. "status": "normal"
  40. }
  41. }
  42. ]
  43. }
  44. }

若以eas sim为关键词进行term查询不会匹配到任何文档。

multi_match查询

multi_match用于对多个字段进行同一个查询:

  1. {
  2. "query": {
  3. "multi_match": {
  4. "analyzer": "keyword",
  5. "query": "easy",
  6. "fields": [
  7. "nickname.ngram",
  8. "username.ngram"
  9. ]
  10. }
  11. }
  12. }

上述查询等同于对nickname.ngramusername.ngram两个字段进行match查询,对结果进行综合排序。

排序方式有:

  • best_fields
  • most_fields
  • cross_fields

script查询

script查询可以使用脚本描述查询条件,进行复杂的查询。

  1. {
  2. "query": {
  3. "script": {
  4. "script": {
  5. "source": "doc['count'].value > params.limit",
  6. "lang": "painless",
  7. "params": {
  8. "limit": 1
  9. }
  10. }
  11. }
  12. }

我们通常使用ElasticSearch提供的painless脚本语言编写脚本,也可以使用Groovy等脚本语言。

组合查询

组合查询用于将多个查询组合起来,以表达更复杂的查询条件或排序规则。

组合查询都是可以自由嵌套的,如我们可以bool查询中使用另一个bool查询或dis_max作为must子查询。

bool查询

Bool查询用于组合多个条件查询相关度最高的文档, 下面展示了一个Bool查询请求:

  1. POST /user/naive/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": {
  6. "match": {
  7. "nickname": "easy"
  8. }
  9. },
  10. "must_not": {
  11. "match": {
  12. "nickname": "hard"
  13. }
  14. },
  15. "should": {
  16. "match": {
  17. "nickname": "simple"
  18. }
  19. },
  20. "filter": [
  21. {
  22. "term": {
  23. "status": "normal"
  24. }
  25. }
  26. ]
  27. }
  28. }
  29. }
  30. response:
  31. {
  32. "took": 20,
  33. "timed_out": false,
  34. "_shards": {
  35. "total": 5,
  36. "successful": 5,
  37. "failed": 0
  38. },
  39. "hits": {
  40. "total": 3,
  41. "max_score": 1.3847495,
  42. "hits": [
  43. {
  44. "_index": "blog",
  45. "_type": "user",
  46. "_id": "4",
  47. "_score": 1.3847495,
  48. "_source": {
  49. "nickname": "easy",
  50. "content": "simple",
  51. "status": "normal"
  52. }
  53. },
  54. {
  55. "_index": "blog",
  56. "_type": "user",
  57. "_id": "5",
  58. "_score": 0.45532417,
  59. "_source": {
  60. "nickname": "easy",
  61. "content": "bitter",
  62. "status": "normal"
  63. }
  64. }
  65. ]
  66. }
  67. }

上述bool查询的目标为:

  • must条件必须满足, 即nickname字段必须与词条easy匹配,字段的匹配程度会影响得分
  • must_not条件必须不满足, 即nickname字段不能与词条hard匹配
  • should条件不做要求, 但满足should条件的文档会获得更高的相关度评分_score。 当should查询中包含多个字段时, 会将各字段得分的和作为总分。所以查询到两个nickname与easy匹配的文档,但是contentsimple的字段获得了更高的评分。
  • filter条件必须满足,但是匹配程度不会影响得分。

filter子查询一般使用精确查询,因为过滤器不进行相关度计算且使用过滤器缓存, 其执行速度非常快。

dis_max查询

上文已经提到bool查询的should查询会将各字段得分之和作为总分,然而在实际应用中通常一个字段高度匹配的文档可能比拥有多个字段低匹配更符合用户的期望。

dismax查询同样用于组合多个查询,但是按照匹配程度最高的字段确定总分:

  1. {
  2. "query": {
  3. "dis_max": {
  4. "queries": [
  5. {
  6. "match": {
  7. "nickname": "easy"
  8. }
  9. },
  10. {
  11. "match": {
  12. "content": "easy"
  13. }
  14. }
  15. ]
  16. }
  17. }
  18. }

function_score 查询

function_score可以更加自由地控制评分过程:

  1. {
  2. "query": {
  3. "function_score": {
  4. "filter": {
  5. "term": { "uid": 1 }
  6. },
  7. "functions": [
  8. {
  9. "filter": { "term": { "features": "a" }},
  10. "weight": 1
  11. },
  12. {
  13. "filter": { "term": { "features": "b" }},
  14. "weight": 2
  15. }
  16. ],
  17. "score_mode": "sum",
  18. }
  19. }
  20. }

一致性随机

function_score可以对结果进行随机排序:

  1. {
  2. "query": {
  3. "random_score": {
  4. "seed": 2
  5. },
  6. "bost_mode": "replace",
  7. "query": {
  8. "dis_max": {
  9. "queries": [
  10. {
  11. "multi_match": {
  12. "analyzer": "keyword",
  13. "query": "finley",
  14. "boost": 1.2,
  15. "fields": [
  16. "username.ngram"
  17. ]
  18. }
  19. },
  20. {
  21. "multi_match": {
  22. "analyzer": "keyword",
  23. "query": "finley",
  24. "boost": 1,
  25. "fields": [
  26. "nickname.ngram"
  27. ]
  28. }
  29. }
  30. ]
  31. }
  32. }
  33. }
  34. }

只要随机种子random_score.seed不变结果的排序就不会变化,这种方式被称为一致性随机评分。

一致性随机评分机制可以为每个用户生成一个独有的排序,并支持翻页浏览。

排序

ElasticSearch的搜索结果默认按照_score进行降序排列,_score是根据TF-IDF算法得出文档与查询调价匹配度。

在一些情况下我们希望自定义排序方式, 比如按创建时间排列。

  1. POST /blog/user/_search
  2. {
  3. "query" : {
  4. "term" : {
  5. "uid" : 1
  6. }
  7. },
  8. "sort": {
  9. "date": {
  10. "order": "desc"
  11. }
  12. }
  13. }

script 排序

  1. {
  2. "query" : {
  3. "term" : {
  4. "uid" : 1
  5. }
  6. },
  7. "sort": {
  8. "script_score": {
  9. "source": "_score / (params.current - doc['create_at'].value)"
  10. "params": {
  11. "current": 1534606168
  12. }
  13. }
  14. }
  15. }

索引管理

ElasticSearch中的Index是最高级的逻辑的结构, 类似于MySQL中的数据库(schema),可以配置独立的搜索策略和存储策略。

ElasticSearch通过倒排索引进行搜索,所谓倒排索引是指对文档进行分析提取关键词,然后建立关键词到文档的索引,当我们搜索关键词时就可以找到它所关联的文档。

我们可以通过在Index中配置分析器和映射来设置倒排索引的策略。

分析器是通用的从文档提取关键词的方法,即将文档中某个字段映射为关键字的方法。例如:过滤停用词,分词,添加同义词等。

映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词。

分析器

这个拆分的过程即是分析的过程, 分析执行的操作包括不限于: 字符过滤, 分词, 停用词过滤, 添加同义词.

ES提供了很多内置分析器:

  • standard: 默认分析器, 根据Unicode定义删除标点并将词条小写
  • simple: 根据空格分词并将词条小写
  • whitespace: 根据空格分词但不将词条小写
  • english: 过滤英文停用词并将词条还原为词根, 详情参考官方文档.
  • ngram: 滑动窗口分析器,取文本中所有子串作为关键词。 比如对easy进行处理可以得到关键词:e, a, s, y, ea, as, sy, eas, asy, easy
  • edge-ngram: 边缘固定滑动窗口分析器,取文本所有从头开始的子串作为关键词。 比如对easy进行处理可以得到关键词:e, ea, eas, easy。常用于联想搜索,根据用户输入前几个字符进行搜索。

此外, 也可以通过配置字符过滤器(char_filter), 词过滤器(filter), 分词器(tokenizer)的方式来自定义分析器。

这里展示基于ngram的分析器定义:

  1. PUT /blog
  2. {
  3. "settings": {
  4. "analysis": {
  5. "filter": {
  6. "grams_filter": {
  7. "type": "ngram",
  8. "min_gram": 1,
  9. "max_gram": 5
  10. }
  11. },
  12. "analyzer": {
  13. "gram_analyzer": {
  14. "type": "custom",
  15. "tokenizer": "standard",
  16. "filter": [
  17. "lowercase",
  18. "grams_filter"
  19. ]
  20. }
  21. }
  22. }
  23. }
  24. },
  25. mappings: {...}
  26. }

自定义分析器的更多信息可以查阅官方文档:

类型和映射

映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词:

  1. PUT /blog
  2. {
  3. "settings": { ... },
  4. "mappings": {
  5. "user": {
  6. "properties": {
  7. "nickname": {
  8. "type": "string",
  9. "analyzer": "gram_analyzer",
  10. "fields": {
  11. "keyword:": {
  12. "type": "keyword"
  13. }
  14. }
  15. },
  16. "status": {
  17. "type": "text",
  18. "fields": {
  19. "keyword:": {
  20. "type": "keyword"
  21. }
  22. }
  23. }
  24. }
  25. }
  26. }
  27. }

上述JSON中user项定义了一个根对象, 它通常与文档对应。根对象下可以包含下列几个字段:

文档中每一个字段都有3个配置项:

  • type: 指定字段类型, 如:text, long, doubledate.
  • index: 指定字段索引的类型:
    • no: 不可被搜索
    • not_analyzed: 必须精确匹配
    • analyzed: 使用分析器建立倒排索引
  • analyzer: 该字段使用的默认分析器
  • fields: 字段的属性, 可以配置独立的type, index和analyzer。用于将一个字段映射为不同类型适应不同用途。

管理索引

在分析器及映射两节中展示了创建索引所需的PUT请求片段,将类型和映射一节中PUT请求的settings字段, 用分析器一节中的settings字段替换即可得到完整创建索引请求。

发送DELETE请求可以删除索引:

  • DELETE /user: 删除user索引
  • DELET /user1,user2: 删除user1和user2两个suoyin
  • DELETE /user*: 根据通配符删除索引
  • DELET /_all, DELETE /*: 删除所有索引

GET /_cat/indices可以列出ElasticSearch上的所有索引。

GET /blog?pretty列出索引blog的所有信息。

中文搜索

若需要进行中文搜索我们需要使用中文分析器,它的核心是中文分词器(tokenizer)。

这里我们使用插件elastic-analysis-ik进行中文搜索。

  1. PUT /post/Post
  2. {
  3. "settings": {
  4. "analysis": {
  5. "analyzer": {
  6. "cn_smart": {
  7. "type": "custom",
  8. "tokenizer": "ik_smart",
  9. "filter": [
  10. {
  11. "type": "length",
  12. "min": "3"
  13. }
  14. ]
  15. },
  16. "max_word": {
  17. "type": "custom",
  18. "tokenizer": "ik_max_word",
  19. "filter": [
  20. {
  21. "type": "length",
  22. "min": "3"
  23. }
  24. ]
  25. }
  26. }
  27. }
  28. },
  29. "mappings": {
  30. "user": {
  31. "properties": {
  32. "content": {
  33. "type": "text",
  34. "analyzer": "cn_smart",
  35. "fields": {
  36. "max_word": {
  37. "type": "text",
  38. "analyzer": "max_word"
  39. }
  40. }
  41. }
  42. }
  43. }
  44. }
  45. }

使用match进行查询:

  1. {
  2. "query" : {
  3. "match" : {
  4. "content" : "搜索"
  5. }
  6. }
  7. }
  1. {
  2. "query" : {
  3. "match" : {
  4. "content.max_word" : "搜索"
  5. }
  6. }
  7. }

ik_smart会进行标准的中文分词, ik_max_word则会试图穷尽所有分词

ElasticSearch快速指南的更多相关文章

  1. Elasticsearch 快速开始

    Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎. 查询 : Elasticsearch 允许执行和合并多种类型的搜索 - 结构化.非结构化.地理位置.度量指标 - ...

  2. 史上最全面的Elasticsearch使用指南

    Elasticsearch使用指南 Elasticsearch使用指南 前言 ES是什么 什么是全文检索 ES的应用场景 ES的存储结构 第一章:安装 1.下载 2.解压 3.配置 4.启动 5.查看 ...

  3. 初识Elastic search—附《Elasticsearch权威指南—官方guide的译文》

    本文作为Elastic search系列的开篇之作,简要介绍其简要历史.安装及基本概念和核心模块. 简史 Elastic search基于Lucene(信息检索引擎,ES里一个index—索引,一个索 ...

  4. [译] MongoDB Java异步驱动快速指南

    导读 mongodb-java-driver是mongodb的Java驱动项目. 本文是对MongoDB-java-driver官方文档 MongoDB Async Driver Quick Tour ...

  5. Elasticsearch 权威指南

    Elasticsearch 权威指南 http://fuxiaopang.gitbooks.io/learnelasticsearch/content/index.html

  6. 转:C++ Boost/tr1 Regex(正则表达式)快速指南

    C++ Boost/tr1 Regex(正则表达式)快速指南 正则表达式自Boost 1.18推出,目前已经成为C++11(tr1)的标准部分. 本文以Boost 1.39正则表达式为基础,应该广泛适 ...

  7. Elasticsearch 权威指南 NESTAPI地址

    Elasticsearch 权威指南:http://fuxiaopang.gitbooks.io/learnelasticsearch/content/index.html NEST:http://n ...

  8. (译)快速指南:用UIViewPropertyAnimator做动画

    翻译自:QUICK GUIDE: ANIMATIONS WITH UIVIEWPROPERTYANIMATOR 译者:Haley_Wong iOS 10 带来了一大票有意思的新特性,像 UIViewP ...

  9. JUnit5 快速指南

    JUnit5 快速指南 version: junit5 1. 安装 2. JUnit 注解 3. 编写单元测试 3.1. 基本的单元测试类和方法 3.2. 定制测试类和方法的显示名称 3.3. 断言( ...

随机推荐

  1. Word+PS制作拼音表格

    这几天,朋友让帮忙做个拼音表格,使用Word可以直接标注音标,却无法实现小时候那种4线3格,Word的模板只有练习书法的.使用Excel却,无法将拼音标注单独标注到上一单元格(有朋友会VBA的话,帮我 ...

  2. 微信小程序一:微信小程序UI组件、开发框架、实用库

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/8079095.html 内容持续更新,维护中 邮箱 ...

  3. 前端学习_02_vps、web服务器、域名申请

    vps申请 国内比较好用的服务器:阿里云,青云:在国内申请ip比较方便,但是必须要备案域名,否则马上就会被封禁掉. 话说我也有点自己的思路想做个网站,服务器还真的是个问题. 小型的网站只需要ECS服务 ...

  4. gcc编译器用法

    一个用c语言写的程序把他编译成计算机可执行的文件,一般有4个步骤 /*================================================================ ...

  5. 安装Django时报错'module' object has no attribute 'lru_cache'

    使用pip方法安装Django时报错'module' object has no attribute 'lru_cache' 解决办法如下 命令行输入命令sudo pip install Django ...

  6. 机器学习笔记3-Tensorflow简介

    前言 前面两篇主要写了一些机器学习的基础概念,从本篇开始我们来了解下深度学习.深度学习是机器学习的一个子集,是一种特殊的数学模型.同样是从输入到输出,深度学习在这两者之间会有很多层称为"隐层 ...

  7. MyEclipse过期后怎么破解

    方法一:写一个程序生成Subscriptioncode import java.io.*; public class MyEclipseGen { private static final Strin ...

  8. MVC异步加载学习笔记

    一.普通的异步请求 1.View层 <input type="text" id="Name" value="" placeholder ...

  9. Java点滴之类与对象

    类的概述 Java是一门纯粹的面向对象(OOP)语言,面向对象程序是由多个对象所组成的,而对象的创建又必须依赖于类,那么什么又是类呢?在现实世界中,我们常常将多个具有相同或相似特征的对象分为一类,并冠 ...

  10. MongoDb 快速入门教程

    文章首发于[博客园-陈树义],点击跳转到原文MongoDb 快速入门教程. MongoDb 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的. 它是可扩展的 ...