用php实现Elasticsearch中的子字符串匹配

9
以下是我使用elasticsearch生成索引的代码。索引已成功生成。基本上,我使用它来根据电影名称、演员名称和类型生成自动建议。
现在我的要求是,我需要匹配特定字段中的子字符串。如果我使用$params['body']['query']['wildcard']['field'] = '*sub_word*';,这个方法可以正常工作(即搜索“to”会返回“tom kruz”,但搜索“tom kr”不会返回任何结果)。
这只匹配字符串中的特定单词。我想匹配包含多个单词的子字符串(即“tom kr”应该返回“tom kruz”)。
我发现一些文档,说可以使用“ngram”。但我不知道如何在我的代码中实现它,因为我正在使用基于数组的elasticsearch配置,而所有支持的文档都提到了json格式的配置。
请帮忙。
require 'vendor/autoload.php';

$client = \Elasticsearch\ClientBuilder::create()
->setHosts(['http://localhost:9200'])->build();

/*************Index a document****************/
$params = ['body' => []];
$j = 1;
for ($i = 1; $i <= 100; $i++) {
    $params['body'][] = [
        'index' => [
            '_index' => 'pvrmod',
            '_type' => 'movie',
            '_id' => $i
        ]
    ];
    if ($i % 10 == 0) 
        $j++;
    $params['body'][] = [
        'title' => 'salaman khaan'.$j,
        'desc' => 'salaman khaan description'.$j,
        'gener' => 'movie gener'.$j,
        'language' => 'movie language'.$j,
        'year' => 'movie year'.$j,
        'actor' => 'movie actor'.$j,
    ];

    // Every 10 documents stop and send the bulk request
    if ($i % 10 == 0) {
        $responses = $client->bulk($params);

        // erase the old bulk request
        $params = ['body' => []];

        unset($responses);
    }
}

// Send the last batch if it exists
if (!empty($params['body'])) {
    $responses = $client->bulk($params);
}
2个回答

6
问题在于Elasticsearch建立了一个倒排索引。假设您使用标准分析器,句子“tom kruz is a top gun”被分成6个标记:tom - kruz - is - a - top - gun。这些标记分配给文档(带有一些关于位置的元数据,但是现在我们不讨论)。如果您想进行部分匹配,可以,但只能在单独的标记上进行,而不是像您希望的那样跨越标记边界。将搜索字符串拆分并构建通配符查询也是一种选择。
另一种选择确实是使用ngramedge_ngram标记过滤器。它将在索引时间创建这些部分标记(如t - to - tom - ... - k - kr - kru - kruz - ...),您只需在搜索中输入“tom kr”即可匹配。但要小心:这将膨胀您的索引(正如您所看到的,它将存储更多的标记),您需要自定义分析器和关于映射的相当多的知识。
通常,(边缘)ngram路线仅适用于自动完成之类的内容,而不适用于索引中的任何文本字段。有几种方法可以解决您的问题,但大多数方法都涉及构建单独的功能来检测拼写错误的单词,并尝试建议正确的术语。

5

尝试创建此JSON

{
"query": {
    "filtered": {
        "query": {
            "bool": {
                "should": [
                    {
                        "wildcard": {
                            "field": {
                                "value": "tom*",
                                "boost": 1
                            }
                        }
                    },
                    {
                        "field": {
                            "brandname": {
                                "value": "kr*",
                                "boost": 1
                            }
                        }
                    },
                ]
            }
        }
    }
}

您可以扩展您的搜索词。
$searchTerms = explode(' ', 'tom kruz');

然后为每个创建通配符。
foreach($searchTerms as $searchTerm) {
//create the new array
}

亲爱的 Costa,感谢您的回答。但是我有一个问题,您确定对单个单词运行通配符搜索将给我匹配多个子字符串的最相关结果吗? - Dinesh Belkare

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接