Spring数据弹性搜索通配符搜索

liv*_*ves 4 elasticsearch spring-data spring-data-elasticsearch

我正在尝试在下面的文本列表中搜索单词blue

"BlueSaphire","Bluo","alue","blue","BLUE","Blue","Blue Black","Bluo","Saphire Blue", "black", "green","bloo" , “宝蓝”

SearchQuery searchQuery = new NativeSearchQueryBuilder().withIndices("color")
                  .withQuery(matchQuery("colorDescriptionCode", "blue")
                  .fuzziness(Fuzziness.ONE)
                  )
                  .build();
Run Code Online (Sandbox Code Playgroud)

这工作正常,搜索结果返回以下记录以及分数

alue    2.8718023
Bluo    1.7804208
Bluo    1.7804208
BLUE    1.2270637
blue    1.2270637
Blue    1.2270637
Blue Black    1.1082436
Saphire Blue    0.7669148
Run Code Online (Sandbox Code Playgroud)

但我无法使通配符工作。“SaphireBlue”和“BlueSaphire”也有望成为结果的一部分

我尝试了以下设置,但它不起作用。

SearchQuery searchQuery = new NativeSearchQueryBuilder().withIndices("color")
                      .withQuery(matchQuery("colorDescriptionCode", "(.*?)blue")
                      .fuzziness(Fuzziness.ONE)
                      )
                      .build();
Run Code Online (Sandbox Code Playgroud)

在堆栈溢出中,我观察到指定分析通配符的解决方案。

QueryBuilder queryBuilder = boolQuery().should(
                queryString("blue").analyzeWildcard(true)
                        .field("colorDescriptionCode", 2.0f);
Run Code Online (Sandbox Code Playgroud)

我没有找到 queryString 静态方法。我正在使用 spring-data-elasticsearch 2.0.0.RELEASE 。

让我知道如何指定通配符,以便所有包含blue 的单词也将在搜索结果中返回

Nik*_*mov 7

我知道工作实例总是比理论好,但我还是想先讲一点理论。Elasticsearch 的核心是 Lucene。因此,在将文档写入 Lucene 索引之前,他会经历分析阶段。分析阶段可以分为3个部分:

  1. 字符过滤
  2. 标记化
  3. 令牌过滤

在第一阶段,我们可以丢弃不需要的字符,例如 HTML 标签。有关字符过滤器的更多信息,您可以在官方网站上找到。下一阶段要有趣得多。在这里,我们将输入文本拆分为标记,稍后将用于搜索。一些非常有用的标记器

  • 标准分词器。默认情况下使用它。分词器实现了 Unicode 文本分割算法。在实践中,您可以使用它来将文本拆分为单词并将这些单词用作标记。
  • n-gram 分词器。如果您想按单词的一部分进行搜索,这就是您所需要的。此分词器将文本拆分为连续的 n 个项目序列。例如文本“例如”将"fo", "or", "r ", " e", "ex", "for", "or ex"被拆分为这个标记序列等。n-gram 的长度是可变的,可以通过 min_gram 和 max_gram 参数进行配置。
  • 边缘 n-gram 分词器。除了一件事之外,与 n-gram 分词器的工作方式相同 - 此分词器不会增加偏移量。例如,文本“例如”将被拆分为这个标记序列"fo", "for", "for ", "for e", "for ex", "for exa"等。有关标记器的更多信息,您可以在官方网站上找到。不幸的是,由于声誉低,我无法发布更多链接。

下一阶段也非常有趣。在我们将文本拆分为标记之后,我们可以用它做很多有趣的事情。我再次给出了一些非常有用的令牌过滤器示例:

  • 小写过滤器。在大多数情况下,我们希望进行不区分大小写的搜索,因此将标记变为小写是一种很好的做法。
  • 词干过滤器。当我们处理自然语言时,我们会遇到很多问题。问题之一是一个词可以有多种形式。Stemmer 过滤器帮助我们获得单词的词根形式。
  • 模糊过滤器。另一个问题是用户经常打错字。此过滤器添加包含可能的错别字的标记。

如果您有兴趣查看分析器的结果,可以使用此 _termvectors 端点

curl [ELASTIC_URL]:9200/[INDEX_NAME]/[TYPE_NAME]/[DOCUMENT_ID]/_termvectors?pretty
Run Code Online (Sandbox Code Playgroud)

现在谈谈查询。查询分为 2 个大组。这些组有两个显着差异:

  1. 请求是否经过分析阶段;
  2. 请求是否需要确切答案(是或否)

示例是匹配查询和术语查询。第一个会通过分析阶段,第二个不会。第一个不会给我们一个具体的答案(但会给我们一个分数),第二个会。在为文档创建映射时,我们可以为每个字段分别指定分析器和搜索分析器的索引。

现在有关弹簧数据弹性搜索的信息。在这里讨论具体的例子是有意义的。假设我们有一个带有标题字段的文档,并且我们想要搜索有关此字段的信息。首先,创建一个包含 elasticsearch 设置的文件。

{
 "analysis": {
    "analyzer": {
        "ngram_analyzer": {
            "tokenizer": "ngram_tokenizer",
            "filter": [
                "lowercase"
            ]
        },
        "edge_ngram_analyzer": {
            "tokenizer": "edge_ngram_tokenizer",
            "filter": [
                "lowercase"
            ]
        },
        "english_analyzer": {
            "tokenizer": "standard",
            "filter": [
                "lowercase",
                "english_stop",
                "unique",
                "english_possessive_stemmer",
                "english_stemmer"
            ]
        "keyword_analyzer": {
            "tokenizer": "keyword",
            "filter": ["lowercase"]
        }

   },
   "tokenizer": {
       "ngram_tokenizer": {
           "type": "ngram",
           "min_gram": 2,
           "max_gram": 20
       },
       "edge_ngram_tokenizer": {
           "type": "edge_ngram",
           "min_gram": 2,
           "max_gram": 20
       }
   },
   "filter": {
       "english_stop": {
           "type": "stop",
           "stopwords": "_english_"
       },
   "english_stemmer": {
       "type": "stemmer",
       "language": "english"
   },
   "english_possessive_stemmer": {
       "type": "stemmer",
       "language": "possessive_english"
   }
 }    
}
Run Code Online (Sandbox Code Playgroud)

您可以将此设置保存到您的资源文件夹。现在让我们看看我们的文档类

@Document(indexName = "document", type = "document")
@Setting(settingPath = "document_index_setting.json")
public class Document {

    @Id
    private String id;

    @MultiField(
        mainField = @Field(type = FieldType.String, 
                           index = not_analyzed),
        otherFields = {
                @InnerField(suffix = "edge_ngram",
                        type = FieldType.String,
                        indexAnalyzer = "edge_ngram_analyzer",
                        searchAnalyzer = "keyword_analyzer"),
                @InnerField(suffix = "ngram",
                        type = FieldType.String,
                        indexAnalyzer = "ngram_analyzer"),
                        searchAnalyzer = "keyword_analyzer"),
                @InnerField(suffix = "english",
                        type = FieldType.String,
                        indexAnalyzer = "english_analyzer")
        }
    )
    private String title;

    // getters and setters omitted

}
Run Code Online (Sandbox Code Playgroud)

所以这里有三个内部字段的字段标题:

  • title.edge_ngram用于使用关键字搜索分析器按边缘 n-gram 进行搜索。我们需要这样做是因为我们不需要将查询拆分为边 n-gram;
  • title.ngram 用于按 n-gram 搜索;
  • title.english用于搜索自然语言和主要字段标题的细微差别。我们不分析这个,因为有时我们想按这个字段排序。让我们使用简单的多匹配查询来搜索所有这些字段:
String searchQuery = "blablabla";
MultiMatchQueryBuilder queryBuilder = multiMatchQuery(searchQuery)
    .field("title.edge_ngram", 2)
    .field("title.ngram")
    .field("title.english");
NativeSearchQueryBuilder searchBuilder = new NativeSearchQueryBuilder()
    .withIndices("document")
    .withTypes("document")
    .withQuery(queryBuilder)
    .withPageable(new PageRequest(page, pageSize));
elasticsearchTemplate.queryForPage(searchBuilder.build, 
                                   Document.class, 
                                   new SearchResultMapper() {
                                   //realisation omitted });
Run Code Online (Sandbox Code Playgroud)

搜索是一个非常有趣和庞大的话题。我试图尽可能简短地回答,因此可能会出现令人困惑的时刻 - 不要犹豫,问。