一、简介

上一篇文章中,我们演示了如何在项目中配置和使用 Spring Data Elasticsearch。在本文中,我们将研究 Elasticsearch 提供的几种查询类型,还将讨论字段分析器及其对搜索结果的影响。

2. 分析仪

默认情况下,所有存储的字符串字段均由分析器处理。分析器由一个分词器和多个分词过滤器组成,并且前面通常有一个或多个字符过滤器。

默认分析器通过常见的单词分隔符(例如空格或标点符号)分割字符串,并将每个标记转换为小写。它还忽略常见的英语单词。

Elasticsearch 还可以配置为同时将某个字段视为已分析和未分析。

例如,在 Article 类中,假设我们将标题字段存储为标准分析字段。带有 后缀 的相同字段将被存储为未分析字段:

@MultiField(
  mainField = @Field(type = Text, fielddata = true),
  otherFields = {
      @InnerField(suffix = "verbatim", type = Keyword)
  }
)
private String title;

在这里,我们应用 @MultiField 注解来告诉 Spring Data 我们希望以多种方式对该字段进行索引。主字段将使用名称 标题 ,并将根据上述规则进行分析。

但我们还提供了第二个注释 @InnerField ,它描述了 标题 字段的附加索引。我们使用 FieldType.keyword 来指示我们在执行字段的附加索引时不想使用分析器,并且应使用带有后缀 verbatim 的 嵌套字段来存储该值。

2.1.分析字段

让我们看一个例子。假设一篇标题为“Spring Data Elasticsearch”的文章被添加到我们的索引中。默认分析器将在空格字符处分解字符串并生成小写标记:“ spring ”、“ data” 和“ elasticsearch ”。

现在我们可以使用这些术语的任意组合来匹配文档:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "elasticsearch data"))
  .build();

2.2.非分析字段

未分析的字段未标记化,因此在使用匹配或术语查询时只能作为整体进行匹配:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch"))
  .build();

使用匹配查询,我们只能按完整标题进行搜索,该标题也区分大小写。

3. 匹配查询

匹配查询 接受文本、数字和日期。

“匹配”查询分为三种类型:

  • 布尔值
  • 短语
  • 短语前缀

在本节中,我们将探讨 布尔 匹配查询。

3.1.与布尔运算符匹配

boolean 是匹配查询的默认类型;您可以指定要使用哪个布尔运算符( 或是 默认值):

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title","Search engines").operator(Operator.AND))
  .build();
SearchHits<Article> articles = elasticsearchTemplate()
  .search(searchQuery, Article.class, IndexCoordinates.of("blog"));

此查询将通过使用 and 运算符指定标题中的两个术语,返回标题为“搜索引擎”的文章。但是,当只有一个术语匹配时,如果我们使用默认的 ( ) 运算符进行搜索,会发生什么情况呢?

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "Engines Solutions"))
  .build();
SearchHits<Article> articles = elasticsearchTemplate()
  .search(searchQuery, Article.class, IndexCoordinates.of("blog"));
assertEquals(1, articles.getTotalHits());
assertEquals("Search engines", articles.getSearchHit(0).getContent().getTitle());

搜索引擎 ”文章仍然匹配,但分数会较低,因为并非所有术语都匹配。

每个匹配术语的分数总和加起来就是每个结果文档的总分。

在某些情况下,包含在查询中输入的罕见术语的文档可能比包含多个常见术语的文档具有更高的排名。

3.2.模糊性

当用户在单词中输入错误时,仍然可以通过指定 模糊 参数来将其与搜索相匹配,这允许不精确匹配。

对于字符串字段, 模糊度 意味着编辑距离:需要对一个字符串进行单字符更改以使其与另一个字符串相同的次数。

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "spring date elasticsearch")
  .operator(Operator.AND)
  .fuzziness(Fuzziness.ONE)
  .prefixLength(3))
  .build();

prefix_length 参数用于提高性能。在这种情况下,我们要求前三个字符应该完全匹配,这减少了可能的组合数量。

5. 短语搜索

相位搜索更严格,尽管您可以使用 slop 参数控制它。此参数告诉短语查询在仍考虑文档匹配的情况下允许术语相距多远。

换句话说,它表示您需要移动术语以使查询和文档匹配的次数:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1))
  .build();

这里的查询将匹配标题为“ Spring Data Elasticsearch ”的文档,因为我们将斜率设置为 1。

6. 多匹配查询

当您想在多个字段中搜索时,您可以使用 QueryBuilders#multiMatchQuery() 指定所有要匹配的字段:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(multiMatchQuery("tutorial")
    .field("title")
    .field("tags")
    .type(MultiMatchQueryBuilder.Type.BEST_FIELDS))
  .build();

在这里,我们在 标题标签 字段中搜索匹配项。

请注意,这里我们使用“最佳字段”评分策略。它将取各个字段中的最大分数作为文档分数。

7. 聚合

在我们的 Article 类中,我们还定义了一个 标签 字段,该字段是非分析的。我们可以通过使用聚合轻松创建标签云。

请记住,由于该字段未经分析,因此标签不会被标记:

TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags")
  .field("tags")
  .order(Terms.Order.count(false));
SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation);

SearchRequest searchRequest = 
  new SearchRequest().indices("blog").types("article").source(builder);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

Map<String, Aggregation> results = response.getAggregations().asMap();
StringTerms topTags = (StringTerms) results.get("top_tags");

List<String> keys = topTags.getBuckets()
  .stream()
  .map(b -> b.getKeyAsString())
  .collect(toList());
assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys);

八、总结

在本文中,我们讨论了分析字段和非分析字段之间的区别,以及这种区别如何影响搜索。

我们还了解了Elasticsearch提供的几种查询类型,例如匹配查询、短语匹配查询、全文搜索查询和布尔查询。

Elasticsearch 提供了许多其他类型的查询,例如地理查询、脚本查询和复合查询。您可以在Elasticsearch 文档中阅读有关它们的信息,并探索 Spring Data Elasticsearch API,以便在代码中使用这些查询。

您可以在GitHub 存储库中找到包含本文中使用的示例的项目。