Elasticsearch系列---结构化搜索

概要

结构化搜索针对日期、时间、数字等结构化数据的搜索,它们有自己的格式,我们可以对它们进行范围,比较大小等逻辑操作,这些逻辑操作得到的结果非黑即白,要么符合条件在结果集里,要么不符合条件在结果集之外,没有那种相似的概念。

前言

结构化搜索将会有大量的搜索实例,我们将"音乐APP"作为主要的案例背景,去开发一些跟音乐APP相关的搜索或数据分析,有助力于我们理解实战的目标,顺带巩固一下学习的知识。

我们将一首歌需要的字段暂定为:
| name | code | type | remark |
| :---- | :--: | :--: | -----: |
| ID | id | keyword | 文档ID |
| 歌手 | author | text | |
| 歌曲名称 | name | text | |
| 歌词 | content | text | |
| 语种 | language | text | |
| 标签 | tags | text | |
| 歌曲时长 | length | long | 记录秒数 |
| 喜欢次数 | likes | long | 点击喜欢1次,自增1 |
| 是否发布 | isRelease | boolean | true已发布,false未发布 |
| 发布日期 | releaseDate | date | |

我们手动定义的索引mapping信息如下:

PUT /music
{
  "mappings": {
      "children": {
        "properties": {
          "id": {
            "type": "keyword"
          },
          "author_first_name": {
            "type": "text",
            "analyzer": "english"
          },
          "author_last_name": {
            "type": "text",
            "analyzer": "english"
          },
          "author": {
            "type": "text",
            "analyzer": "english",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "content": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "language": {
            "type": "text",
            "analyzer": "english",
            "fielddata": true
          },
          "tags": {
            "type": "text",
            "analyzer": "english"
          },
          "length": {
            "type": "long"
          },
          "likes": {
            "type": "long"
          },
          "isRelease": {
            "type": "boolean"
          },
          "releaseDate": {
            "type": "date"
          }
        }
      }
  }
}

我们预先导入一批数据进去:

POST /music/children/_bulk
{ "index": { "_id": 1 }}
{ "id" : "34116101-7fa2-5630-a1a4-1735e19d2834", "author_first_name":"Peter", "author_last_name":"Gymbo", "author" : "Peter Gymbo", "name": "gymbo", "content":"I hava a friend who loves smile, gymbo is his name", "language":"english", "tags":["enlighten","gymbo","friend"], "length":53, "likes": 5, "isRelease":true, "releaseDate": "2019-12-20" }
{ "index": { "_id": 2 }}
{ "id" : "34117101-54cb-59a1-9b7a-82adb46fa58d", "author_first_name":"John", "author_last_name":"Smith", "author" : "John Smith", "name": "wake me, shark me", "content":"don't let me sleep too late, gonna get up brightly early in the morning", "language":"english", "tags":["wake","early","morning"], "length":55, "likes": 8,"isRelease":true, "releaseDate": "2019-12-21" }
{ "index": { "_id": 3 }}
{ "id" : "34117201-8d01-49d4-a495-69634ae67017", "author_first_name":"Jimmie", "author_last_name":"Davis", "author" : "Jimmie Davis", "name": "you are my sunshine", "content":"you are my sunshine, my only sunshine, you make me happy, when skies are gray", "language":"english", "tags":["sunshine","happy"], "length":65,"likes": 12, "isRelease":true, "releaseDate": "2019-12-22" }
{ "index": { "_id": 4 }}
{ "id" : "55fa74f7-35f3-4313-a678-18c19c918a78", "author_first_name":"Peter", "author_last_name":"Raffi", "author" : "Peter Raffi", "name": "brush your teeth", "content":"When you wake up in the morning it's a quarter to one, and you want to have a little fun You brush your teeth", "language":"english", "tags":"teeth", "length":45,"likes": 17, "isRelease":true, "releaseDate": "2019-12-22" }
{ "index": { "_id": 5 }}
{ "id" : "1740e61c-63da-474f-9058-c2ab3c4f0b0a", "author_first_name":"Jean", "author_last_name":"Ritchie", "author" : "Jean Ritchie", "name": "love somebody", "content":"love somebody, yes I do", "language":"english", "tags":"love", "length":38, "likes": 3,"isRelease":true, "releaseDate": "2019-12-22" }

精确值查找

我们根据文档的mapping设计,可以按ID、按日期进行查找。

根据ID搜索歌曲

GET /music/children/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "term" : {
                    "id" : "34116101-7fa2-5630-a1a4-1735e19d2834"
                }
            }
        }
    }
}

注意ID建立时,类型是指定为keyword,这样ID在索引时不会进行分词。如果类型为text,UUID值在索引时会分词,这样反而查不到结果了。

按日期搜索歌曲

GET /music/children/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "term" : {
                    "releaseDate" : "2019-12-21"
                }
            }
        }
    }
}

按歌曲时长搜索

GET /music/children/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "term" : {
                    "length" : 53
                }
            }
        }
    }
}

搜索已发布的歌曲

GET /music/children/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "term" : {
                    "isRelease" : true
                }
            }
        }
    }
}

以上3个小例子可以发现:准确值搜索对keyword、日期、数字、boolean值天然支持。

组合过滤

前面的4个小例子都是单条件过滤的,实际的需求肯定会有多个条件,不过万变不离其宗,再复杂的搜索需求,也是由一个一个的基础条件复合而成的,我们来看几个简单的组合过滤的例子。

复习一下之前学过的逻辑:

  • bool 组合多个条件,可以嵌套
  • must 必须匹配
  • should 可以匹配(类似于or,多个条件在should里)
  • must_not 必须不匹配

搜索发布日期为2019-12-20,或歌曲ID为2a8f4288-c0a9-5c9b-8f99-67339b66f4c0,但发布日期不能是2019-12-21的歌曲

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "should": [
            {"term":{
              "releaseDate":"2019-12-20"
            }},
            {"term":{
              "id":"2a8f4288-c0a9-5c9b-8f99-67339b66f4c0"
            }}
          ],
          "must_not": {
          "term": {
            "releaseDate":"2019-12-21"
          }
        }
        }
      }
    }
  }
}

搜索歌曲ID为2a8f4288-c0a9-5c9b-8f99-67339b66f4c0,或者是歌曲ID为34116101-7fa2-5630-a1a4-1735e19d2834而且发布日期为2019-12-20的帖子

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "should": [
            {"term":{
              "id":"2a8f4288-c0a9-5c9b-8f99-67339b66f4c0"
            }},
            {
              "bool": {
                "must" : [
                  {
                  "term" : {
                "id":"34116101-7fa2-5630-a1a4-1735e19d2834"
                  }},
                 { "term" : {
                    "releaseDate":"2019-12-20"
                  }}
                ]
              }
            }
          ]
        }
        }
      }
    }
  }

多值搜索

使用语法terms,可以同时搜索多个值,类似mysql的in语句。

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "terms": {
          "id": [
            "34116101-7fa2-5630-a1a4-1735e19d2834",
            "99268c7e-8308-569a-a975-bbce7d3f9a8e"
          ]
        }
      }
    }
  }
}

范围查询

针对Long类型和date类型的数据,是支持范围查询的,使用gt、lt、gte、lte来完成范围的判断。与mysql的>、<、>=、<=以及between...and异曲同工。

搜索时长在45-60秒之间的歌曲

对Long类型的范围查询,直接使用范围表达式:

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "length": {
            "gte": 45,
            "lte": 60
          }
        }
      }
    }
  }
}

日期的范围搜索

针对日期的范围搜索,除了直接写日期,加上常规的范围表达式之外,还可以使用+1d、-1d表示对指定日期的加减,如"2019-12-21||-1d"表示"2019-12-20",也可以使用now-1d表示昨天,挺有趣。

给个示例:搜索2019-12-21前一天新发布的歌曲

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "releaseDate" :{
          "gt":"2019-12-21||-1d"
        }
      }
    }
  }
}
}

Null值处理

倒排索引在建立时,是不接受空值的,这就意味着null,[],[null]这些各种形式的null值,不无法存入倒排索引的,那这样怎么办?

Elasticsearch提供了两种查询,类似于mysql的is not null和not exists。

存在查询

exists查询,会返回那些指定字段有值的文档,与mysql的is not null类似。

案例中的tags字段,就是一个选填项,有些记录可能是null值,如果我需要查询所有的tags值的记录,请求如下:

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "exists": {
          "field": "tags"
        }
      }
    }
  }
}

缺失查询

缺失查询原来是有关键字missing表示,效果与exists相反,语法上与mysql的is null类似,但6.x版本就已经废弃了,我们可以改用must not + exists实现相同的效果。

还是使用tags字段为例,查询tags为空的文档:

GET /music/children/_search
{
  "query": {
    "bool": {
      "must_not": {
        "exists": {
          "field": "tags"
        }
      }
    }
  }
}

filter缓存

过滤器为什么效率那么高?除了本身的设计集合来达到高效过滤之外,还将查询结果适当地缓存化。

filter执行原理

我们了解一下Elasticsearch对过滤器的简单操作:

  1. 根据fitler条件查找匹配的文档,获取document list。如果有多个过滤条件且涉及多个字段,那么就会有多个document list,document list是按倒排索引来的。
  2. 根据document list构建bitset(包含0或1的数组),匹配了是1,没匹配上为0,如[1,0,0,0]。
  3. 迭代所有的bitset,从最稀疏的开始(可以排除到大量的文档),取数组相同位置所有值为1的记录。
  4. 将bitset缓存在内存中,用于提高性能。

filter比query好处是会caching,下次不用查倒排索引,filter大部分情况下在query之前执行query会计算doc对搜索条件的relevance score,还会根据这个score去排序
filter简单过滤出想要的数据,不计算relevance score,也不排序

filter缓存

缓存条件
  1. 最近的256个filter中,某个filter超过一定次数(次数不固定),就会自动缓存这个filter对应的bitset。
  2. filter针对小segment获取的结果,可以不缓存,segment<1000条或segment大小<index总大小的 3%。原因是数据量小,重新扫描很快,太小的segment在后台会自动合并到大的segment中,缓存意义不大
缓存更新

缓存的更新非常智能,增量更新的方式,如果有document新增或修改时,会将新文档加入bitset,而不是删除缓存或整个重新计算。

小结

本篇前半部分使用了大量的示例,可以快速阅读,后面介绍了filter的过滤原理及缓存处理机制,可以了解一下,谢谢。

专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
可以扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术

原文地址:https://www.cnblogs.com/huangying2124/p/12230175.html

时间: 2024-11-07 23:35:42

Elasticsearch系列---结构化搜索的相关文章

ElasticSearch常用结构化搜索

最近,需要用到ES的一些常用的结构化搜索命令,因此,看了一些官方的文档,学习了一下.结构化查询指的是查询那些具有内在结构的数据,比如日期.时间.数字都是结构化的. 它们都有精确的格式,我们可以对这些数据进行逻辑操作,比较常见的操作包括比较时间区间,或者获取两个数字间的较大值. 精确查询 当进行精确查询时,过滤器filter是十分重要的,因为它们效率非常高,过滤器不计算相关性(直接跳过了整个记分阶段)而且很容易进行缓存. 过滤数字 我们首先看 term filter,它最常用,可以用来处理数字,布

ElasticSearch结构化搜索和全文搜索

https://segmentfault.com/a/1190000019753737?utm_source=tag-newest 1.结构化搜索 1.1 精确值查找 过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存.请尽可能多的使用过滤式查询. term 查询会查找我们指定的精确值.作为其本身, term 查询是简单的.它接受一个字段名以及我们希望查找的数值:{ "term" : { "price" : 20 } }

ElasticSearch 结构化搜索全文

1.介绍 上篇介绍了搜索结构化数据的简单应用示例,现在来探寻 全文搜索(full-text search) :怎样在全文字段中搜索到最相关的文档. 全文搜索两个最重要的方面是: 相关性(Relevance) 它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是 TF/IDF 方法(参见 相关性的介绍).地理位置邻近.模糊相似,或其他的某些算法. 分析(Analysis) 它是将文本块转换为有区别的.规范化的 token 的一个过程,(参见 分析的介绍) 目

Elasticsearch系列---多字段搜索

概要 本篇介绍一下multi_match的best_fields.most_fields和cross_fields三种语法的场景和简单示例. 最佳字段 bool查询采取"more-matches-is-better"匹配越多分越高的方式,所以每条match语句的评分结果会被加在一起,从而为每个文档提供最终的分数_score.能与两条语句同时匹配的文档会比只与一条语句匹配的文档得分要高,但有时这样也会带来一些与期望不符合的情况,我们举个例子: 我们以英文儿歌为案例背景,我们这样搜索: G

ElasticSearch结构化查询

ElasticSearch结构化查询 Elasticsearch 提供了丰富的查询过滤语句,而有一些是我们较常用到的. 现在我们快速的介绍一下 这些最常用到的查询过滤语句. term 过滤 term主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据类型): { "term": { "age": 26 }} { "term": { "date": "2014-09-0

【文智背后的奥秘】系列篇——结构化抽取平台

版权声明:本文由文智原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/91 来源:腾云阁 https://www.qcloud.com/community 随着大数据时代的到来,一个大规模生成.分享.处理以及应用数据的时代正在开启.如果能将互联网上异源异构的非结构化或半结构化数据转换为更易处理的结构化数据,可以极大的降低获取数据的门槛,为信息检索和数据挖掘提供基础,更好的挖掘数据中蕴藏的价值. 单纯考虑网页这种半结构化数据

结构化异常处理系列

最近在项目中看到了许多自定义异常类,就想把它搞明白. 想知道为什么要这样处理?这样处理的优点是什么?自己怎么常见自己需要的自定义异常类? C#结构化异常处理是将程序中出现的错误的名称.消息和其他有用的信息都打进一个定义明确的包内. 构建一个强类型的自定义异常,这样在程序中对异常处理时就可以根据不同问题具有针对性的使用异常类,这样使用起来也更加明了,一看到就知道这里处理的是什么异常,一目了然. 下面就将学习笔记放在这里,希望大家多多指点: 结构化异常处理(一)概述 结构化异常处理(二)配置异常的状

Hulu机器学习问题与解答系列 | 二十二:特征工程—结构化数据

听说最近冒出的大批呱儿子个个都是撑着眼皮也要看书的无眠小青蛙.我们学习Machine Learning的脚步又怎能停下来?动动手指,上滑开始~ 今天的内容是 [特征工程-结构化数据] 场景描述 特征工程是指结合问题寻找有效的特征并进行处理成适合模型的输入形式.机器学习中有句经典的话叫做"Garbage in, garbage out",意思是如果输入的数据是垃圾,那么得到的结果也是垃圾.可以看出模型成败的关键并不仅仅取决于模型的选取,还取决于我们是否有根据特定的问题找到了行之有效的输入

搜索引擎系列十:Solr(solrj 、索引API 、 结构化数据导入)

一.SolrJ介绍 1. SolrJ是什么? Solr提供的用于JAVA应用中访问solr服务API的客户端jar.在我们的应用中引入solrj: <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>7.3.0</version> </dependency> 2. SolrJ的核