Elasticsearch 如何匹配字段标记是查询标记子集的文档
Elasticsearch how to match documents for which the field tokens are a sub-set of the query tokens
我有一个 keyword/key-phrase 字段,我使用标准分析器对其进行了标记。如果搜索短语中包含该字段的所有标记,我希望该字段匹配。
例如,如果字段值为 "veni, vidi, vici" 且搜索词组为 "Ceaser veni,vidi,vici" 我希望此搜索词组匹配但搜索词组 "veni, vidi" 不匹配。
我还需要 "vidi, veni, vici"(奇怪!)来匹配。所以术语的位置和顺序并不重要。我认为词组匹配不太适合我。
对于这个特定示例,我可以使用 "bool query" 和 "minimum_should_match" 参数,但这并不是我真正想要的,因为至少应该匹配搜索短语中大约 ratio/number 个标记。
没有内置解决方案,但这个有效:
为每个文档添加一个额外的字段,其中包含字段中的术语数。因此,在您的 "veni, vidi, vici" 示例中,您将拥有一个类似于 "field_term_count" 的字段:3.
对搜索查询中的每个标记执行单独的匹配搜索。
对至少有一个匹配项的每个文档匹配的搜索次数求和(例如,具有文档 ID 键和计数值的哈希表)。
将 3 中的匹配项数与每个具有匹配项的文档的 "field_term_count" 字段进行比较。如果它们相等,则文档匹配。
然后 "Ceaser veni,vidi,vici" 将匹配,但搜索词组 "veni, vidi" 将不会根据需要匹配。对于合理数量的匹配,它应该相当快。
纯 ES 解决方案应该是这样的。您将需要两个请求。
1) 首先你需要通过 analyze api 传递用户查询以获得所有搜索令牌。
curl -XGET 'localhost:9200/_analyze' -d '
{
"analyzer" : "standard",
"text" : "Ceaser veni,vidi,vici"
}'
您将获得 4 个代币 ceaser, veni, vidi, 维奇。您需要将这些标记作为数组传递给下一个 search
请求。
2) 我们需要搜索其标记为搜索标记 子集 的文档。
{
"query": {
"filtered": {
"filter": {
"bool": {
"must": [
{
"query": {
"match": {
"title": "Ceaser veni,vidi,vici"
}
}
},
{
"script": {
"script": "if(search_tokens.containsAll(doc['title'].values)){return true;}",
"params": {
"search_tokens": [
"ceaser",
"veni",
"vidi",
"vici"
]
}
}
}
]
}
}
}
}
}
过滤器中第一个 match query
的工作是缩小脚本应该 运行 的文档范围。 containsAll
方法将检查文档标记是否为搜索标记的 sublist
。这会很慢,但可以完成您当前设置的工作。您可以做的一项重大改进是将令牌存储为数组,以便 doc['title'].values
可以替换为该字段,从而改进脚本。
希望对您有所帮助!
我有一个 keyword/key-phrase 字段,我使用标准分析器对其进行了标记。如果搜索短语中包含该字段的所有标记,我希望该字段匹配。
例如,如果字段值为 "veni, vidi, vici" 且搜索词组为 "Ceaser veni,vidi,vici" 我希望此搜索词组匹配但搜索词组 "veni, vidi" 不匹配。
我还需要 "vidi, veni, vici"(奇怪!)来匹配。所以术语的位置和顺序并不重要。我认为词组匹配不太适合我。
对于这个特定示例,我可以使用 "bool query" 和 "minimum_should_match" 参数,但这并不是我真正想要的,因为至少应该匹配搜索短语中大约 ratio/number 个标记。
没有内置解决方案,但这个有效:
为每个文档添加一个额外的字段,其中包含字段中的术语数。因此,在您的 "veni, vidi, vici" 示例中,您将拥有一个类似于 "field_term_count" 的字段:3.
对搜索查询中的每个标记执行单独的匹配搜索。
对至少有一个匹配项的每个文档匹配的搜索次数求和(例如,具有文档 ID 键和计数值的哈希表)。
将 3 中的匹配项数与每个具有匹配项的文档的 "field_term_count" 字段进行比较。如果它们相等,则文档匹配。
然后 "Ceaser veni,vidi,vici" 将匹配,但搜索词组 "veni, vidi" 将不会根据需要匹配。对于合理数量的匹配,它应该相当快。
纯 ES 解决方案应该是这样的。您将需要两个请求。
1) 首先你需要通过 analyze api 传递用户查询以获得所有搜索令牌。
curl -XGET 'localhost:9200/_analyze' -d '
{
"analyzer" : "standard",
"text" : "Ceaser veni,vidi,vici"
}'
您将获得 4 个代币 ceaser, veni, vidi, 维奇。您需要将这些标记作为数组传递给下一个 search
请求。
2) 我们需要搜索其标记为搜索标记 子集 的文档。
{
"query": {
"filtered": {
"filter": {
"bool": {
"must": [
{
"query": {
"match": {
"title": "Ceaser veni,vidi,vici"
}
}
},
{
"script": {
"script": "if(search_tokens.containsAll(doc['title'].values)){return true;}",
"params": {
"search_tokens": [
"ceaser",
"veni",
"vidi",
"vici"
]
}
}
}
]
}
}
}
}
}
过滤器中第一个 match query
的工作是缩小脚本应该 运行 的文档范围。 containsAll
方法将检查文档标记是否为搜索标记的 sublist
。这会很慢,但可以完成您当前设置的工作。您可以做的一项重大改进是将令牌存储为数组,以便 doc['title'].values
可以替换为该字段,从而改进脚本。
希望对您有所帮助!