【问题标题】:Spring data elastic search wild card searchSpring数据elasticsearch通配符搜索
【发布时间】:2018-01-17 03:26:42
【问题描述】:

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

"BlueSaphire","Bluo","alue","blue","BLUE", “蓝色”、“蓝色黑色”、“蓝色”、“蓝宝石蓝色”、 “黑”、“绿”、“血”、“宝蓝”

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

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

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

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 的所有单词

【问题讨论】:

  • 您的映射和集群设置也可以提供帮助,而不仅仅是正则表达式
  • 您能否详细说明您的评论?您的意思是在弹性搜索中配置同义词吗?
  • 我不知道,就在那个时候,你不能再用这种冒犯的方式问我,直到我得到我非常体面的问题,或者可能带着疯狂的想法我可能随着时间的推移学到了。谢谢

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


【解决方案1】:

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

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

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

  • 标准分词器。默认情况下使用它。标记器实现了 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" 等。 您可以在官方网站上找到有关标记器的更多信息。很遗憾,由于声誉低,我无法发布更多链接。

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

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

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

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

现在谈谈查询。查询分为 2 个大组。这些组有 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"
   }
 }    
}

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

@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 进行搜索。我们需要这个,因为我们不需要将查询拆分为边 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 });

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

【讨论】:

    【解决方案2】:

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

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

        @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);
    

    【讨论】:

      猜你喜欢
      • 2015-03-15
      • 2018-12-06
      • 1970-01-01
      • 1970-01-01
      • 2014-06-05
      • 1970-01-01
      • 1970-01-01
      • 2018-08-08
      • 1970-01-01
      相关资源
      最近更新 更多