Elasticsearch系列---结构化搜索

时间:2022-08-27 20:25:21

概要

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

前言

结构化搜索将会有大量的搜索实例,我们将"音乐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架构社区微信群共同探讨技术

Elasticsearch系列---结构化搜索

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

  1. ElasticSearch常用结构化搜索

    最近,需要用到ES的一些常用的结构化搜索命令,因此,看了一些官方的文档,学习了一下.结构化查询指的是查询那些具有内在结构的数据,比如日期.时间.数字都是结构化的. 它们都有精确的格式,我们可以对这些数 ...

  2. elasticsearch 深入 —— 结构化搜索

    结构化搜索 结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程.比如日期.时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作.比较常见的操作 ...

  3. ElasticSearch 2 &lpar;13&rpar; - 深入搜索系列之结构化搜索

    ElasticSearch 2 (13) - 深入搜索系列之结构化搜索 摘要 结构化查询指的是查询那些具有内在结构的数据,比如日期.时间.数字都是结构化的.它们都有精确的格式,我们可以对这些数据进行逻 ...

  4. ElasticSearch 结构化搜索

    1.介绍 结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程.比如日期.时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作. 比较常见的操作 ...

  5. ElasticStack学习(九):深入ElasticSearch搜索之词项、全文本、结构化搜索及相关性算分

    一.基于词项与全文的搜索 1.词项 Term(词项)是表达语意的最小单位,搜索和利用统计语言模型进行自然语言处理都需要处理Term. Term的使用说明: 1)Term Level Query:Ter ...

  6. Elasticsearch结构化搜索与查询

    Elasticsearch 的功能之一就是搜索,搜索主要分为两种类型,结构化搜索和全文搜索.结构化搜索是指有关查询那些具有内在结构数据的过程.比如日期.时间和数字都是结构化的:它们有精确的格式,我们可 ...

  7. Elasticsearch 结构化搜索、keyword、Term查询

    前言 Elasticsearch 中的结构化搜索,即面向数值.日期.时间.布尔等类型数据的搜索,这些数据类型格式精确,通常使用基于词项的term精确匹配或者prefix前缀匹配.本文还将新版本的&qu ...

  8. ElasticSearch 结构化搜索全文

    1.介绍 上篇介绍了搜索结构化数据的简单应用示例,现在来探寻 全文搜索(full-text search) :怎样在全文字段中搜索到最相关的文档. 全文搜索两个最重要的方面是: 相关性(Relevan ...

  9. ElasticSearch&lpar;6&rpar;-结构化查询

    引用:ElasticSearch权威指南 一.请求体查询 请求体查询 简单查询语句(lite)是一种有效的命令行_adhoc_查询.但是,如果你想要善用搜索,你必须使用请求体查询(request bo ...

随机推荐

  1. SAM初探

    SAM,即Suffix Automaton,后缀自动机. 关于字符串有很多玩法,有很多算法都是围绕字符串展开的.为什么?我的理解是:相较于数字组成的序列,字母组成的序列中每个单位上元素的个数是有限的. ...

  2. SHA-1 加密算法破解现已只需要 10 天

    转自:http://www.linuxeden.com/html/news/20151009/163173.html SHA-1是如今很常见的一种加密哈希算法,HTTPS传输和软件签名认证都很喜欢它, ...

  3. HTK学习2:工具使用

    选自:http://www.cnblogs.com/mingzhao810/archive/2012/08/03/2617674.html 这个是重点,呵呵,本部分会讨论到如下内容: 1. 建立语音材 ...

  4. input 字符限制

    1,文本框只能输入数字代码(小数点也不能输入): onkeyup="this.value=this.value.replace(/\D/g,'')" onafterpaste=&q ...

  5. Python IDLE 运行错误:IDLE&&num;39&semi;s subprocess didn&&num;39&semi;t make connection&period; --已解决(原创)!

    Python IDLE 错误描述: Subprocess Startup ErrorIDLE's subprocess didn't make connection. Either IDLE can' ...

  6. Linux Curl常用命令使用【转】

    Curl是Linux下一个很强大的http命令行工具,其功能十分强大. 1)读取网页 $ curl linuxidc.com">http://www.linuxidc.com 2)保存 ...

  7. &lpar;链表 importance&rpar; leetcode 2&period; Add Two Numbers

    You are given two non-empty linked lists representing two non-negative integers. The digits are stor ...

  8. BUAA-OO-表达式解析与求导

    BUAA-OO-表达式解析与求导 解析 按照常规,解析这一部分我们分为词法分析与语法分析.当然由于待解析的字符串较简单,词法分析器和语法分析器不必单独实现. 词法分析器 按照常规,我们先手写一个词法分 ...

  9. 确保安全的HTTPS(对HTTP加密的几种技术,前端面试常问)第一篇

    HTTP固然足够好,但是在安全方面有着很大隐患: 1.与服务器进行通信使用的是明文,内容可能会被窃听(HTTP协议本身并不具备加密功能,所以无法对请求和响应的内容进行加密) 2.使用HTTP协议的服务 ...

  10. 【EF框架异常】System&period;MissingMethodException&colon;&OpenCurlyDoubleQuote;找不到方法&colon;&OpenCurlyDoubleQuote;System&period;Data&period;Entity&period;ModelConfiguration&period;Configuration&period;PrimitivePropertyConfiguration

    最近调试EF的时候遇到下面这个问题 System.MissingMethodException:“找不到方法:“System.Data.Entity.ModelConfiguration.Config ...