【问题标题】:Elasticsearch query and sorting by parametersElasticsearch 查询和按参数排序
【发布时间】:2018-11-11 16:13:26
【问题描述】:

如何在elasticsearch中通过以下参数查询和排序文本

1 - 搜索查询在结果的第一部分准确

2 - 搜索查询在结果的另一部分准确

3 - 结果包含搜索查询的所有单词

例如:

当我搜索时:i love dogs

结果必须分别为:

1-  I love dogs

2 - i love dogs and birds

3 - birds good but i love dogs and horses 

4 - Horses and i love dogs

5 - I love horses and dogs

6 - good dogs and i love horses

【问题讨论】:

  • 没人帮忙吗?
  • 你用的是哪个elasticsearch版本?
  • 所以你希望文档首先包含短语(i love dogs),然后文档在搜索查询中包含“单词”。对吗?
  • 我使用的是 6.2 版

标签: elasticsearch


【解决方案1】:

可以实现所需的行为,但需要对映射和查询进行一些调整。

简而言之,这是有效的查询

首先,这是映射:

PUT my_phrase_search
{
  "mappings": {
    "doc": {
      "properties": {
        "expected_position": {
          "type": "long"
        },
        "my_phrase": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256,
              "normalizer": "my_normalizer"
            }
          }
        }
      }
    }
  },
  "settings": {
    "index": {
      "analysis": {
        "normalizer": {
          "my_normalizer": {
            "filter": [
              "lowercase"
            ],
            "type": "custom"
          }
        }
      }
    }
  }
}

注意:我添加了字段expected_position 以便更轻松地评估结果。

现在,查询:

POST my_phrase_search/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "should": [
              {
                "prefix": {
                  "my_phrase.keyword": "i love dogs"
                }
              }
            ],
            "_name": "prefix",
            "boost": 2
          }
        },
        {
          "bool": {
            "should": [
              {
                "match": {
                  "my_phrase": "i love dogs"
                }
              }
            ],
            "_name": "match"
          }
        },
        {
          "bool": {
            "should": [
              {
                "match_phrase": {
                  "my_phrase": "i love dogs"
                }
              }
            ],
            "_name": "phrase",
            "boost": 2
          }
        }
      ]
    }
  }
}

这给出了以下结果:

[
  {
    "_score": 4.015718,
    "_source": {
      "my_phrase": "I love dogs",
      "expected_position": 1
    },
    "matched_queries": [
      "match",
      "phrase",
      "prefix"
    ]
  },
  {
    "_score": 3.233316,
    "_source": {
      "my_phrase": "i love dogs and birds",
      "expected_position": 2
    },
    "matched_queries": [
      "match",
      "phrase",
      "prefix"
    ]
  },
  {
    "_score": 1.3836111,
    "_source": {
      "my_phrase": "birds good but i love dogs and horses ",
      "expected_position": 3
    },
    "matched_queries": [
      "match",
      "phrase"
    ]
  },
  {
    "_score": 1.2333161,
    "_source": {
      "my_phrase": "Horses and i love dogs",
      "expected_position": 4
    },
    "matched_queries": [
      "match",
      "phrase"
    ]
  },
  {
    "_score": 0.8630463,
    "_source": {
      "my_phrase": "I love horses and dogs",
      "expected_position": 5
    },
    "matched_queries": [
      "match"
    ]
  },
  {
    "_score": 0.38110584,
    "_source": {
      "my_phrase": "good dogs and i love horses",
      "expected_position": 6
    },
    "matched_queries": [
      "match"
    ]
  }
]

您可能想知道,它是如何工作的?所有这些改变都是必要的吗?一起来了解一下吧。

如果我们只使用text 字段和match 查询呢?

match 查询如下所示:

POST my_phrase/doc/_search
{
  "query": {
    "match": {
      "my_phrase": "i love dogs"
    }
  }
}

这将为我们提供以下结果顺序:5 - 1 - 3 - 2 - 4 - 6

问题是,为什么对"i love dogs" 的查询没有返回完美匹配1- I love dogs 作为第一个结果?为什么5 - I love horses and dogs排在第一位?

在这种情况下,答案是avgFieldLength,它用于计算score,它是computed per shard,因此对于不同的文档可能会略有不同。

很明显,ES 应该给我们以查询开头的结果。我们如何告诉 ES 更喜欢这样的文档?

keyword字段上添加prefix搜索

我们可以通过bool 查询将prefix 查询与match 查询结合使用(在这种情况下可以大致解释为OR),如下所示:

POST my_phrase/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "prefix": {
            "my_phrase.keyword": "i love dogs"
          }
        },
        {
          "match": {
            "my_phrase": "i love dogs"
          }
        }
      ]
    }
  }
}

请注意,prefix 查询仅适用于 keyword 类型,因为它需要将文档解释为一个大标记。

此查询为我们提供了以下结果顺序:2 - 5 - 1 - 3 - 4 - 6

2 跳起来,但 1 没有。为什么会这样?

这里字符的大小写起作用了:keyword 数据类型不被分析,因此iI 将对这个前缀搜索产生影响。

我们如何使keyword 不区分大小写?

使keyword不区分大小写

这是通过在映射中定义normalizer 来实现的:

PUT my_phrase2
{
  "settings": {
    "analysis": {
      "normalizer": {
        "my_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "doc": {
      "properties": {
        "my_phrase": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256,
                "normalizer": "my_normalizer"
              }
            }
          }
      }
    }
  }
}

现在,相同的查询将为我们提供以下顺序:1 - 2 - 5 - 3 - 4 - 6

这已经很不错了,但5 - I love horses and dogs 仍然太高——高于精确短语匹配的3 - birds good but i love dogs and horses

match 查询不关心短语中单词的顺序。我们可以提升具有正确顺序的文档吗?

添加match_phrase 以增强词组匹配

match_phrase 查询确实支持原始顺序中的令牌。让我们在查询中使用它:

POST my_phrase2/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "prefix": {
            "my_phrase.keyword": "i love dogs"
          }
        },
        {
          "match_phrase": {
            "my_phrase": "i love dogs"
          }
        },
        {
          "match": {
            "my_phrase": "i love dogs"
          }
        }
      ]
    }
  }
}

这给了我们以下顺序:1 - 2 - 3 - 5 - 4 - 6

3 出现了!但是5 - I love horses and dogs 仍然高于4 - Horses and i love dogs。看起来词组匹配应该有利于 4。

查询变得相当复杂,让我们找出文档实际匹配的部分。

为查询添加名称

可以将names 提供给查询,以便了解复杂查询的哪些部分实际生效:

POST my_phrase2/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "should": [
              {
                "prefix": {
                  "my_phrase.keyword": "i love dogs"
                }
              }
            ],
            "_name": "prefix"
          }
        },
...

对感兴趣的文件的回复将给我们:

  {
    "_score": 0.8630463,
    "_source": {
      "my_phrase": "I love horses and dogs",
      "expected_position": 5
    },
    "matched_queries": [
      "match"
    ]
  },
  {
    "_score": 0.82221067,
    "_source": {
      "my_phrase": "Horses and i love dogs",
      "expected_position": 4
    },
    "matched_queries": [
      "match",
      "phrase"
    ]
  },

Doc 5 与 phrase 部分不匹配。看起来分数波动再次袭击了我们。

短语查询看起来更相关,有没有办法提升它?

最后:提升词组和前缀查询

有一种方法可以影响分数的计算,告诉 ES 查询的某些部分更重要,称为boost。下面是它的样子:

POST my_phrase2/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "should": [
              {
                "prefix": {
                  "my_phrase.keyword": "i love dogs"
                }
              }
            ],
            "_name": "prefix",
            "boost": 2
          }
        },
        {
          "bool": {
            "should": [
              {
                "match": {
                  "my_phrase": "i love dogs"
                }
              }
            ],
            "_name": "match"
          }
        },
        {
          "bool": {
            "should": [
              {
                "match_phrase": {
                  "my_phrase": "i love dogs"
                }
              }
            ],
            "_name": "phrase",
            "boost": 2
          }
        }
      ]
    }
  }
}

这给了我们想要的结果顺序:1 - 2 - 3 - 4 - 5 - 6

请注意,我们还提升了 prefix 查询,因为我们想降低 match 的重要性。

这种方法安全吗,还是过拟合警告

虽然此查询可以完成这项工作,但您可能需要执行大量实际验证并进一步调整以确保获得足够的搜索结果。

完全适合这 6 个文档的查询可能不适合大型现实世界的集合,请将此答案作为优化的开始。

如您所见,并非查询的所有部分都是必需的:查询的名称可以很容易地省略,但有助于理解文档是如何匹配的。

【讨论】:

  • 谢谢,这个解决方案对波斯文本有什么不同吗?
  • @amirali 老实说,我没有使用波斯语的经验,但我相信将persian analyzer 用于text 字段就足够了。
【解决方案2】:

要获得您想要的结果,您需要使用match_phrase_prefix 和下面的max_expansions 等参数以供进一步阅读。

match_phrase_prefix

GET /_search
{
    "query": {
        "match_phrase_prefix" : {
            "message" : "quick brown f"
        }
    }
}

【讨论】:

    猜你喜欢
    • 2018-12-08
    • 2012-02-23
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 2022-01-17
    • 1970-01-01
    相关资源
    最近更新 更多