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

时间:2017-08-09 09:53:22

标签: elasticsearch spring-data spring-data-elasticsearch

我正在尝试在下面的文字列表中搜索<​​strong> blue 这个词

&#34; BlueSaphire&#34;,&#34; Bluo&#34;,&#34; alue&#34;,&#34; blue&#34;,&#34; BLUE&#34;, &#34; Blue&#34;,&#34; Blue Black&#34;,&#34; Bluo&#34;,&#34; Saphire Blue&#34;, &#34;黑色&#34; ,&#34;绿色&#34;,&#34; bloo&#34; ,&#34; Saphireblue&#34;

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

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

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

但我无法制作外卡。 &#34; SaphireBlue&#34;和&#34; BlueSaphire&#34;预计也将成为结果的一部分

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

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

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

QueryBuilder queryBuilder = boolQuery().should(
                queryString("blue").analyzeWildcard(true)
                        .field("colorDescriptionCode", 2.0f);

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

让我知道如何指定外卡,以便在搜索结果中也会返回包含 blue 的所有字词

2 个答案:

答案 0 :(得分:0)

我无法在一个查询中实现模糊和威尔卡搜索。

这是我能得到的最接近的解决方案。我不得不触发两个不同的查询并手动合并结果。

    @Query("{\"wildcard\" : {\"colorDescriptionCode\" : \"?0\" }}")
    Page<ColorDescription> findByWildCard(String colorDescriptionCode, Pageable pageable);

    @Query("{\"match\": { \"colorDescriptionCode\": { \"query\":     \"?0\", \"fuzziness\": 1 }}}")
    Page<ColorDescription> findByFuzzy(String colorDescriptionCode, Pageable pageable);

答案 1 :(得分:0)

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

  1. 字符过滤;
  2. 标记化
  3. 令牌过滤
  4. 在第一阶段,我们可以丢弃不需要的字符,例如HTML标记。有关字符过滤器的更多信息,您可以在official site上找到。 下一阶段更有趣。在这里,我们将输入文本拆分为标记,稍后将用于搜索。一些非常有用的tokenizers

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

    下一阶段也很有意思。在我们将文本拆分为令牌之后,我们可以用这个做很多有趣的事情。我再次举几个非常有用的令牌过滤器示例:

    • 小写过滤器。在大多数情况下,我们希望获得不区分大小写的搜索,因此将令牌设置为小写是一种很好的做法。
    • 词干分析器。当我们与自然语言达成协议时,我们遇到了很多问题。其中一个问题是一个词可以有多种形式。 Stemmer过滤器帮助我们获得单词的根形式。
    • 模糊过滤器。另一个问题是用户经常犯错字。此过滤器会添加包含可能的拼写错误的令牌。

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

    curl [ELASTIC_URL]:9200/[INDEX_NAME]/[TYPE_NAME]/[DOCUMENT_ID]/_termvectors?pretty
    

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

    1. 请求是否会经历分析阶段;
    2. 请求是否需要确切答案(是或否)
    3. 示例是匹配查询和术语查询。第一个将通过分析阶段,第二个不会。第一个不会给我们一个具体的答案(但给我们一个分数),第二个会给我们一个答案。在为文档创建映射时,我们可以为每个字段分别指定分析器的索引和搜索分析器。

      现在有关弹簧数据弹性搜索的信息。在这里谈谈具体的例子是有意义的。假设我们有一个带有标题字段的文档,我们想要搜索有关该字段的信息。首先,创建一个包含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"
         }
       }    
      }
      

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

      @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
      
      }
      

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

      • title.edge_ngram使用关键字搜索分析器搜索边缘n-gram。我们需要这个,因为我们不需要将查询拆分为edge 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 });
      

      搜索是一个非常有趣和丰富的主题。我试图尽可能简短地回答,因为这可能会有令人困惑的时刻 - 请不要犹豫。