Elasticsearch:通过结合 Elasticsearch 词干分析器和同义词来提高搜索相关性

Elastic 专栏收录该内容
494 篇文章 87 订阅

在之前的博客中,我们介绍了如何将同义词合并到由 Elasticsearch 驱动的应用程序中。 在这里,我以该博客为基础,展示了如何结合词干分析器(stemmer)和多词同义词(multi-word synonyms),将搜索结果的质量提升到一个新的水平。

动机

想象一下,你正在使用 Elasticsearch 为搜索应用程序提供搜索引擎以查找书籍,并且在此应用程序中你希望将以下单词视为同义词:

  • brainstorm
  • brainstorming
  • brainstormed
  • brain storm
  • brain storming
  • brain stormed
  • envisage
  • envisaging
  • envisaged
  • 等等

明确地使用同义词来定义一个词或复合词的所有可能的变形、变格和屈折变化是乏味和容易出错的。

但是,可以通过在应用同义词之前使用词干分析器提取每个单词的词干来减少同义词列表的大小。 这将允许我们通过仅指定以下同义词来获得与上述同义词列表相同的结果:

  • brainstorm
  • brain storm
  • envisage

定制分词器

在本节中,我将展示定义可用于匹配同义词的定制分词器的代码片段。 稍后在本博客中,我将展示如何将分析器提交给 Elasticsearch。

我们之前的博客详细介绍了索引时(index-time)和搜索时(search-time)同义词之间的区别。 在此处提供的解决方案中,我使用了搜索时同义词。

我们将创建一个同义词图标记过滤器,使用 “brainstorm” 及 “envisage” 匹配多词同义词 “brain storm”。 这也会将 “mind” 和 “brain” 视为同义词。 我们将把这个标记过滤器称为 my_graph_synonyms,它看起来如下:

"filter": { 
  "my_graph_synonyms": { 
    "type": "synonym_graph", 
    "synonyms": [ 
      "mind, brain", 
      "brain storm, brainstorm, envisage" 
    ] 
  } 
}

接下来我们需要定义两个单独的定制分词器,一个将在索引时应用于文本,另一个将在搜索时应用于文本。

我们定义了一个名为 my_index_time_analyzer 的分词器,它使用标准分词器小写分词过滤器词干分词过滤器,如下所示:

"my_index_time_analyzer": { 
  "tokenizer": "standard", 
  "filter": [ 
    "lowercase", 
    "stemmer" 
  ] 
}

我们定义了一个名为 my_search_time_analyzer 的分词器,它也使用了标准分词器、小写分词过滤器和词干分词过滤器(如上)。 但是,这也包括我们称为 my_graph_synonyms 的自定义标记过滤器,可确保在搜索时匹配同义词:

"my_search_time_analyzer": { 
  "tokenizer": "standard", 
  "filter": [ 
    "lowercase", 
    "stemmer", 
    "my_graph_synonyms" 
  ]
}

Mapping

Mapping 是定义文档及其包含的字段如何存储和索引的过程。 每个文档都是字段的集合,每个字段都有自己的数据类型。 在此示例中,我们使用名为 my_new_text_field 的单个字段定义文档的mapping,我们将其定义为文本。 当文档被索引时,这个字段将使用 my_index_time_analyzer,当文档被搜索时将使用 my_search_time_analyzer。 映射如下所示:

"mappings": { 
  "properties": { 
    "my_new_text_field": { 
      "type": "text", 
      "analyzer": "my_index_time_analyzer", 
      "search_analyzer": "my_search_time_analyzer" 
    } 
  } 
}

把它放在一起

下面我们结合我们的自定义分析器和映射并将其应用到名为 test_index 的索引,如下所示:

PUT /test_index 
{ 
  "settings": { 
    "index": { 
      "analysis": { 
        "filter": { 
          "my_graph_synonyms": { 
            "type": "synonym_graph", 
            "synonyms": [ 
              "mind, brain", 
              "brain storm, brainstorm, envisage" 
            ] 
          } 
        }, 
        "analyzer": { 
          "my_index_time_analyzer": { 
            "tokenizer": "standard", 
            "filter": [ 
              "lowercase", 
              "stemmer" 
            ] 
          }, 
          "my_search_time_analyzer": { 
            "tokenizer": "standard", 
            "filter": [ 
              "lowercase", 
              "stemmer", 
              "my_graph_synonyms" 
            ] 
          } 
        } 
      } 
    } 
  }, 
  "mappings": { 
    "properties": { 
      "my_new_text_field": { 
        "type": "text", 
        "analyzer": "my_index_time_analyzer", 
        "search_analyzer": "my_search_time_analyzer" 
      } 
    } 
  } 
}

测试我们的自定义搜索时间分词器

如果我们希望查看分析器如何标记和规范化给定字符串,我们可以直接调用 _analyze API,如下所示:

POST test_index/_analyze 
{ 
  "text" : "Brainstorm", 
  "analyzer": "my_search_time_analyzer" 
}

上面的命令返回的结果是:

{
  "tokens" : [
    {
      "token" : "brain",
      "start_offset" : 0,
      "end_offset" : 10,
      "type" : "SYNONYM",
      "position" : 0
    },
    {
      "token" : "envisag",
      "start_offset" : 0,
      "end_offset" : 10,
      "type" : "SYNONYM",
      "position" : 0,
      "positionLength" : 2
    },
    {
      "token" : "brainstorm",
      "start_offset" : 0,
      "end_offset" : 10,
      "type" : "<ALPHANUM>",
      "position" : 0,
      "positionLength" : 2
    },
    {
      "token" : "storm",
      "start_offset" : 0,
      "end_offset" : 10,
      "type" : "SYNONYM",
      "position" : 1
    }
  ]
}

在真实文档上测试

我们可以使用 _bulk API 将多个文档驱动到 Elasticsearch 中,如下所示:

POST test_index/_bulk 
{ "index" : { "_id" : "1" } } 
{"my_new_text_field": "This is a brainstorm" } 
{ "index" : { "_id" : "2" } } 
{"my_new_text_field": "A different brain storm" } 
{ "index" : { "_id" : "3" } } 
{"my_new_text_field": "About brainstorming" } 
{ "index" : { "_id" : "4" } } 
{"my_new_text_field": "I had a storm in my brain" } 
{ "index" : { "_id" : "5" } } 
{"my_new_text_field": "I envisaged something like that" }

将上述示例文档写入到 test_index 后,我们可以执行搜索,正确响应文档 #1、#2、#3 和 #5,如下所示:

GET test_index/_search 
{ 
  "query": { 
    "match": { 
      "my_new_text_field": "brain storm" 
    } 
  } 
}

我们可以执行以下搜索,它仅正确返回文档 #2 和 #4,如下所示: 

GET test_index/_search 
{ 
  "query": { 
    "match": { 
      "my_new_text_field": "brain" 
    } 
  } 
}

我们可以执行以下搜索,它将正确响应文档 #1、#2、#3 和 #5,如下所示:

GET test_index/_search 
{ 
  "query": { 
    "match": { 
      "my_new_text_field": "brainstorming" 
    } 
  } 
}

我们可以执行以下搜索,正确返回文档 #2 和 #4,如下所示:

GET test_index/_search 
{ 
  "query": { 
    "match": { 
      "my_new_text_field": "mind storm" 
    } 
  } 
}

最后,我们可以执行以下搜索,它仅正确返回文档 #2 和 #4,如下所示:

GET test_index/_search 
{ 
  "query": { 
    "match": { 
      "my_new_text_field": { 
        "query": "storm brain" 
      } 
    } 
  } 
}

结论

在这篇博客中,我展示了如何在 Elasticsearch 中结合词干分析器和多词同义词来提高搜索结果的质量。

如果你还没有自己的 Elasticsearch 集群,您可以在几分钟内启动 Elastic Cloud 的免费试用版,或者下载 Elastic Stack 并在本地运行。 如果你更喜欢带有拖放增强和控件的预调整搜索体验,请查看 Elastic App Search。 借助 App Search,你可以轻松实现高度相关的搜索体验,并且它具有用于调整、管理和分析的直观界面。

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值