这是从 PHP 使用 Sphinx 的正确方法吗?

Posted

技术标签:

【中文标题】这是从 PHP 使用 Sphinx 的正确方法吗?【英文标题】:Is this the right way to use Sphinx from PHP? 【发布时间】:2012-06-19 12:27:27 【问题描述】:

我只是从 Sphinx 开始。到目前为止,我成功安装了它,在我的 mysql 数据库上获得了一个名为 profiles 的表,并且能够使用 php API 获得正确的结果。我正在使用 CodeIgniter,因此我将默认的 PHP API 包装为 CodeIgniter 库。

无论如何,这就是我的代码的样子:

$query = $_GET['q'];
$this->load->library('sphinxclient');
$this->sphinxclient->setMatchMode(SPH_MATCH_ANY);
$result = $this->sphinxclient->query($query);

$to_fetch = array();
foreach($result['matches'] as $key => $match) 
  array_push($to_fetch, $key);

数组$to_fetch 包含匹配表行的ID。现在我可以使用典型的 MySQL 查询来让所有相关用户显示在搜索页面上,如下所示:

$query = 'SELECT * FROM profiles WHERE id IN('. join(',', $to_fetch) . ')';

我的问题是:

    这是正确的方法吗?或者是否有一个默认的“Sphinx 方式”可以提高性能。

    其次,我现在得到的只是匹配表行的 id。我还想要列中匹配的部分文本。例如,如果某人搜索关键字dog,而profiles 表中的用户在其about 列中包含以下文本:

    I like dogs. I also like ice cream.

我希望 Sphinx 回来:

I like <strong>dogs</strong>. I also like ice cream.

我该怎么做?我尝试使用 buildExcerpts() 函数,但无法正常工作。

编辑

这就是我现在获得摘录的方式:

// get matched user ids
$to_fetch = array();
foreach($result['matches'] as $key => $match) 
  array_push($to_fetch, $key);


// get user details of matched ids
$members = $this->search_m->get_users_by_id($to_fetch);

// build excerpts
$excerpts = array();
foreach($members as $member) 

    $fields = array(
        $member['about'],
        $member['likes'],
        $member['dislikes'],
        $member['occupation']
    );

    $options = array(
        'before_match'      => '<strong class="match">',
        'after_match'       => '</strong>',
        'chunk_separator'   => ' ... ',
        'limit'             => 60,
        'around'            => 3,
    );

    $excerpt_result = $this->sphinxclient->BuildExcerpts($fields, 'profiles', $query, $options);
    $excerpts[$member['user_id']] = $excerpt_result;


$excerpts_to_return = array();
foreach($excerpts as $key => $excerpt) 
    foreach($excerpt as $v) 
        if(strpos($v, '<strong class="match">') !== false) 
            $excerpts_to_return[$key] = $v;
        
    

如您所见,我在 4 个不同的 mysql 列中搜索每个查询:

about
likes
dislikes
occupation

因此,我不知道 4 列中的哪一列包含匹配的关键字。它可以是其中任何一个,甚至不止一个。所以我别无选择,只能通过BuildExcerpts()函数运行所有4列的内容。

即便如此,我也不知道BuildExcerpts()&lt;strong class="match"&gt; 标签一起返回了哪一个。因此,我对BuildExcerpts() 返回的所有值运行stpos 检查,以最终获得正确的摘录并将其映射到其个人资料所属的用户。

考虑到我需要匹配 4 个不同列的内容的情况,您有没有比这更好的方法?

【问题讨论】:

【参考方案1】:

是的,这看起来不错。要记住从 Mysql 返回的行可能不会按照 sphinx 的顺序。

有关如何使用 FIELD() 的信息,请参阅 sphinx 网站上的常见问题解答,但我个人喜欢将 sphinx 中的行放入关联数组中,然后循环遍历我列出的 sphinx 并从数组中获取行。以内存为代价完全避免了排序阶段!

至于突出显示,是的,请坚持使用 buildExcerpts - 这就是这样做的方法。


编辑添加,这个演示 http://nearby.org.uk/sphinx/search-example5-withcomments.phps 演示从 mysql 获取行和在应用程序中“排序”。并建摘录。

【讨论】:

关于订单,为什么不用FIND_IN_SET函数呢?像这样:SELECT * FROM profiles WHERE id IN (7,10,5,3,8) ORDER BY FIND_IN_SET(id, '7,10,5,3,8') 您可以使用 find_in_set,其行为与 Field() 几乎相同。不知道字符串解析是否使其变慢。但从更一般的意义上说,我发现最好避免排序 - 特别是在 mysql 中 - 这可能最终会进行文件排序。基本上,在我们的旧服务器上,使用 mysql 进行排序时每秒可以支持大约 50 个查询。将排序转移到应用程序(实际上完全避免了排序)使我们每秒可以处理接近 120 个查询。 (使用 ab 对前端应用程序进行测试) @barryhunter 非常感谢您的回复巴里。我用我目前获得摘录的方式更新了我的问题。请让我知道这是否是一个好方法。这样做感觉真的很脏,因为我必须将所有 4 列提供给 BuildExcerpts() 函数,然后在此之上运行 strpos 检查该函数返回的所有值。并在一个循环中为每个返回的匹配项执行所有这些操作。 一般来说没问题,但可以在选项中添加“allow_empty”。这样很容易检查哪个匹配,因为不匹配将是空的。但是,如果您只是显示摘录,那么它来自哪个并不重要,因此只需将一个字符串转发给函数即可。也可以只使用一个 BuildExcerpts 调用,而不是每个结果一个。减少 API 往返次数。 避免使用 field() 的好技巧......没有考虑到这一点。

以上是关于这是从 PHP 使用 Sphinx 的正确方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

这是从片段中获取字符串资源的正确方法吗?

这是从 Firebase 检索数据的正确方法吗?

这是拟合从python中的高斯分布生成的数据的正确方法吗?

sphinx全文检索之PHP使用教程

这是从iPhone删除联系人的正确方法吗?

这是使用 Doctrine2 的 WHERE IN 表达式处理有序数组的正确方法吗?