gpt4 book ai didi

symfony - Elasticsearch PHP最长前缀匹配

转载 作者:行者123 更新时间:2023-12-02 23:35:13 24 4
gpt4 key购买 nike

我目前在Symfony2中使用FOSElasticaBundle,但在尝试建立匹配最长前缀的搜索时遇到了困难。

我知道Internet上有100个示例使用此示例执行类似自动完成的搜索。但是,我的问题有点不同。

在自动完成类型的搜索中,数据库包含最长的字母数字字符串(以字符长度表示),而用户仅提供了最短的部分,比方说用户键入“jho”,而Elasticsearch可以轻松提供“Jhon,Jhonny,Jhonas”。

我的问题是向后的,我想提供最长的字母数字字符串,并且我希望Elasticsearch为我提供数据库中最大的匹配项。

例如:我可以提供“123456789”,并且我的数据库可以具有[12,123,14,156,16,7,1234,1,67,8,9,123456,0],在这种情况下,数据库中最长的前缀匹配用户提供的数字是“123456”。

我只是从Elasticsearch开始,所以我真的没有接近工作设置或其他任何东西的信息。

如果有任何不清楚或缺失的信息,请告诉我,我将提供更多详细信息。

更新1(使用Val的第二次更新)

索引:Download 1800+ indexes

Settings:

curl -XPUT localhost:9200/tests -d '{
"settings": {
"analysis": {
"analyzer": {
"edge_ngram_analyzer": {
"tokenizer": "edge_ngram_tokenizer",
"filter": [ "lowercase" ]
}
},
"tokenizer": {
"edge_ngram_tokenizer": {
"type": "edgeNGram",
"min_gram": "2",
"max_gram": "25"
}
}
}
},
"mappings": {
"test": {
"properties": {
"my_string": {
"type": "string",
"fields": {
"prefix": {
"type": "string",
"analyzer": "edge_ngram_analyzer"
}
}
}
}
}
}
}'


Query:

curl -XPOST localhost:9200/tests/test/_search?pretty=true -d '{
"size": 1,
"sort": {
"_script": {
"script": "doc.my_string.value.length()",
"type": "number",
"order": "desc"
},
"_score": "desc"
},
"query": {
"filtered": {
"query": {
"match": {
"my_string.prefix": "8092232423"
}
},
"filter": {
"script": {
"script": "doc.my_string.value.length() <= maxlength",
"params": {
"maxlength": 10
}
}
}
}
}
}'

With this configuration the query returns the following results:

{
"took" : 61,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 1754,
"max_score" : null,
"hits" : [ {
"_index" : "tests",
"_type" : "test",
"_id" : "AU8LqQo4FbTZPxBtq3-Q",
"_score" : 0.13441172,
"_source":{"my_string":"80928870"},
"sort" : [ 8.0, 0.13441172 ]
} ]
}
}

奖励问题

我想为搜索提供一个数字数组,并以一种高效的方式获取每个数字的匹配前缀,而不必每次都执行查询

最佳答案

这是我的看法。

基本上,我们需要做的是在索引时使用 my_string tokenizer(以下称为edgeNGram)对字段(以下称为edge_ngram_tokenizer)进行 slice 和切块。这样,像123456789这样的字符串将被标记为12123123412345123456123456712345678123456789,所有标记都将被索引并可以搜索。

因此,让我们创建一个tests索引,一个名为edge_ngram_analyzer Analyzer的自定义分析器以及一个包含单个字符串字段testmy_string映射。您会注意到my_string字段是一个多字段,它声明prefixes子字段,其中将包含所有标记化前缀。

curl -XPUT localhost:9200/tests -d '{
"settings": {
"analysis": {
"analyzer": {
"edge_ngram_analyzer": {
"tokenizer": "edge_ngram_tokenizer",
"filter": [ "lowercase" ]
}
},
"tokenizer": {
"edge_ngram_tokenizer": {
"type": "edgeNGram",
"min_gram": "2",
"max_gram": "25"
}
}
}
},
"mappings": {
"test": {
"properties": {
"my_string": {
"type": "string",
"fields": {
"prefixes": {
"type": "string",
"index_analyzer": "edge_ngram_analyzer"
}
}
}
}
}
}
}

然后让我们使用 test API为一些 _bulk文档建立索引:
curl -XPOST localhost:9200/tests/test/_bulk -d '
{"index":{}}
{"my_string":"12"}
{"index":{}}
{"my_string":"1234"}
{"index":{}}
{"my_string":"1234567890"}
{"index":{}}
{"my_string":"abcd"}
{"index":{}}
{"my_string":"abcdefgh"}
{"index":{}}
{"my_string":"123456789abcd"}
{"index":{}}
{"my_string":"abcd123456789"}
'

我发现特别棘手的是,匹配结果可以长于或短于输入字符串。为此,我们必须组合两个查询,一个查询寻找较短的匹配项,另一个查询寻找较长的匹配项。因此, match查询将查找带有与输入匹配的较短“前缀”的文档,并且 query_string查询(将 edge_ngram_analyzer应用于输入字符串!)将搜索比输入字符串更长的“前缀”。两者都包含在 bool/should中,并以减小的字符串长度(即最长的最短字符串)排序。

让我们做一些查询,看看会发生什么:

此查询将返回与“123456789”最长匹配的一个文档,即“123456789abcd”。在这种情况下,结果比输入要长。
curl -XPOST localhost:9200/tests/test/_search -d '{
"size": 1,
"sort": {
"_script": {
"script": "doc.my_string.value.length()",
"type": "number",
"order": "desc"
}
},
"query": {
"bool": {
"should": [
{
"match": {
"my_string.prefixes": "123456789"
}
},
{
"query_string": {
"query": "123456789",
"default_field": "my_string.prefixes",
"analyzer": "edge_ngram_analyzer"
}
}
]
}
}
}'

第二个查询将返回与“123456789abcdef”最长匹配的一个文档,即“123456789abcd”。在这种情况下,结果比输入短。
curl -XPOST localhost:9200/tests/test/_search -d '{
"size": 1,
"sort": {
"_script": {
"script": "doc.my_string.value.length()",
"type": "number",
"order": "desc"
}
},
"query": {
"bool": {
"should": [
{
"match": {
"my_string.prefixes": "123456789abcdef"
}
},
{
"query_string": {
"query": "123456789abcdef",
"default_field": "my_string.prefixes",
"analyzer": "edge_ngram_analyzer"
}
}
]
}
}
}'

我希望能掩盖它。让我知道是否。

至于您的奖金问题,我只是建议您使用 _msearch API并立即发送所有查询。

UPDATE :最后,请确保使用以下命令在 elasticsearch.yml文件中启用脚本:
 # if you have ES <1.6
script.disable_dynamic: false

# if you have ES >=1.6
script.inline: on

更新2 我离开上面的内容,因为用例可能适合其他人的需求。现在,由于您只需要“更短”的前缀(很有意义!),因此我们需要稍微更改映射和查询。

映射将如下所示:
{
"settings": {
"analysis": {
"analyzer": {
"edge_ngram_analyzer": {
"tokenizer": "edge_ngram_tokenizer",
"filter": [
"lowercase"
]
}
},
"tokenizer": {
"edge_ngram_tokenizer": {
"type": "edgeNGram",
"min_gram": "2",
"max_gram": "25"
}
}
}
},
"mappings": {
"test": {
"properties": {
"my_string": {
"type": "string",
"fields": {
"prefixes": {
"type": "string",
"analyzer": "edge_ngram_analyzer" <--- only change
}
}
}
}
}
}
}

现在查询将有所不同,但将始终只返回最长的前缀,但返回的长度短于或等于输入字符串。请尝试一下。我建议重新索引您的数据,以确保所有设置均正确。
{
"size": 1,
"sort": {
"_script": {
"script": "doc.my_string.value.length()",
"type": "number",
"order": "desc"
},
"_score": "desc" <----- also add this line
},
"query": {
"filtered": {
"query": {
"match": {
"my_string.prefixes": "123" <--- input string
}
},
"filter": {
"script": {
"script": "doc.my_string.value.length() <= maxlength",
"params": {
"maxlength": 3 <---- this needs to be set to the length of the input string
}
}
}
}
}
}

关于symfony - Elasticsearch PHP最长前缀匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31867568/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com