Search a part of word with ElasticSearch

来自stackoverflow

https://stackoverflow.com/questions/6467067/how-to-search-for-a-part-of-a-word-with-elasticsearch

场景还原

// 初始化数据

POST /my_idx/my_type/_bulk
{"index": {"_id": "1"}}
{"name": "John Doeman", "function": "Janitor"}
{"index": {"_id": "2"}}
{"name": "Jane Doewoman", "function": "Teacher"}
{"index": {"_id": "3"}}
{"name": "Jimmy Jackal", "function": "Student"}

Question

ElasticSearch中有数据如下:

{
"_id" : "1",
"name" : "John Doeman",
"function" : "Janitor"
}
{
"_id" : "2",
"name" : "Jane Doewoman",
"function" : "Teacher"
}
{
"_id" : "3",
"name" : "Jimmy Jackal",
"function" : "Student"
}

现在期望搜索所有包含Doe的文档

// 并没有返回任何文档

GET /my_idx/my_type/_search?q=Doe
// 返回一个文档

GET /my_idx/my_type/_search?q=Doeman

提问者还更换了分词器,改用请求体的方式,但这也不行:

GET /my_idx/my_type/_search
{
"query": {
"term": {
"name": "Doe"
}
}
}

后来使用了nGramtokenizerfilter

{
"index": {
"index": "my_idx",
"type": "my_type",
"bulk_size": "100",
"bulk_timeout": "10ms",
"analysis": {
"analyzer": {
"my_analyzer": {
"type": "custom",
"tokenizer": "my_ngram_tokenizer",
"filter": [
"my_ngram_filter"
]
}
},
"filter": {
"my_ngram_filter": {
"type": "nGram",
"min_gram": 1,
"max_gram": 1
}
},
"tokenizer": {
"my_ngram_tokenizer": {
"type": "nGram",
"min_gram": 1,
"max_gram": 1
}
}
}
}
}

引入了另外一个问题:任意的查询都可以返回所有文档

Answers

首先这是一个分词引起的问题,索引默认情况下使用standard分词器,对于文档:

{
"_id" : "1",
"name" : "John Doeman",
"function" : "Janitor"
}
{
"_id" : "2",
"name" : "Jane Doewoman",
"function" : "Teacher"
}
{
"_id" : "3",
"name" : "Jimmy Jackal",
"function" : "Student"
}

索引后会得到这样一个映射,这里只考虑了name字段的分词:

segment document id list
john 1
doeman 1
jane 2
doewoman 2
jimmy 3
jackal 3

那么现在考虑我们的搜索

Search 1

GET /my_idx/my_type/_search?q=Doe

standard分词器会将Doe分析为doe,然后到索引表中查找,并不会找到doe这个索引,因此返回空

Search 2

GET /my_idx/my_type/_search?q=Doeman

standard分词器会将Doeman分析为doeman,然后到索引表中找到了该索引,会发现只有doc ID 1包含该索引,所以只返回一个文档

Search 3

GET /my_idx/my_type/_search
{
"query": {
"term": {
"name": "Doe"
}
}
}

term查询,Doe还是Doe,不会被分析器分析,但是Doe在索引表中依然是不存在的,所以这个方法也无法返回任何文档。

Search 4

额外说明,题主并没有用这种方式试过

GET /my_idx/my_type/_search
{
"query": {
"term": {
"name": "Doeman"
}
}
}

不要以为这样就能找到了,因为term不进行分析,所以直接从索引表中找Doeman也是没有任何文档匹配的,除非把Doeman改为doeman

解决方案

总结了一下stackoverflow上的答案,目前有这么几种可行方案:

  • 正则匹配法
  • 通配符匹配法
  • 前缀匹配法
  • nGram分词器法

正则匹配法

GET my_idx/my_type/_search
{
"query": {
"regexp": {
"name": "doe.*"
}
}
}

通配符匹配法

使用query_string配合通配符进行查询,需要注意的是,通配符查找可能使用大量内存且效率低下

后缀匹配(前导通配符)是非常重的操作(e.g. "*ing"),索引中所有的term都会被查找一遍,可以通过allow_leading_wildcard来关闭后缀匹配功能

GET my_idx/my_type/_search
{
"query": {
"query_string": {
"default_field": "name",
"query": "Doe*"
}
}
}

前缀匹配法

原答案说使用prefix,但是prefix并没有对查询进行分析,这里我们使用match_phrase_prefix

GET my_idx/my_type/_search
{
"query": {
"match_phrase_prefix": {
"name": {
"query": "Doe",
"max_expansions": 10
}
}
}
}

nGram分词器法

创建索引

PUT my_idx
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "my_tokenizer"
}
},
"tokenizer": {
"my_tokenizer": {
"type": "ngram",
"min_gram": 3,
"max_gram": 3,
"token_chars": [
"letter",
"digit"
]
}
}
}
}
}

测试一下分词器

POST my_idx/_analyze
{
"analyzer": "my_analyzer",
"text": "Doeman"
} // response {
"tokens": [
{
"token": "Doe",
"start_offset": 0,
"end_offset": 3,
"type": "word",
"position": 0
},
{
"token": "oem",
"start_offset": 1,
"end_offset": 4,
"type": "word",
"position": 1
},
{
"token": "ema",
"start_offset": 2,
"end_offset": 5,
"type": "word",
"position": 2
},
{
"token": "man",
"start_offset": 3,
"end_offset": 6,
"type": "word",
"position": 3
}
]
}

再查就可以查到了。而题主虽然使用了ngram,但是min_grammax_gram都配置为1

长度越小,匹配到的文档越多,但匹配的质量会越差

长度越大,检索到的文档越匹配。推荐使用长度为3的tri-gram官方文档对此有详细介绍

ElasticSearch - How to search for a part of a word with ElasticSearch的更多相关文章

  1. ElasticSearch报 EsThreadPoolExecutor[search, queue capacity = 1000, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@c0efba

    ElasticSearch报以下错误的解决办法: "type": "es_rejected_execution_exception", "reason ...

  2. Elasticsearch: 使用URI Search

    在Elasticsearch中,我们可以使用_search终端进行搜索.这个在我之前的文章 "开始使用Elasticsearch (2)" 中有很多的描述.针对这种搜索,我们可以使 ...

  3. ElasticSearch: SearchContextMissingException[No search context found for id [173690]]

    这个原因是scroll的时间设置不够久,设久一些就可以了. ----------------------------------- 原文:https://www.cnblogs.com/chenmz1 ...

  4. Elasticsearch通关教程(五):如何通过SQL查询Elasticsearch

    前言 这篇博文本来是想放在全系列的大概第五.六篇的时候再讲的,毕竟查询是在索引创建.索引文档数据生成和一些基本概念介绍完之后才需要的.当前面的一些知识概念全都讲解完之后再讲解查询是最好的,但是最近公司 ...

  5. 【docker Elasticsearch】Rest风格的分布式开源搜索和分析引擎Elasticsearch初体验

    概述: Elasticsearch 是一个分布式.可扩展.实时的搜索与数据分析引擎. 它能从项目一开始就赋予你的数据以搜索.分析和探索的能力,这是通常没有预料到的. 它存在还因为原始数据如果只是躺在磁 ...

  6. 学习用Node.js和Elasticsearch构建搜索引擎(3):使用curl命令操作elasticsearch

    使用Elasticsearch不免要提到curl工具,curl是利用URL语法在命令行方式下工作的开源文件传输工具.官网地址:https://curl.haxx.se/ 因为elasticsearch ...

  7. elasticsearch安装与使用(4)-- 安装中文分词插件elasticsearch 的 jdbc

    前言 elasticsearch(下面简称ES)使用jdbc连接mysql比go-mysql-elasticsearch的elasticsearch-river-jdbc能够很好的支持增量数据更新的问 ...

  8. elasticsearch配置文件里的一些坑 [Failed to load settings from [elasticsearch.yml]]

    这里整理几个空格引起的问题. 版本是elasticsearch-2.3.0 或者elasticsearch-rtf-master Exception in thread "main" ...

  9. 几篇关于MySQL数据同步到Elasticsearch的文章---第五篇:logstash-input-jdbc实现mysql 与elasticsearch实时同步深入详解

    文章转载自: https://blog.csdn.net/laoyang360/article/details/51747266 引言: elasticsearch 的出现使得我们的存储.检索数据更快 ...

随机推荐

  1. 骨骼动画的原理及在Unity中的使用

    制作骨骼动画 我们看看这几步操作后,我们得到了那些数据: 1.每个皮肤顶点的初始世界坐标. 2.每个骨骼关节顶点的初始世界坐标. 3.每个顶点被骨骼顶点的影响信息. 4.骨骼如何移动. 骨骼动画原理 ...

  2. C语言上机复习(一)文件操作

    C语言—文件操作 1.1 fgetc() + fputc(): 以 字符 形式存取数据定义文件指针 #define _CRT_SECURE_NO_WARNINGS #include <cstdi ...

  3. C# 实现Bresenham算法(vs2010)

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  4. Mac下MySQL与MySQLWorkbench的安装

    通过查阅各种各样的资料,去安装这些东东.最后经过一番周折终于安装完成.下面是对安装过程和遇到的问题做个简单记录. 一. 下载MySQL和MySQL Workbench http://dev.mysql ...

  5. Java EE之Hibernate的HibernateSessionFactory

    昨天,一下午都被一个bug缠身,最后逐层排查,发现是MyEclipse 2014自动生成的HibernateSessionFactory有问题.后观察网友提供的自动生成的HibernateSessio ...

  6. Python之Eclipse环境下安装与配置

    奔着对python的好奇,今天又是周末,欲小试Python.那么首先避不开的问题就是python的环境搭建.而我之前已经在学习Java的过程中安装了Eclipse,不想再安装更多的IDE了,就那Ecl ...

  7. luogu P2480 [SDOI2010]古代猪文

    M_sea:这道题你分析完后就是一堆板子 废话 理解完题意后,我们要求的东西是\(G^s(s=\sum_{d|n} \binom{n}{d})\) 但是这个指数\(s\)算出来非常大,,, 我们可以利 ...

  8. luogu P1486 [NOI2004]郁闷的出纳员

    一万年以后终于调过了这题 这道题主要是维护一个有序的集合(吧),所以使用平衡树(我这里用\(Splay\)) 记录一个变量\(ff\)(雾),表示所有工资的变化量 对于\(I\)操作,如果初始工资大于 ...

  9. R 的农场 chebnear (二分答案+最近平面点对)

    题面 \(solution:\) 这道题想到二分答案应该是不难的,因为题目是求平均工资的最小值,这个显然具备单调性: 我们设平均工资的最小值为ans,如果我们现在的平均工资x小于ans那么将x带入题目 ...

  10. Django学习手册 - 正则URL路由配置/路由分发

    ############################################### 总结: 一.url路由配置: 方式一:(通过url链接get获取) 方式二:(url路由匹配方式获取-拓 ...