【Elastic Engineering】Elasticsearch:使用同义词 synonyms 来提高搜索效率

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: Elasticsearch:使用同义词 synonyms 来提高搜索效率

作者:刘晓国


在我们的很多情况下,我们希望在搜索时,有时能够使用一个词的同义词来进行搜索,这样我们能搜索出来更多相关的内容。我们可以通过 text analysis 来帮助我们形成同义词。如果大家对 Elastic 的 analyzer 还不是很熟的话,请参阅我之前的文章 “Elasticsearch: analyzer”。文本分析通常应用于你建立索引时的所有文档以及发送给 Elasticsearch image.png的所有查询。在进行同义词搜索时,我们有如下的几种方案:


在建立索引时 (indexing),通过 analyzer 建立 synonyms 的反向索引 (inverted index)

在 query 时,通过 search analyzer 对查询的词建立 synonyms

在 indexing 及 query 时,同时建立反向索引中的 synonym 及在 query 时为查询的词建立 synonyms

那么在实际的使用中,我们到底是用上述的哪种方案呢?在下面的例子中,你将看到在 query 时使用 synonym 会更加灵活,并且更容易让我们更新同义词的名单已经更好地支持 multi-word synonyms


在今天的文章中,我们将分别论述。


在 query 时对词进行同义词解析


首先,我们来创建一个具有如下 anaylzer 及 mapping 的一个索引:

PUT myindex
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonyms": {
          "type": "synonym_graph",
          "synonyms": [
            "China, chn, PRC, People's Republic of China"
          ]
        }
      },
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter":[
            "lowercase",
            "my_synonyms"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "standard", 
        "search_analyzer": "my_analyzer"
      }
    }
  }
}

在上面,我们使用 synonym_graph 过滤器对 quey 时的词进行过滤。在这个过滤器中,我们把如下的一个词都视为同义词:

China, chn, PRC, People's Republic of China

在mapping 中,我们定义了 search_analyzer 为 my_analyzer,也就是说在 query 时,它会对所有的词进行分词。但凡有任何一个词是 China, chn, PRC, People's Republic of China 其中的一个,它都将被视为同义词。


我们首先来创建一个文档:

PUT myindex/_doc/1
{
  "content": "I like People's Republic of China"
}

运行上面的指令,我们将创建一个 content 为 I like People's Republic of China 的文档。


接下来,我们做如下的查询:

GET myindex/_search
{
  "query": {
    "match": {
      "content": "China"
    }
  }
}

那么显示的结果是:

{
  "took" : 256,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.4384104,
    "hits" : [
      {
        "_index" : "myindex",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.4384104,
        "_source" : {
          "content" : "I like People's Republic of China"
        }
      }
    ]
  }
}

可能有人说了,这是因为上面的 content 里本身就含有 China, 所以上面的结果证明不了什么。接下来,我们进行如下的搜索:

GET myindex/_search
{
  "query": {
    "match": {
      "content": "prc"
    }
  }
}

结果,我们可以发现,我们同样显示上面的搜索的结果。这个说明了这个同义词的搜索是成功的。


接下来,我们想搜索 silk road 也能搜索出中国来,那么我怎么做呢?


我们来执行如下的命令:

POST myindex/_close
PUT myindex/_settings
{
  "analysis": {
    "filter": {
      "my_synonyms": {
        "type": "synonym_graph",
        "synonyms": [
          "china, silk road, chn, PRC, People's Republic of China"
        ]
      }
    },
    "analyzer": {
      "my_analyzer": {
        "type": "custom",
        "tokenizer": "standard",
        "filter": [
          "lowercase",
          "my_synonyms"
        ]
      }
    }
  }
}
POST myindex/_open

我们可以通过更新  setting 来实现这个。在上面请注意:当我们更新一个索引的 index 时,我们必须先把它关掉,等设置好后,在重新打开。否则会有错误。那么经过上面的修改后,我们重新运行如下的搜索:

GET myindex/_search
{
  "query": {
    "match": {
      "content": "silk road"
    }
  }
}

那么上面的搜索结果将会显示我们之前显示的结果。在这里 silk road 也就是和之前的其它词都是同义词。


有人可能觉得上面在 settings 里配置太多的同义词很麻烦(如果同义词很多的话)。按照 Elastic 的官方文档,我们可以把所有的同义词放到一个文档中。首先,我们在 Elasticsearch 的 config 目录中,创建一个叫做 analysis 的子目录,然后创建一个叫做 synonyms.txt 的文档,而它的内容如下:

$ pwd
/Users/liuxg/elastic/elasticsearch-7.8.0/config/analysis
liuxg:analysis liuxg$ cat synonyms.txt 
"china, silk road, chn, PRC, People's Republic of China",
"elk, elastic stack"

在这里,我们多添加了一个 elk, elastic stack 的同义词。我们来创建一个新的索引:

PUT myindex1
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonyms": {
          "type": "synonym_graph",
          "synonyms_path": "analysis/synonyms.txt"
        }
      },
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter":[
            "lowercase",
            "my_synonyms"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "standard", 
        "search_analyzer": "my_analyzer"
      }
    }
  }
}

运行完上的指令后,我们来创建一个文档:

PUT myindex1/_doc/1
{
  "content": "I love elastic stack"
}

然后我们做如下的搜索:

GET myindex1/_search
{
  "query": {
    "match": {
      "content": "elk"
    }
  }
}

上面的搜索结果显示:

{
  "took" : 451,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "myindex1",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.5753642,
        "_source" : {
          "content" : "I love elastic stack"
        }
      }
    ]
  }
}

显然,我可以看到搜索 elk,我们就可以搜索到含有 elastic stack 的文档。


在实际的使用中,如果我们更新 synonyms.txt 文件,那么,我们可以使用如下的 API 来进行更新:

POST myindex1/_reload_search_analyzers


在建立索引时建立同义词


针对这种情况,我们可以在建立索引的时候,就把同义词建立好。这样,我们可以在 query 时,不使用同义词解析。在这种情况下,我们可以使用 synonym 过滤器,而不是 synonym_graph 过滤器。


我们接下来使用如下的命令来创建一个新的索引:

PUT myindex2
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonyms": {
          "type": "synonym",
          "synonyms": [
            "china, silk road, chn, PRC, People's Republic of China",
            "elk, elastic stack"
          ]
        }
      },
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_synonyms"
          ]
        }
      }
    },
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

在上面,我们使用了 my_analyzer 作为 myindex2 在索引时使用的分词器。它将使用 synonym 过滤器,并把如下的词视为同义词:

"china, silk road, chn, PRC, People's Republic of China",
"elk, elastic stack"

我们可以使用如下的方法来测试这个 analyzer:

POST myindex2/_analyze
{
  "text": "I like elk a lot",
  "analyzer": "my_analyzer"
}

上面的命令显示的结果是:

{
  "tokens" : [
    {
      "token" : "i",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "like",
      "start_offset" : 2,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "elk",
      "start_offset" : 7,
      "end_offset" : 10,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "elastic",
      "start_offset" : 7,
      "end_offset" : 10,
      "type" : "SYNONYM",
      "position" : 2
    },
    {
      "token" : "a",
      "start_offset" : 11,
      "end_offset" : 12,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "stack",
      "start_offset" : 11,
      "end_offset" : 12,
      "type" : "SYNONYM",
      "position" : 3
    },
    {
      "token" : "lot",
      "start_offset" : 13,
      "end_offset" : 16,
      "type" : "<ALPHANUM>",
      "position" : 4
    }
  ]
}

你可以看到,尽管在测试的 text 没有 elastic stack,只有 elk,但是显示的结果了含有 elastic 及 stack 这两个 token。


我们接下来使用如下的命令来创建一个文档:

PUT myindex2/_doc/1
{
  "content": "I like elk a lot"
}

我们使用如下的查询:

GET myindex2/_validate/query?rewrite=true
{
  "query": {
    "match": {
      "content": "elastic stack"
    }
  }
}

上面显示的结果是:

{
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "valid" : true,
  "explanations" : [
    {
      "index" : "myindex2",
      "valid" : true,
      "explanation" : """content:"elastic stack" content:elk"""
    }
  ]
}

从上面的显示的结果来看,当我们搜索 elastic stack 时,它同时匹配 content: "elastic stack" 以及 content: elk。也就是说,如果文档里含有 elk,那么这个文档也将被搜索到。我们做如下的搜索:

GET myindex2/_search
{
  "query": {
    "match": {
      "content": "elastic stack"
    }
  }
}

那么上面的命令显示的结果是:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.977273,
    "hits" : [
      {
        "_index" : "myindex2",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.977273,
        "_source" : {
          "content" : "I like elk a lot"
        }
      }
    ]
  }
}

显然它已经把我们的想要的结果搜索出来了。


总结


在上面,我们展示了两种方法进行同义词的查询。在实际的使用中,你可以根据自己的情况适当进行选择。当然,我们有可以把上面的两种方法进行同时并用。通过这两种方法,也有可能会造成搜索的精确度的问题。这个是你必须要想清楚的。这个就像我们撒网打鱼一样,把网撒大了,捞上来的也有可能不是我们想要的。


相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
2月前
|
数据采集 人工智能 运维
从企业级 RAG 到 AI Assistant,阿里云Elasticsearch AI 搜索技术实践
本文介绍了阿里云 Elasticsearch 推出的创新型 AI 搜索方案
160 3
从企业级 RAG 到 AI Assistant,阿里云Elasticsearch AI 搜索技术实践
|
17天前
|
人工智能 自然语言处理 搜索推荐
云端问道12期实操教学-构建基于Elasticsearch的企业级AI搜索应用
本文介绍了构建基于Elasticsearch的企业级AI搜索应用,涵盖了从传统关键词匹配到对话式问答的搜索形态演变。阿里云的AI搜索产品依托自研和开源(如Elasticsearch)引擎,提供高性能检索服务,支持千亿级数据毫秒响应。文章重点描述了AI搜索的三个核心关键点:精准结果、语义理解、高性能引擎,并展示了架构升级和典型应用场景,包括智能问答、电商导购、多模态图书及商品搜索等。通过实验部分,详细演示了如何使用阿里云ES搭建AI语义搜索Demo,涵盖模型创建、Pipeline配置、数据写入与检索测试等步骤,同时介绍了相关的计费模式。
|
17天前
|
人工智能 算法 API
构建基于 Elasticsearch 的企业级 AI 搜索应用
本文介绍了基于Elasticsearch构建企业级AI搜索应用的方案,重点讲解了RAG(检索增强生成)架构的实现。通过阿里云上的Elasticsearch AI搜索平台,简化了知识库文档抽取、文本切片等复杂流程,并结合稠密和稀疏向量的混合搜索技术,提升了召回和排序的准确性。此外,还探讨了Elastic的向量数据库优化措施及推理API的应用,展示了如何在云端高效实现精准的搜索与推理服务。未来将拓展至多模态数据和知识图谱,进一步提升RAG效果。
|
2月前
|
机器学习/深度学习 人工智能 运维
阿里云技术公开课直播预告:基于阿里云 Elasticsearch 构建 AI 搜索和可观测 Chatbot
阿里云技术公开课预告:Elastic和阿里云搜索技术专家将深入解读阿里云Elasticsearch Enterprise版的AI功能及其在实际应用。
154 2
阿里云技术公开课直播预告:基于阿里云 Elasticsearch 构建 AI 搜索和可观测 Chatbot
|
28天前
|
数据采集 人工智能 运维
从企业级 RAG 到 AI Assistant,阿里云Elasticsearch AI 搜索技术实践
本文介绍了阿里云 Elasticsearch 推出的创新型 AI 搜索方案。
141 5
|
2月前
|
存储 人工智能 API
(Elasticsearch)使用阿里云 infererence API 及 semantic text 进行向量搜索
本文展示了如何使用阿里云 infererence API 及 semantic text 进行向量搜索。
102 8
|
2月前
|
搜索推荐 API 定位技术
一文看懂Elasticsearch的技术架构:高效、精准的搜索神器
Elasticsearch 是一个基于 Lucene 的开源搜索引擎,以其强大的全文本搜索功能和快速的倒排索引技术著称。它不仅支持数字、文本、地理位置等多类型数据,还提供了可调相关度分数、高级查询 DSL 等功能。Elasticsearch 的核心技术流程包括数据导入、解析、索引化、查询处理、得分计算及结果返回,确保高效处理大规模数据并提供准确的搜索结果。通过 RESTful API、Logstash 和 Filebeat 等工具,Elasticsearch 可以从多种数据源中导入和解析数据,支持复杂的查询需求。
130 0
|
3月前
|
存储 缓存 固态存储
Elasticsearch高性能搜索
【11月更文挑战第1天】
69 6
|
3月前
|
API 索引
Elasticsearch实时搜索
【11月更文挑战第2天】
65 1
|
4月前
|
人工智能
云端问道12期-构建基于Elasticsearch的企业级AI搜索应用陪跑班获奖名单公布啦!
云端问道12期-构建基于Elasticsearch的企业级AI搜索应用陪跑班获奖名单公布啦!
193 2

热门文章

最新文章

相关产品

  • 检索分析服务 Elasticsearch版