【问题标题】:Elasticsearch Query for good title keyword resultsElasticsearch 查询良好的标题关键字结果
【发布时间】:2019-07-24 06:37:36
【问题描述】:

我们有一个包含产品目录的弹性搜索索引,我们希望按标题和描述进行搜索。

我们希望它具有以下约束:

  • 我们正在搜索出现的标题和描述(标题中的匹配应该是描述的两倍)
  • 我们希望它有一个非常轻的模糊搜索结果(但仍然是准确的结果)
  • 不应过滤掉与搜索词不匹配的结果,而应仅在稍后显示(因此匹配的结果应位于顶部,较差的结果应位于底部)
  • category_id 应过滤掉产品(因此不应显示其他类别的结果)
  • created_at 属性在排序中的价值也应该很高。 产品应该失去他们获得的“旧”分数。 (这很重要,因为它们每天都在失去重要性)

我曾尝试创建这样的查询,但结果确实不太准确。有时会发现完全不相关的东西。我认为这是因为通配符查询。

此外,我认为“created_at”评分必须有一个更优雅的解决方案。对吧?

我正在使用 Elasticsearch 6.2

这是我当前的代码。我很高兴看到一个优雅的解决方案:

{
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "min_score": 0.3,
  "size": 12,
  "from": 0,
  "query": {
    "bool": {
      "filter": {
        "terms": {
          "category_id": [
            "212",
            "213"
          ]
        }
      },
      "should": [
        {
          "match": {
            "title_completion": {
              "query": "Development",
              "boost": 20
            }
          }
        },
        {
          "wildcard": {
            "title": {
              "value": "*Development*",
              "boost": 1
            }
          }
        },
        {
          "wildcard": {
            "title_completion": {
              "value": "*Development*",
              "boost": 10
            }
          }
        },
        {
          "match": {
            "title": {
              "query": "Development",
              "operator": "and",
              "fuzziness": 1
            }
          }
        },
        {
          "range": {
            "created_at": {
              "gte": 1563264817998,
              "boost": 11
            }
          }
        },
        {
          "range": {
            "created_at": {
              "gte": 1563264040398,
              "boost": 4
            }
          }
        },
        {
          "range": {
            "created_at": {
              "gte": 1563256264398,
              "boost": 1
            }
          }
        }
      ]
    }
  }
}

【问题讨论】:

  • 你试过用功能评分查询吗? elastic.co/guide/en/elasticsearch/reference/6.2/…
  • @KCFragrance 你的意思是距离问题?就像衰减 created_at 日期一样?不,我没有,但谢谢你的建议。剩下的“搜索词”问题呢?对于这样一个明显的用例,在标题和描述中搜索搜索词并使其对拼写错误稍微灵活一些,但仍然很好,必须有一种简单的方法或准备好的查询。对吗?

标签: elasticsearch


【解决方案1】:

首先,构建返回相关结果的请求通常是一项艰巨的任务。在不知道文件内容的情况下无法完成。也就是说,我可以给你一些提示来满足你的要求并避免不相关的结果。

我们正在搜索出现的标题和描述(标题中的匹配应该是描述的两倍)

您可以像在查询中一样使用boost,以便更加重视与描述匹配的标题。

我们希望它有一个非常轻的模糊搜索结果(但仍然是准确的结果)

您应该使用模糊字段的AUTO 值来定义不同的模糊值,具体取决于术语的长度。例如,默认情况下,少于 3 个字母的术语(最常见的术语,其中字母更改可能导致不同的单词)不允许更改。超过 3 个字母的术语将允许进行 1 次更改,超过 5 个将允许进行 2 次更改。您可以根据您的测试更改此行为。

与搜索词不匹配的结果不应该被过滤掉,而只在后面显示(所以匹配的结果应该在顶部,更差的结果应该在底部)

bool 语句中使用should 子句。 should 语句中的子句不过滤文档(除非另有说明)。 should 子句中的查询仅用于提高分数。

category_id 应过滤掉产品(因此不应显示其他类别的结果)

bool 语句中使用mustfilter 子句以确保所有文档都验证约束。如果您不希望子查询对分数有所贡献(我相信这是您的情况),请使用filter 而不是match,因为filter 将能够缓存结果。您的查询符合此要求。

created_at 属性在排序中的价值也应该很高。产品应该失去他们获得的“旧”分数。 (这很重要,因为他们每天都在失去重要性)

您应该使用function scoredecay function。如果您不清楚衰减函数,您可以跳过文档中的方程式并跳转到不言自明的图。以下查询是使用高斯衰减函数的示例。

{
    "function_score": {
        // Name of the decay function
        "gauss": {
            // Field to use
            "created_at": {
                    "origin": "now",  // "now" is the default so you can omit this field
                    "offset": "1d",   // Values with less than 1 day will not be impacted
                    "scale": "10d",   // Duration for which the scores will be scaled using a gauss function
                    "decay" : 0.01    // Score for values further than scale
            }
        }
    }
}

编写查询的提示

  • 避免通配符查询:如果您使用*,它们效率不高并且会消耗大量内存。如果您希望能够搜索部分术语(当用户搜索“house”时查找“penthouse”),您应该使用ngram tokenizer 创建一个子字段,并使用该子字段编写一个标准的match 查询。

  • 避免设置最低分数:分数是一个相对值。小分或高分并不意味着该文档是相关的或不相关的。您可以阅读this article 了解该主题。

  • 小心fuzzy 查询:模糊会产生大量噪音并使用户感到困惑。一般来说,我会建议为模糊增加默认的AUTO 阈值,并接受一些拼写错误的查询不会返回好的结果。通常,用户检测输入中的拼写错误要比理解为什么他得到完全不相关的结果更简单。

查询示例

这只是一个示例,您需要根据自己的数据进行调整。

{
  "size": 12,
  "query": {
    "bool": {
      "filter": {
        "terms": {
          "category_id": <CATEGORY_IDS>
        }
      },
      "should": [
        {
          "match": {
            "title": {
              "query": <QUERY>,
              "fuzziness": AUTO:4:12,
              "boost": 3
            }
          }
        },
        {
          "match": {
            "title_completion": {
              "query": <QUERY>,
              "boost": 1
            }
          }
        },
        {
          "match": {
            // title_completion field with ngram tokenizer
            "title_completion.ngram": {
              "query": <QUERY>,
              // Use lower boost because it match only partially
              "boost": 0.5
            }
          }
        }
      ]
    },
    "function_score": {
        // Name of the decay function
        "gauss": {
            // Field to use
            "created_at": {
                "origin": "now",  // "now" is the default so you can omit this field
                "offset": "1d",   // Values with less than 1 day will not be impacted
                "scale": "10d",   // Duration for which the scores will be scaled using a gauss function
                "decay" : 0.01    // Score for values further than scale
            }
        }
    }
  }
}

【讨论】:

  • 感谢@Pierre 的回答,但不幸的是,当我考虑到您的查询时,结果会更糟。我已经找到了衰减函数,但真正的问题是获得良好的搜索结果,既能找到部分术语,又能找到准确的术语和逻辑评分。我的意思是:如果我有几个标题为“一个拖拉机”“新拖拉机免费”、“拖拉机等”、“拖拉机”的条目,可以通过搜索词“拖拉机”找到。现在,当我使用您的查询时,当我将最低分数设置为 0.1 时,我不再得到任何结果
猜你喜欢
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-22
相关资源
最近更新 更多