橘子学ES19之词项搜索全文检索
在ES中,词项搜索也叫term搜索,term就有词项的意思。词项检索的意思就是说我输入一个词汇,在检索的时候不会把你输入的这个词汇做分词,匹配条件就是完整的输入的词汇,但是文档插入的时候该分词还是分词。下面会有例子说明。
全文检索不一样,全文检索就是按照分词插入,分词匹配,分词处理输入条件。
一、基于Term的查询
1、简介
term是表达语义最小的单位,搜索和利用统计语言模型进行自然语言处理都需要处理它。
在ES中term查询包括:Term Query / Range Query / Exists Query / Prefix Query / Wildcard Query(通配符查询)
ES中的Term查询,对输入不做分词(注意只是对输入),会将输入作为一个整体,在倒排索引中查找准确的词项。
而且使用相关度算分公式会为每个包含该词项的文档进行相关度算分,也就是说只要包含这个词的索引,都会算分,可能一个文档出现多次,分就高了。这个后面算分的时候看看。
可以通过Constant Score将查询转换成一个Filtering避免算分,而且可以利用到缓存,提高性能。
2、操作演示
1、准备数据
批量插入一些数据给products,动态mapping
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "productID" : "XHDK-A-1293-#fJ3","desc":"iPhone" }
{ "index": { "_id": 2 }}
{ "productID" : "KDKE-B-9947-#kL5","desc":"iPad" }
{ "index": { "_id": 3 }}
{ "productID" : "JODL-X-1937-#pV7","desc":"MBP" }、GET /products/_mapping 查看一下索引结构
{"products" : {"mappings" : {"properties" : {"desc" : { desc自动识别成为text,插入会分词"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"productID" : { productID自动识别成为text,插入会分词"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}}}}}
}
2、执行一下term查询
POST /products/_search
{"query": {"term": { 指定查询类型为term"desc": {"value":"iphone" 这里我们用的是iphone小写的}}}
}
查询结果为:
hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.9808292,"hits" : [{"_index" : "products","_type" : "_doc","_id" : "1","_score" : 0.9808292,"_source" : {"productID" : "XHDK-A-1293-#fJ3","desc" : "iPhone"}}]}
此时我们能查出结果,然后把查询条件里面的iphone换为iPhone大写的p
看下查询结果
"hits" : {"total" : {"value" : 0,"relation" : "eq"},"max_score" : null,"hits" : [ ]}
注意,此时就没插出来东西。
我们解释一下上述问题,因为term查询是对查询条件不做分词处理的,就是精确的词项查询,但是你插入的时候也看到了动态mapping把desc识别成了text,插入的时候做了分词,前面说过了,默认分词进去是转小写的,所以此时建立的倒排索引都是小写的,这里你精确匹配的时候存在大写,自然就查不到了。
3、检验一下分词效果
我们使用标准分词验证一下他插入的时候是不是真的转了小写,其实前面讲三大组件的时候说过。
POST /_analyze
{"analyzer": "standard","text": ["iPhone"]
}
分词效果是
{"tokens" : [{"token" : "iphone","start_offset" : 0,"end_offset" : 6,"type" : "<ALPHANUM>","position" : 0}]
}
3、再次演示
1、我们以航班号检索一下
POST /products/_search
{"query": {"term": {"productID": {"value": "XHDK-A-1293-#fJ3"}}}
}
查询结果依然是空,我们不妨再看下标准分词器下的分词效果。
POST /_analyze
{"analyzer": "standard","text": ["XHDK-A-1293-#fJ3"]
}
看到分词结果为:
{"tokens" : [{"token" : "xhdk","start_offset" : 0,"end_offset" : 4,"type" : "<ALPHANUM>","position" : 0},{"token" : "a","start_offset" : 5,"end_offset" : 6,"type" : "<ALPHANUM>","position" : 1},{"token" : "1293","start_offset" : 7,"end_offset" : 11,"type" : "<NUM>","position" : 2},{"token" : "fj3","start_offset" : 13,"end_offset" : 16,"type" : "<ALPHANUM>","position" : 3}]
}
我们看到他按照空格做了拆分、并且转了小写、所以你输入整个词汇是没有这个分词的,所以你检索不出来。
2、解决方式1
你要是用分词一个的词汇去找,就能找到了,因为他有这个词项的索引
POST /products/_search
{"query": {"term": {"productID": {"value": "xhdk"}}}
}
3、解决方式2
但是有时候我们就要全部匹配,因为用户想我插入的是全部的,为啥就查不出来了。你是不是在逗我。
而且插入的是个整个词汇,如果具有唯一性,你整个查出了一个。但是你要是分词的字段查可能是多个,这有时候也不符合业务。
此时要怎么处理呢,我们想一下,我们在插入text的类型的时候,他默认给你一个keyword类型的子字段,在ES中keyword是不分词的,所以你直接用keyword这个子字段查就可以了。
POST /products/_search
{"query": {"term": {"productID.keyword": { 这里是语法"value": "XHDK-A-1293-#fJ3"}}}
}
查询结果如下:
{"took" : 1,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.9808292, 返回了算法"hits" : [{"_index" : "products","_type" : "_doc","_id" : "1","_score" : 0.9808292,"_source" : {"productID" : "XHDK-A-1293-#fJ3","desc" : "iPhone"}}]}
}
4、符合查询-Constant Score 转为Filter
我们上面看到了term查询的时候,返回了查询结果的算分结果,有时候我们不想要这个算分,因为算分影响性能,他多了算分这个步骤,自然耗时。所以就需要忽略TF-IDF(算分),避免相关性算分的开销。
避免算分可以把Query转为Filter查询,Filter还能有效利用缓存,性能更好一些。
POST /products/_search
{"explain": true, 开启分析计划"query": {"constant_score": { 先转为Constant Score"filter": { Constant Score里面可以使用filter"term": { 最终查询还是term。"productID.keyword": "XHDK-A-1293-#fJ3"}}}}
}
注意,我们最终查询还是term,只是在term的基础上转为了filter,两者结合。查询结级果为:
{"took" : 3,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 1.0, 分数不计算了直接就是1"hits" : [{"_shard" : "[products][0]","_node" : "wHEn3SGkRz2IuJ2Cl2M1Xw","_index" : "products","_type" : "_doc","_id" : "1","_score" : 1.0,"_source" : {"productID" : "XHDK-A-1293-#fJ3","desc" : "iPhone"},"_explanation" : { 查询计划分析"value" : 1.0,"description" : "ConstantScore(productID.keyword:XHDK-A-1293-#fJ3)","details" : [ ]}}]}
}
二、基于全文的查询
ES的一大特点就是全文检索,其实是lucence的东西。
1、简介
1、基于全文本的查找
在ES中,Match Query / Match Phrase Query / Query String Query
这些都是全文检索类型的Query
2、特点
- 索引文档(就是插入文档)的时候,以及检索的时候都会进行分词,查询字符串先传递到一个合适的分词器,然后生成一个供查询的词项列表,就是把输入的查询字段先分词得到一个列表。
- 查询的时候,先会对输入的查询进行分词,然后把每个词项逐个进行底层的查询,最终将结果进行合并,并且为每一个文档生成一个算分。例如查"fuck you",会查到包括fuck 或者 you的所有结果。前面我们讲的时候还可以设置操作符and 之类的。
#设置 position_increment_gap
DELETE groups
PUT groups
{"mappings": {"properties": {"names":{"type": "text","position_increment_gap": 0 默认是100,这里设置为0}}}
}GET groups/_mappingPOST groups/_doc
{"names": [ "John Water", "Water Smith"]
}POST groups/_search
{"query": {"match_phrase": {"names": {"query": "Water Water","slop": 100 意思就是中间填充100个位置}}}
}POST groups/_search
{"query": {"match_phrase": {"names": "Water Smith"}}
}
2、多字段检索
对多值字段使用短语匹配时会发生奇怪的事。 想象一下你索引这个文档:
PUT /my_index/groups/1
{"names": [ "John Abraham", "Lincoln Smith"]
}
然后运行一个对 Abraham Lincoln 的短语查询:GET /my_index/groups/_search
{"query": {"match_phrase": {"names": "Abraham Lincoln"}}
}
令人惊讶的是, 即使 Abraham 和 Lincoln 在 names 数组里属于两个不同的人名, 我们的文档也匹配了查询。 这一切的原因在Elasticsearch数组的索引方式。
在分析 John Abraham 的时候, 产生了如下信息:
Position 1: john
Position 2: abraham
然后在分析 Lincoln Smith 的时候, 产生了:
Position 3: lincoln
Position 4: smith
换句话说, Elasticsearch对以上数组分析生成了与分析单个字符串 John Abraham Lincoln Smith 一样几乎完全相同的语汇单元。 我们的查询示例寻找相邻的 lincoln 和 abraham , 而且这两个词条确实存在,并且它们俩正好相邻, 所以这个查询匹配了。
幸运的是, 在这样的情况下有一种叫做 position_increment_gap 的简单的解决方案, 它在字段映射中配置。
DELETE /my_index/groups/ (1)PUT /my_index/_mapping/groups (2)
{"properties": {"names": {"type": "string","position_increment_gap": 100 配置100的间隔,此时数据的每个元素之间分词的时候,实际上中间是有100的间隔的}}
}
首先删除映射 groups 以及这个类型内的所有文档。
然后创建一个有正确值的新的映射 groups 。
position_increment_gap 设置告诉 Elasticsearch 应该为数组中每个新元素增加当前词条 position 的指定值。 所以现在当我们再索引 names 数组时,会产生如下的结果:
Position 1: john
Position 2: abraham
Position 103: lincoln
Position 104: smith
现在我们的短语查询可能无法匹配该文档因为 abraham 和 lincoln 之间的距离为 100 。 要是你还想查出来,那么为了匹配这个文档你必须添加值为 100 的 slop (就是添虫100个间隔,就能匹配到了)。
橘子学ES19之词项搜索全文检索相关推荐
- ES20-JAVA API 词项搜索
2019独角兽企业重金招聘Python工程师标准>>> 1.term query term是代表完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词,所以我们的搜索词必须是文档分词 ...
- laravel 分词搜索匹配度_elasticsearch基础笔记9-elasticsearch 词项全文搜索
es的核心功能就是搜索和分析.那么我们看看搜索相关内容 1.搜索机制 在进入搜索之前,会对查询体根据情况进行分析和处理. 2.有哪些常用搜索类型 全文查询 词项查询 复合查询 嵌套查询 位置查询 特殊 ...
- Elasticsearch中基于词项的搜索
为了方便我们学习,我们导入kibana为我们提供的范例数据. 目前为止,我们已经探索了如何将数据放入Elasticsearch,现在来讨论下如何将数据从Elasticsearch中拿出来,那就是通过搜 ...
- WWW 2020 | 信息检索中基于上下文的文本词项权重生成
©PaperWeekly 原创 · 作者|金金 单位|阿里巴巴研究实习生 研究方向|推荐系统 本文由卡耐基梅隆大学发表于 WWW 2020,介绍了基于上下文的文本词项权重生成方法 HDCT.原有的搜索 ...
- 搜索引擎核心技术与算法 —— 词项词典与倒排索引优化
一只小狐狸带你解锁NLP/ML/DL秘籍 作者:QvQ 老板-我会写倒排索引啦!我要把它放进咱们自研搜索引擎啦! 我呸!你这种demo级代码,都不够当单元测试的! 嘤嘤嘤,课本上就是这样讲的呀?! 来 ...
- 《introduction to information retrieval》信息检索学习笔记2 词项词汇和倒排记录表
第2章 词项词汇和倒排记录表 回顾建立倒排索引的主要步骤: 1.收集要索引的文档. 2.词条化文本. 3.对词条进行语言预处理,生成标准化词条. 4.建立倒排索引,索引每个词项出现的文档. 2.1文档 ...
- 信息检索(基础知识一)——词项-文档关联矩阵及倒排索引构建
sdnu 202011000106 实现内容: (本文中用到的文件名为:hyatt-k,有需要可以留言) 利用文件读取方法对给定邮件数据集中的文本文件进行预处理,并按照图1中的词项词典构造流程生成词项 ...
- PostgreSQL 实时高效搜索 - 全文检索、模糊查询、正则查询、相似查询、ADHOC查询...
标签 PostgreSQL , 搜索引擎 , GIN , ranking , high light , 全文检索 , 模糊查询 , 正则查询 , 相似查询 , ADHOC查询 背景 字符串搜索是非常常 ...
- 自然语言处理--词向量使用基于 Annoy 的高级索引(近似最近邻)来查找最近邻词项
我们可以使用 Annoy 对 word2vec 向量建立索引并将其结果与 gensim 的 KeyedVectors索引进行对比( 像 Annoy 这样的局部敏感哈希使潜在语义索引成为现实): fro ...
最新文章
- idea svn的项目无法标识修改新增的类_是时候让你的 IDEA 飞起来啦!
- 【笔记】重学前端-winter
- JavaWeb(一)——web服务器、Tomcat安装和配置
- OpenGL之矩阵变换的原理分析与数学推导
- Mysql大数据备份和增量备份及还原
- 快速打开Github某个commit页面的方法
- 如何远程管理Quartz
- 清除css,js,img的浏览器缓存
- Android APP性能优化(一)
- flask-restful 开发API
- php gd libpng,libpng版本问题导致的PHP调用gd扩展出错解决方案
- 用计算机发传真,用电脑怎么发传真
- 离散数学-自反性-对称性-传递性,关系的性质
- 弦截法 解高次方程 C语言/C++
- 分享一点关于安装、使用达梦数据库的愚见
- One Pixel Attack(对抗攻击) —— 使用差分进化算法寻找最优解
- 浅谈教师资格证备考心得
- OFDM和OFDMA的主要优缺点
- 业务流程持续优化的三个方法
- matlab学习笔记9 高级绘图命令_2 图形的高级控制_视点控制和图形旋转_色图和颜色映像_光照和着色