150

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

示例:我在 ElasticSearch 中索引了来自我的 couchdb 的三个文档:

{
  "_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 文档不是很好……

4

11 回答 11

90

我也在使用 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。德语单词可以变得非常大,所以我将其设置为高值。

于 2011-09-20T09:47:54.130 回答
68

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

于 2011-06-24T17:23:40.970 回答
68

我认为没有必要更改任何映射。尝试使用query_string,它是完美的。所有场景都将使用默认标准分析器:

我们有数据:

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

场景一:

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

回复:

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

场景二:

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

于 2017-04-07T13:00:01.293 回答
15

在不更改索引映射的情况下,您可以执行一个简单的前缀查询,该查询将像您希望的那样进行部分搜索

IE。

{
  "query": { 
    "prefix" : { "name" : "Doe" }
  }
}

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html

于 2016-06-09T16:14:21.907 回答
7

尝试这里描述的解决方案:Exact Substring Searches in 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 个字符子字符串。例如,如果用户搜索large yard(10 个字符的字符串),则搜索结果为:

“arge ya AND arge yar AND rge yard .

于 2015-05-06T13:09:00.490 回答
4

虽然有很多答案专注于解决手头的问题,但并没有过多地谈论人们在选择特定答案之前需要做出的各种权衡。因此,让我尝试在这个观点上添加更多细节。

部分搜索现在是一个非常普遍和重要的功能,如果没有正确实现会导致糟糕的用户体验和糟糕的性能,所以首先要了解你的应用程序功能和与此功能相关的非功能需求,我在我的这篇详细中谈到所以回答

现在有各种方法,例如查询时间、索引时间、完成建议和搜索,因为您键入在最新版本的 elasticsarch 中添加的数据类型。

现在,想要快速实施解决方案的人可以使用以下端到端工作解决方案。

索引映射

{
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter": {
          "type": "ngram",
          "min_gram": 1,
          "max_gram": 10
        }
      },
      "analyzer": {
        "autocomplete": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      }
    },
    "index.max_ngram_diff" : 10
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "autocomplete", 
        "search_analyzer": "standard" 
      }
    }
  }
}

给定示例文档的索引

{
  "title" : "John Doeman"
  
}

{
  "title" : "Jane Doewoman"
  
}

{
  "title" : "Jimmy Jackal"
  
}

和搜索查询

{
    "query": {
        "match": {
            "title": "Doe"
        }
    }
}

返回预期的搜索结果

 "hits": [
            {
                "_index": "6467067",
                "_type": "_doc",
                "_id": "1",
                "_score": 0.76718915,
                "_source": {
                    "title": "John Doeman"
                }
            },
            {
                "_index": "6467067",
                "_type": "_doc",
                "_id": "2",
                "_score": 0.76718915,
                "_source": {
                    "title": "Jane Doewoman"
                }
            }
        ]
于 2020-12-16T07:22:51.837 回答
2

如果您想实现自动完成功能,那么Completion Suggester是最简洁的解决方案。下一篇博客文章非常清楚地描述了它是如何工作的。

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

在此处输入图像描述

于 2018-03-03T15:57:19.650 回答
2

你可以使用正则表达式。

{ "_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.*"
    }
  }
}

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

于 2018-08-14T13:52:38.193 回答
1

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

于 2019-07-01T15:48:31.373 回答
1

我正在使用它并开始工作

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }
于 2020-04-16T08:04:23.783 回答
-6

没关系。

我不得不查看 Lucene 文档。看来我可以使用通配符!:-)

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

成功了!

于 2011-06-24T11:56:20.523 回答