MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

ElasticSearch执行搜索的精准度优化

2023-09-306.1k 阅读

ElasticSearch执行搜索的精准度优化基础概念

理解ElasticSearch搜索机制

ElasticSearch是基于Lucene构建的分布式搜索引擎,其搜索过程涉及多个步骤。当用户发起一个搜索请求时,首先请求会被分发到各个分片上。每个分片独立执行查询,然后将结果返回给协调节点。协调节点对这些结果进行合并、排序等操作后返回给用户。

ElasticSearch支持多种查询类型,如全文搜索(match query)、精确匹配(term query)等。全文搜索会对输入文本进行分词处理,然后在倒排索引中查找相关文档。例如,对于文本“ElasticSearch is a great search engine”,使用标准分词器会将其分为“elasticsearch”、“is”、“a”、“great”、“search”、“engine”等词项(terms)。精确匹配则直接在倒排索引中查找完整的词项。

精准度相关因素

  1. 分词器的影响:不同的分词器会对文本产生不同的分词结果。标准分词器适用于大多数语言,但对于一些特殊需求,如中文分词,需要使用专门的中文分词器。例如,使用IK分词器对“我爱北京天安门”进行分词,可能会得到“我”、“爱”、“北京”、“天安门”这样更符合中文语义的结果,相比标准分词器在处理中文时会更精准。
  2. 文档结构与映射:文档的字段类型和映射定义对搜索精准度有重要影响。如果将一个应该是精确匹配的字段定义为全文搜索类型,可能会导致搜索结果不准确。例如,将身份证号码字段定义为text类型并进行全文搜索,就会出现模糊匹配的情况,而定义为keyword类型则可实现精确匹配。
  3. 查询语法与参数:选择合适的查询语法和参数可以提高搜索精准度。在使用match query时,可以通过调整参数如minimum_should_match来控制匹配的词项数量。例如,设置minimum_should_match为75%,意味着至少75%的词项需要匹配才能返回文档,这可以避免一些无关文档的返回,提高精准度。

优化分词策略提升精准度

选择合适的分词器

  1. 内置分词器:ElasticSearch提供了多种内置分词器。除了标准分词器外,还有空格分词器(whitespace),它以空格为分隔符进行分词,适用于简单按空格分割的场景;keyword分词器,它不会对输入文本进行分词,而是将整个文本作为一个词项,常用于精确匹配字段,如产品编号、订单号等。例如,对于字段“product_id: P001”,使用keyword分词器就可以确保在搜索“P001”时能精确匹配到该文档。
  2. 自定义分词器:在某些复杂场景下,内置分词器无法满足需求,需要自定义分词器。自定义分词器可以通过组合字符过滤器(character filters)、分词器(tokenizer)和词项过滤器(token filters)来实现。假设我们有一个需求,需要对一些带有特殊字符的文本进行处理,先去除特殊字符再进行分词。可以定义一个自定义分词器,使用字符过滤器去除特殊字符,如html_strip字符过滤器可以去除HTML标签,然后使用标准分词器进行分词,最后使用词项过滤器进行一些词项的转换或过滤。
    PUT my_index
    {
      "settings": {
        "analysis": {
          "analyzer": {
            "my_custom_analyzer": {
              "type": "custom",
              "char_filter": ["html_strip"],
              "tokenizer": "standard",
              "filter": ["lowercase"]
            }
          }
        }
      }
    }
    
    在上述示例中,定义了一个名为my_custom_analyzer的自定义分词器,它首先使用html_strip字符过滤器去除HTML标签,然后使用标准分词器分词,最后使用lowercase词项过滤器将所有词项转换为小写。

分词优化技巧

  1. 同义词处理:在搜索中,同义词的存在可能会影响精准度。通过添加同义词可以扩大搜索范围,但同时也需要注意可能引入的噪声。可以通过同义词词项过滤器(synonym token filter)来处理同义词。例如,我们希望“car”、“automobile”和“motor vehicle”在搜索时视为同义词。
    PUT my_index
    {
      "settings": {
        "analysis": {
          "filter": {
            "my_synonym_filter": {
              "type": "synonym",
              "synonyms": ["car, automobile, motor vehicle"]
            }
          },
          "analyzer": {
            "my_custom_analyzer": {
              "type": "custom",
              "tokenizer": "standard",
              "filter": ["my_synonym_filter"]
            }
          }
        }
      }
    }
    
    这样,当用户搜索“car”时,包含“automobile”或“motor vehicle”的文档也会被返回。
  2. 停用词处理:停用词是一些常见但对搜索结果贡献不大的词,如“the”、“and”、“is”等。去除停用词可以减少噪声,提高搜索精准度。可以使用停用词词项过滤器(stop token filter)。例如,对于英文文本,可以定义一个包含常见英文停用词的停用词过滤器。
    PUT my_index
    {
      "settings": {
        "analysis": {
          "filter": {
            "my_stop_filter": {
              "type": "stop",
              "stopwords": ["the", "and", "is", "are", "of"]
            }
          },
          "analyzer": {
            "my_custom_analyzer": {
              "type": "custom",
              "tokenizer": "standard",
              "filter": ["my_stop_filter"]
            }
          }
        }
      }
    }
    
    经过这个自定义分析器处理后,文档中的停用词在索引和搜索时将被忽略。

优化文档映射提高精准度

合理定义字段类型

  1. 区分text和keyword:如前文所述,text类型适用于全文搜索,会进行分词处理;keyword类型适用于精确匹配,不会分词。对于需要精确匹配的字段,如用户名、手机号码等,应定义为keyword类型。例如,定义一个用户文档:
    PUT users
    {
      "mappings": {
        "properties": {
          "username": {
            "type": "keyword"
          },
          "bio": {
            "type": "text"
          }
        }
      }
    }
    
    这样,在搜索用户名时可以使用精确匹配,而搜索用户简介(bio)时可以进行全文搜索。
  2. 数值类型与日期类型:对于数值字段,如年龄、价格等,应使用合适的数值类型,如integerfloat等。日期字段则使用date类型。正确定义这些类型可以确保在进行范围查询、排序等操作时的精准度。例如,定义一个产品文档:
    PUT products
    {
      "mappings": {
        "properties": {
          "price": {
            "type": "float"
          },
          "release_date": {
            "type": "date"
          }
        }
      }
    }
    
    这样在查询价格范围或按发布日期排序时就能得到准确的结果。

多字段映射

  1. 同时使用text和keyword:有时,我们既需要对一个字段进行全文搜索,又需要进行精确匹配。这时可以使用多字段映射。例如,对于产品名称字段,我们可以同时定义一个text类型用于全文搜索,一个keyword类型用于精确匹配。
    PUT products
    {
      "mappings": {
        "properties": {
          "product_name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword"
              }
            }
          }
        }
      }
    }
    
    这样,在搜索产品名称时,可以根据需求选择使用product_name进行全文搜索,或使用product_name.keyword进行精确匹配。
  2. 不同分析器的多字段:对于一些需要不同分词策略的场景,也可以使用多字段映射。比如,对于一篇英文文章,我们可能希望使用标准分词器进行全文搜索,同时使用一个自定义分析器对文章中的特定术语进行更精准的匹配。
    PUT articles
    {
      "mappings": {
        "properties": {
          "article_text": {
            "type": "text",
            "fields": {
              "standard_search": {
                "type": "text",
                "analyzer": "standard"
              },
              "custom_search": {
                "type": "text",
                "analyzer": "my_custom_analyzer"
              }
            }
          }
        }
      }
    }
    
    这里定义了article_text字段的两个子字段,standard_search使用标准分析器,custom_search使用自定义分析器,满足不同的搜索需求。

优化查询语句提升精准度

选择合适的查询类型

  1. 全文搜索与精确匹配选择:如果用户明确需要精确匹配,如搜索产品编号、订单号等,应使用term query。例如,搜索产品编号为“P001”的产品:
    GET products/_search
    {
      "query": {
        "term": {
          "product_id": {
            "value": "P001"
          }
        }
      }
    }
    
    而对于用户输入的自然语言文本,如搜索产品描述相关内容,则应使用match query。例如,搜索描述中包含“high quality”的产品:
    GET products/_search
    {
      "query": {
        "match": {
          "description": "high quality"
        }
      }
    }
    
  2. 短语匹配与模糊匹配:当用户希望搜索精确的短语时,应使用match_phrase query。例如,搜索描述中包含“red apple”这个短语的产品:
    GET products/_search
    {
      "query": {
        "match_phrase": {
          "description": "red apple"
        }
      }
    }
    
    模糊匹配则适用于用户不确定准确词汇但希望找到相似结果的场景,如使用fuzzy query。例如,搜索可能拼写错误的“aple”,可以找到包含“apple”的文档:
    GET products/_search
    {
      "query": {
        "fuzzy": {
          "product_name": {
            "value": "aple",
            "fuzziness": 2
          }
        }
      }
    }
    
    这里fuzziness参数表示允许的编辑距离,取值范围为0 - 2,0表示精确匹配,1表示一个字符的差异,2表示两个字符的差异。

查询参数优化

  1. minimum_should_match参数:在match query中,minimum_should_match参数控制匹配的词项数量。默认情况下,它会根据文档长度等因素自动计算。但我们可以手动设置以提高精准度。例如,希望至少80%的词项匹配才能返回文档:
    GET products/_search
    {
      "query": {
        "match": {
          "description": {
            "query": "high quality product",
            "minimum_should_match": "80%"
          }
        }
      }
    }
    
    这样可以避免一些只匹配少量词项的无关文档被返回。
  2. boost参数boost参数用于调整字段或查询的权重。例如,在一个包含产品名称和描述的搜索中,我们认为产品名称更重要,可以给产品名称字段更高的权重。
    GET products/_search
    {
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query": "high quality",
              "fields": ["product_name^3", "description"]
            }
          }
        }
      }
    }
    
    这里product_name^3表示产品名称字段的权重是描述字段的3倍,这样包含“high quality”在产品名称中的文档会在搜索结果中更靠前。

利用索引优化提升精准度

索引结构优化

  1. 适当拆分索引:如果一个索引包含的数据非常复杂,不同类型的数据混合在一起,可能会影响搜索精准度。可以考虑将索引进行适当拆分。例如,一个包含用户信息、订单信息和产品信息的大索引,可以拆分为用户索引、订单索引和产品索引。这样在搜索时可以更精准地定位到相关数据,减少噪声。
  2. 索引分片与副本:合理设置索引的分片和副本数量也会影响搜索性能和精准度。分片数量过多可能会导致搜索时的合并开销增大,影响精准度和性能;分片数量过少则可能无法充分利用集群资源。副本数量主要影响数据的可用性和读性能。一般来说,需要根据数据量和查询负载来调整分片和副本数量。例如,对于一个数据量较小但读请求较多的索引,可以适当增加副本数量以提高读性能。

索引维护与更新

  1. 定期重建索引:随着数据的不断变化,索引中的数据可能会变得碎片化,影响搜索精准度和性能。定期重建索引可以重新组织数据,提高索引质量。例如,每月对一些变化较大的索引进行重建,确保索引结构的合理性。
  2. 增量更新优化:在进行增量更新时,要注意对索引的影响。如果频繁进行小的更新操作,可能会导致索引碎片化。可以考虑批量更新操作,减少索引的碎片化程度。例如,将多个文档的更新操作合并为一个批量更新请求,这样可以减少索引的调整次数,提高索引的稳定性和搜索精准度。

其他优化手段提升精准度

语义理解与知识图谱应用

  1. 语义理解技术:利用自然语言处理(NLP)中的语义理解技术,如词向量模型(Word2Vec、GloVe等),可以更好地理解用户查询的语义。例如,通过训练词向量模型,可以发现“car”和“automobile”在语义空间中距离较近,从而在搜索时将它们视为更相关的词汇。可以将这种语义理解技术集成到ElasticSearch的搜索逻辑中,提高搜索精准度。
  2. 知识图谱整合:知识图谱包含了丰富的实体和关系信息。将知识图谱与ElasticSearch结合,可以利用知识图谱中的信息来丰富搜索结果。例如,当用户搜索“苹果”时,如果知识图谱中知道“苹果”既可以指水果,也可以指科技公司,那么可以根据用户的搜索历史或上下文信息,更精准地返回相关结果。可以通过在ElasticSearch中添加自定义插件或扩展来实现与知识图谱的整合。

用户反馈与搜索日志分析

  1. 用户反馈收集:收集用户对搜索结果的反馈,如用户标记为不相关的结果,或用户重新调整搜索词后得到了更满意的结果。通过分析这些反馈,可以了解到当前搜索精准度存在的问题,进而对分词策略、查询语句等进行调整。例如,如果很多用户反馈某个搜索词返回了大量无关结果,可以考虑调整该搜索词对应的分词策略或查询参数。
  2. 搜索日志分析:分析搜索日志可以了解用户的搜索行为模式,包括常见的搜索词、搜索频率、搜索时间等。通过对搜索日志的分析,可以发现一些潜在的优化点。例如,如果发现某个时间段内某个搜索词的搜索量突然增加,但搜索结果精准度较低,可以针对这个搜索词进行深入分析和优化。可以使用日志分析工具,如Elasticsearch本身的日志功能结合Kibana进行可视化分析,快速定位问题并进行优化。