【问题标题】:How to use an ngram and edge ngram tokenizer together in elasticsearch index?如何在弹性搜索索引中一起使用 ngram 和边缘 ngram 标记器?
【发布时间】:2020-08-28 00:54:06
【问题描述】:

我有一个包含 3 个文档的索引。

            {
                    "firstname": "Anne",
                    "lastname": "Borg",
                }

            {
                    "firstname": "Leanne",
                    "lastname": "Ray"
                },

            {
                    "firstname": "Anne",
                    "middlename": "M",
                    "lastname": "Stone"
                }

当我搜索“Anne”时,我希望弹性返回所有 3 个文档(因为它们都在一定程度上与术语“Anne”匹配)。但是,我希望 Leanne Ray 的分数(相关性排名)较低,因为搜索词“Anne”在本文档中出现的位置比在其他两个文档中出现的位置要晚。

最初,我使用的是 ngram 分词器。我的索引映射中还有一个名为“full_name”的生成字段,其中包含名字、中间名和姓氏字符串。当我搜索“Anne”时,所有 3 个文档都在结果集中。然而,Anne M Stone 的得分与 Leanne Ray 相同。 Anne M Stone 的分数应该比 Leanne 高。

为了解决这个问题,我将我的 ngram 分词器更改为 edge_ngram 分词器。这具有将 Leanne Ray 从结果集中完全排除在外的效果。我们希望将此结果保留在结果集中 - 因为它仍然包含查询字符串 - 但得分低于其他两个更好的匹配项。

我在某处读到,可以在同一索引中将边缘 ngram 过滤器与 ngram 过滤器一起使用。如果是这样,我应该如何重新创建我的索引来做到这一点?有没有更好的解决方案?

这里是初始索引设置。

{
    "settings": {
        "analysis": {
            "analyzer": {
                "my_analyzer": {
                    "filter": [
                        "lowercase"
                    ],
                    "type": "custom",
                    "tokenizer": "my_tokenizer"
                }
            },
            "tokenizer": {
                "my_tokenizer": {
                    "token_chars": [
                        "letter",
                        "digit",
                        "custom"
                    ],
                    "custom_token_chars": "'-",
                    "min_gram": "3",
                    "type": "ngram",
                    "max_gram": "4"
                }
            }
        }
    },
    "mappings": {
        "properties": {
            "contact_id": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            },

            "firstname": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                },
                "copy_to": [
                    "full_name"
                ]
            },


            "lastname": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                },
                "copy_to": [
                    "full_name"
                ]
            },

            "middlename": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                },
                "copy_to": [
                    "full_name"
                ]
            },

            "full_name": {
                "type": "text",
                "analyzer": "my_analyzer",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                }
            }
        }
    }
}

这是我的查询

{
    "query": {
        "bool": {
            "should": [
                {
                    "query_string": {
                        "query": "Anne",
                        "fields": [
                            "full_name"
                        ]
                    }
                }
            ]
        }
    }
}

这带回了这些结果

    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 0.59604377,
        "hits": [
            {
                "_index": "contacts_15",
                "_type": "_doc",
                "_id": "3",
                "_score": 0.59604377,
                "_source": {
                    "firstname": "Anne",
                    "lastname": "Borg"
                }
            },
            {
                "_index": "contacts_15",
                "_type": "_doc",
                "_id": "1",
                "_score": 0.5592099,
                "_source": {
                    "firstname": "Anne",
                    "middlename": "M",
                    "lastname": "Stone"
                }
            },
            {
                "_index": "contacts_15",
                "_type": "_doc",
                "_id": "2",
                "_score": 0.5592099,
                "_source": {
                    "firstname": "Leanne",
                    "lastname": "Ray"
                }
            }
        ]
    }

如果我改为使用边缘 ngram 标记器,这就是索引设置的样子...

{
    "settings": {
        "max_ngram_diff": "10",
        "analysis": {
            "analyzer": {
                "my_analyzer": {
                    "filter": [
                        "lowercase"
                    ],
                    "type": "custom",
                    "tokenizer": ["edge_ngram_tokenizer"]
                }
            },
            "tokenizer": {
                "edge_ngram_tokenizer": {
                    "token_chars": [
                        "letter",
                        "digit",
                        "custom"
                    ],
                    "custom_token_chars": "'-",
                    "min_gram": "2",
                    "type": "edge_ngram",
                    "max_gram": "10"
                }
            }
        }
    },
    "mappings": {
        "properties": {
            "contact_id": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            },

            "firstname": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                },
                "copy_to": [
                    "full_name"
                ]
            },


            "lastname": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                },
                "copy_to": [
                    "full_name"
                ]
            },

            "middlename": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                },
                "copy_to": [
                    "full_name"
                ]
            },

            "full_name": {
                "type": "text",
                "analyzer": "my_analyzer",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                }
            }
        }
    }
}

同样的查询带回了这个新的结果集...

   "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.5131824,
        "hits": [
            {
                "_index": "contacts_16",
                "_type": "_doc",
                "_id": "3",
                "_score": 1.5131824,
                "_source": {
                    "firstname": "Anne",
                    "middlename": "M",
                    "lastname": "Stone"
                }
            },
            {
                "_index": "contacts_16",
                "_type": "_doc",
                "_id": "1",
                "_score": 1.4100108,
                "_source": {
                    "firstname": "Anne",
                    "lastname": "Borg"
                }
            }
        ]
    }

【问题讨论】:

    标签: elasticsearch n-gram relevance


    【解决方案1】:

    您可以继续使用 ngram(即第一个解决方案),但随后您需要更改查询以提高相关性。它的工作方式是在should 子句中添加提升的multi_match 查询,以增加名字或姓氏与输入完全匹配的文档的分数:

    {
      "query": {
        "bool": {
          "must": [
            {
              "query_string": {
                "query": "Anne",
                "fields": [
                  "full_name"
                ]
              }
            }
          ],
          "should": [
            {
              "multi_match": {
                "query": "Anne",
                "fields": [
                  "firstname",
                  "lastname"
                ],
                "boost": 10
              }
            }
          ]
        }
      }
    }
    

    此查询会将Anne BorgAnne M Stone 放在Leanne Ray 之前。

    更新

    这是我得出结果的方式。

    首先,我使用与您添加到问题中的完全相同的设置/映射创建了一个测试索引:

    PUT test
    { ... copy/pasted mappings/settings ... }
    

    然后我添加了您提供的三个示例文档:

    POST test/_doc/_bulk
    {"index":{}}
    {"firstname":"Anne","lastname":"Borg"}
    {"index":{}}
    {"firstname":"Leanne","lastname":"Ray"}
    {"index":{}}
    {"firstname":"Anne","middlename":"M","lastname":"Stone"}
    

    最后,如果你运行我上面的查询,你会得到以下结果,这正是你所期望的(看看分数):

    {
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 5.1328206,
        "hits" : [
          {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "4ZqbDHIBhYuDqANwQ-ih",
            "_score" : 5.1328206,
            "_source" : {
              "firstname" : "Anne",
              "lastname" : "Borg"
            }
          },
          {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "45qbDHIBhYuDqANwQ-ih",
            "_score" : 5.0862665,
            "_source" : {
              "firstname" : "Anne",
              "middlename" : "M",
              "lastname" : "Stone"
            }
          },
          {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "4pqbDHIBhYuDqANwQ-ih",
            "_score" : 0.38623023,
            "_source" : {
              "firstname" : "Leanne",
              "lastname" : "Ray"
            }
          }
        ]
      }
    }
    

    【讨论】:

    • 那个查询根本没有带回 Leanne Ray。
    • 您应该从头开始再试一次,因为我上面报告的所有内容都是基于您提供的映射和样本数据的真实测试,而不是假设;-) 或者用您尝试的内容更新您的问题做,我们可以看到痒的地方
    • 谢谢瓦尔。我让我的问题更直截了当。这有帮助吗?分享哪些其他信息会有所帮助?我想不出别的了。
    • 我已经更新了我的答案,向您展示我是如何创建我的查询以及它返回什么结果。我没有更改查询,因为它按您期望的方式工作。
    • 谢谢。我的错。您的查询确实回答了我的问题。我要提出另一个问题,因为您对这个问题的回答是完整的。也许您可以阐明这种可能更典型的情况stackoverflow.com/questions/61768534/…
    猜你喜欢
    • 1970-01-01
    • 2017-12-23
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 2022-08-17
    • 2021-01-18
    • 2017-10-27
    • 1970-01-01
    相关资源
    最近更新 更多