如何使用ElasticSearch搜索单词的一部分


128

我最近开始使用ElasticSearch,但似乎无法让它搜索单词的一部分。

示例:我有我的ouchdb中的三个文档,在ElasticSearch中建立了索引:

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

所以现在,我要搜索包含“ Doe”的所有文档

curl http://localhost:9200/my_idx/my_type/_search?q=Doe

那不会返回任何点击。但是如果我寻找

curl http://localhost:9200/my_idx/my_type/_search?q=Doeman

它确实返回一个文档(John Doeman)。

我尝试将不同的分析器和不同的过滤器设置为索引的属性。我也尝试使用完整的查询(例如:

{
  "query": {
    "term": {
      "name": "Doe"
    }
  }
}

),但似乎没有任何效果。

当我搜索“ Doe”时,如何使ElasticSearch同时找到John Doeman和Jane Doewoman?

更新

我尝试使用nGram标记生成器和过滤器,就像Igor建议的那样:

{
  "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
        }
      }
    }
  }
}

我现在遇到的问题是每个查询都返回所有文档。有指针吗?使用nGram的ElasticSearch文档不是很好...


9
也难怪,你HABE最小/最大NGRAM设置为1,那么1个字母:)
马丁B.

Answers:


85

我也在使用nGram。我将标准令牌生成器和nGram用作过滤器。这是我的设置:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "analysis": {
      "index_analyzer": {
        "my_index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "mynGram"
          ]
        }
      },
      "search_analyzer": {
        "my_search_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "mynGram"
          ]
        }
      },
      "filter": {
        "mynGram": {
          "type": "nGram",
          "min_gram": 2,
          "max_gram": 50
        }
      }
    }
  }
}

让我们找到最多50个字母的单词部分。根据需要调整max_gram。用德语来说可以变得很大,所以我将其设置为很高的值。



是从索引设置中获取的信息,还是发布到elasticsearch进行配置的信息?
Tomas Jansson 2014年

这是配置Elasticsearch的POST。
roka,2014年

我不确定Elasticsearch的当前版本,但应该在文档中提及它:elastic.co/guide/en/elasticsearch/reference/current/index.html
roka

1
@JimC我已经有7年没有使用过ElasticSearch了,所以我不知道该项目的当前变更。
roka

63

在大索引上,使用前导和尾随通配符进行搜索将非常慢。如果您希望能够按单词前缀搜索,请删除前导通配符。如果确实需要在一个单词的中间找到一个子字符串,则最好使用ngram tokenizer。


14
伊戈尔是对的。至少删除前导*。对于NGram ElasticSearch示例,请参见以下要点:gist.github.com/988923
karmi 2011年

3
@karmi:感谢您提供完整的示例!也许您想添加您的评论作为实际答案,这就是它对我有用的原因,也是我想要提出的建议。
Fabian Steeg 2012年

54

我认为没有必要更改任何映射。尝试使用query_string,这是完美的。所有方案都可以使用默认的标准分析仪:

我们有数据:

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

方案1:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Doe*"}
} }

响应:

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

方案2:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Jan*"}
} }

响应:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}

方案3:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*oh* *oe*"}
} }

响应:

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

编辑-与弹簧数据弹性搜索相同的实现 https://stackoverflow.com/a/43579948/2357869

另一个解释是query_string如何比其他参数更好 https://stackoverflow.com/a/43321606/2357869


3
我认为这是最简单的
Esgi Dendyanri

是的。我已经在我的项目中实现了。
Opster Elasticsearch Pro-Vijay

如何包含多个字段以进行搜索?
Shubham A.

试试这个:-{“ query”:{“ query_string”:{“ fields”:[“ content”,“ name”],“ query”:“ this AND that”}}}
Opster Elasticsearch Pro-Vijay



6

尝试使用以下描述的解决方案:ElasticSearch中的确切子字符串搜索

{
    "mappings": {
        "my_type": {
            "index_analyzer":"index_ngram",
            "search_analyzer":"search_ngram"
        }
    },
    "settings": {
        "analysis": {
            "filter": {
                "ngram_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                }
            },
            "analyzer": {
                "index_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [ "ngram_filter", "lowercase" ]
                },
                "search_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": "lowercase"
                }
            }
        }
    }
}

为了解决磁盘使用问题和搜索词太长的问题,使用了短的8个字符长的ngram(配置为:“ max_gram”:8)。要搜索包含8个以上字符的字词,请将搜索结果转换为布尔AND查询,以查找该字符串中每个不同的8个字符的子字符串。例如,如果用户搜索了大码(10个字符的字符串),则搜索将是:

”“ arge ya and arge yar and rge yard


2
死链接,请修复
DarkMukke

我一直在寻找类似的东西。谢谢!你知道如何与记忆量表min_grammax_gram它看起来像这将是线性相关的字段值的大小和范围minmax。使用这样的东西有多不满意?
格伦·汤普森

另外,是否有任何原因ngram是标记器上的过滤器?您能否不仅将其用作令牌生成器,然后应用小写过滤器... index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }我尝试了一下,但使用分析器测试API似乎能提供相同的结果
Glen Thompson

2

如果要实现自动完成功能,则完成建议程序是最简洁的解决方案。下一篇博客文章非常清楚地描述了它是如何工作的。

简而言之,它是一种称为FST的内存数据结构,其中包含有效建议,并针对快速检索和内存使用进行了优化。本质上,它只是一个图。例如,和FST包含单词hotelmarriotmercuremunchenmunich是这样的:

在此处输入图片说明


2

您可以使用regexp。

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

如果使用此查询:

{
  "query": {
    "regexp": {
      "name": "J.*"
    }
  }
}

您将获得所有名称以“ J”开头的数据。考虑到您只想接收前两个以“ man”结尾的记录,因此可以使用此查询:

{
  "query": { 
    "regexp": {
      "name": ".*man"
    }
  }
}

并且如果您想接收名称中存在“ m”的所有记录,则可以使用以下查询:

{
  "query": { 
    "regexp": {
      "name": ".*m.*"
    }
  }
}

这对我有用。我希望我的回答适合解决您的问题。


1

使用通配符(*)可防止计算分数


1
您能否在答案中添加更多详细信息?提供示例代码或有关此功能的文档参考。
Cray

0

我正在使用它并得到了我的工作

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }

-6

没关系。

我不得不看一下Lucene文档。似乎我可以使用通配符!:-)

curl http://localhost:9200/my_idx/my_type/_search?q=*Doe*

绝招!


11
参见@imotov答案。通配符的使用根本无法很好地扩展。
Mike Munroe 2012年

5
@Idx-了解您自己的答案是如何被否决的。下票代表答案的质量和相关性。您能抽出一点时间接受正确的答案吗?至少新用户会感谢您。
asyncwait 2013年

3
足够的投票权。OP明确了最佳答案。+1,以便在有人发布更好的答案之前分享似乎是最好的答案。
丹尼尔(Daniel)2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.