【问题标题】:Improve score if the field starts with the term如果字段以术语开头,则提高分数
【发布时间】:2016-12-15 18:07:02
【问题描述】:

我正在尝试在我的网站上进行高效的自动完成搜索输入,以搜索城市。我假设人们总是会以正确的单词顺序开始搜索他们的城市名称。 例如。居住在Saint-Maur 的用户会输入sai..,但不会首先输入mau..

如果结果以查询中的术语开头,我需要提高结果分数。例如。如果用户输入pari,则城市Parigné-le-Pôlin 的得分应该高于Fontenay-en-Parisis,因为它以pari 开头。

我正在使用边缘语法过滤器和短语匹配,因为单词的顺序很重要。我确信我的问题有一个简单的解决方案,但我是 ES 魔法世界的新手 :)

这是我的映射:

{
    "settings": {
        "index": {
            "number_of_shards": 1
        },

        "analysis": {
            "analyzer": {
                "partialPostalCodeAnalyzer": {
                    "tokenizer": "standard",
                    "filter": ["partialFilter"]
                },
                "partialNameAnalyzer": {
                    "tokenizer": "standard",
                    "filter": ["asciifolding", "lowercase", "word_delimiter", "partialFilter"]
                },
                "searchAnalyzer": {
                    "tokenizer": "standard",
                    "filter": ["asciifolding", "lowercase", "word_delimiter"]
                }
            },

            "filter": {
                "partialFilter": {
                    "type": "edge_ngram",
                    "min_gram": 1,
                    "max_gram": 50
                }
            }
        }
    },

    "mappings": {
        "village": {
            "properties": {
                "postalCode": {
                    "type": "string",
                    "index_analyzer": "partialPostalCodeAnalyzer",
                    "search_analyzer": "searchAnalyzer"
                },

                "name": {
                    "type": "string",
                    "index_analyzer": "partialNameAnalyzer",
                    "search_analyzer": "searchAnalyzer"
                },

                "population": {
                    "type": "integer",
                    "index": "not_analyzed"
                }
            }
        }
    }
}

一些示例:

PUT /tv_village/village/1 {"name": "Paris"}
PUT /tv_village/village/2 {"name": "Parigny"}
PUT /tv_village/village/3 {"name": "Fontenay-en-Parisis"}
PUT /tv_village/village/4 {"name": "Parigné-le-Pôlin"}

如果我执行此查询,您会看到结果不是我希望的顺序(我希望第 4 个结果在 3d 之前):

GET /tv_village/village/_search
{
  "query": {
    "match_phrase": {
      "name": "pari"
    }
  }
}

结果:

      "hits": [
         {
            "_index": "tv_village",
            "_type": "village",
            "_id": "1",
            "_score": 0.7768564,
            "_source": {
               "name": "Paris"
            }
         },
         {
            "_index": "tv_village",
            "_type": "village",
            "_id": "2",
            "_score": 0.7768564,
            "_source": {
               "name": "Parigny"
            }
         },
         {
            "_index": "tv_village",
            "_type": "village",
            "_id": "3",
            "_score": 0.3884282,
            "_source": {
               "name": "Fontenay-en-Parisis"
            }
         },
         {
            "_index": "tv_village",
            "_type": "village",
            "_id": "4",
            "_score": 0.3884282,
            "_source": {
               "name": "Parigné-le-Pôlin"
            }
         }
      ]

【问题讨论】:

  • 你看过Suggest API吗?它可能更接近你想要的。

标签: elasticsearch scoring


【解决方案1】:

在您的映射定义中,放置另一个分析器:

            "keywordLowercaseAnalyer": {
              "tokenizer": "keyword",
              "filter": ["lowercase"]
            }

意思是保持单词完整(通过keyword 分析器)并将其小写(如“parigné-le-pôlin”)。 然后为您的 name 字段定义另外两个字段:

  • 一个raw应该是not_analyzed
  • 一个应该使用keywordLowercaseAnalyerraw_lowercase

    "name": {
      "type": "string",
      "index_analyzer": "partialNameAnalyzer",
      "search_analyzer": "searchAnalyzer",
      "fields": {
        "raw": {
          "type": "string",
          "index": "not_analyzed"
        },
        "raw_lowercase": {
          "type": "string",
          "analyzer": "keywordLowercaseAnalyer"
        }
      }
    }
    

我这样做是因为您可以搜索“pari”或“Pari”。在您的查询中,使用rescore 功能根据附加查询重新计算评分:

{
  "query": {
    "match_phrase": {
      "name": "pari"
    }
  },
  "rescore": {
    "query": {
      "rescore_query": {
        "bool": {
          "should": [
            {"prefix": {"name.raw": "pari"}},
            {"prefix": {"name.raw_lowercase": "pari"}}
          ]
        }
      }
    }
  }
}

从您的用例角度和关于prefix 查询的角度来看,有两个缺点:

  • 这非常耗费资源
  • 传递给prefix 的值是not_analyzed,这就是添加这两个raw* 字段的原因:一个字段处理小写版本,另一个处理未触及的版本,以便查询“pari " 或 "Pari" 涵盖了这些场景。

我有两个建议:

  • 在您的真实数据上测试上面的查询,看看它的行为方式和性能方面
  • rescore 查询中使用window_size 属性来限制执行重新评分的值的数量,从而提高性能。

供您参考,这是documentation page for rescore

【讨论】:

  • 您好,谢谢您的好回答,我做了一些更改以使其适应我的代码,现在一切似乎都很好:) 我只有一个问题:布尔值的目的是什么在您的重新评分查询中?它是强制性的吗?问候
  • 这是我能想到的将两个“前缀”查询放在同一个查询中的第一个想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-26
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多