概要

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

前言

结构化搜索将会有大量的搜索实例,我们将"音乐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系列---结构化搜索的更多相关文章

  1. ElasticSearch常用结构化搜索

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

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

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

  3. ElasticSearch 2 (13) - 深入搜索系列之结构化搜索

    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(6)-结构化查询

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

随机推荐

  1. linux测试 scullpipe 驱动

    我们已经见到了 scullpipe 驱动如何实现阻塞 I/O. 如果你想试一试, 这个驱动的源码 可在剩下的本书例子中找到. 阻塞 I/O 的动作可通过打开 2 个窗口见到. 第一个可运行 一个命令诸 ...

  2. vue 模块化 路由拆分配置

    一.普通路由配置 通常我们编写vue路由配置都会写在 /src/router/index.js 这个文件下.但是,随着我们的vue项目变得越来越大后,路由也随之变得越来越多,出现的问题就是我们所有的路 ...

  3. XSS攻击及防范

    1.什么是XSS攻击 跨站脚本攻击(Cross Site Scripting),攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到 ...

  4. kafka性能测试代码

    bin/kafka-producer-perf-test.sh --num-records 5000000 --record-size 5000 \ --topic kafkatopic2 \ --b ...

  5. Memcahced 缓存过期时间问题

    转载:https://help.aliyun.com/knowledge_detail/38654.html 关于设置缓存数据的过期时间,可以参考以下Memcached官方说明: An expirat ...

  6. word HTML文件与Markdwon互相转换的几种方式

    Tip:word文件与Markdwon转换往往是可逆的.无论使用哪种方式,要想完美转换,必须要预先处理掉markdown与word不兼容的格式,如word文件对象,带边框的代码块等等 方法一:借助pa ...

  7. eclipse中SSM(maven)项目搭建全过程+实现用户登录功能

    项目创建之前确保eclipse中已经配置好了jdk,tomcat,maven如果没有配置下面有配置教程的链接   eclipse中配置jdk的教程url:http://www.cnblogs.com/ ...

  8. Linux 安装 Nvidia 驱动出现的黑屏各种问题和解决方式

    之前因为想OBS支持h264-nvenc这个功能然后就编译ffmpeg,然后使用Github上面的一个编译项目),项目编译完成之后重启电脑,然后就进入不了系统的登录页面了,选择进入Linux系统之后就 ...

  9. 深度兴趣网络DIN-SIEN-DSIN

    看看阿里如何在淘宝做推荐,实现"一人千物千面"的用户多样化兴趣推荐,首先总结下DIN.DIEN.DSIN: 传统深度学习在推荐就是稀疏到embedding编码,变成稠密向量,喂给N ...

  10. docker网络类型访问原理

    • bridge –net=bridge 默认网络,Docker启动后创建一个docker0网桥,默认创建的容器也是添加到这个网桥中. • host –net=host 容器不会获得一个独立的netw ...