在 Drupal 7 中更改数据集的搜索查询条件

Posted

技术标签:

【中文标题】在 Drupal 7 中更改数据集的搜索查询条件【英文标题】:Alter search query condition for dataset in Drupal 7 【发布时间】:2014-09-12 13:20:01 【问题描述】:

我是 Drupal 的新手,正在尝试构建一个模块,允许管理员使用关键字标记节点,以将节点提升到搜索结果的顶部。

我有一个单独的数据库表用于关键字和相应的节点 ID。此表通过 hook_query_alter 与 search_index 表联合...

function mos_search_result_forcer_query_alter(QueryAlterableInterface &$query) 
    if (get_class($query) !== 'PagerDefault')  //<< check this because this function will mod all queries elsewise
        return;
    

    // create unioned search index result set...
    $index = db_select('search_index', 's');

    $index->addField('s', 'sid');
    $index->addField('s', 'word');
    $index->addField('s', 'score');
    $index->addField('s', 'type');

    $msrfi = db_select('mos_search_result_forcer', 'm');

    $msrfi->addField('m', 'nid', 'sid');
    $msrfi->addField('m', 'keyword', 'word');
    $msrfi->addExpression('(SELECT MAX(score) + m.id / (SELECT MAX(id) FROM mos_search_result_forcer) FROM search_index)', 'score');
    $msrfi->addExpression(':type', 'type', array(':type' => 'node'));

    $index->union($msrfi);

    $tables =& $query->getTables();

    $tables['i']['table'] = $index;

    return $query;

Drupal 然后生成几乎正确的查询...

SELECT 
    i.type AS type, i.sid AS sid, SUM(CAST('10' AS DECIMAL) * COALESCE(( (12.048628015788 * i.score * t.count)), 0) / CAST('10' AS DECIMAL)) AS calculated_score 
FROM (
    SELECT 
        s.sid AS sid, s.word AS word, s.score AS score, s.type AS type 
    FROM 
        search_index s 
    UNION SELECT 
        m.nid AS sid, m.keyword AS word, (
            SELECT 
                MAX(score) + m.id / (SELECT MAX(id) FROM mos_search_result_forcer) 
            FROM 
                search_index
        ) AS score, 'node' AS type 
    FROM 
        mos_search_result_forcer m
) i 
INNER JOIN node n ON n.nid = i.sid 
INNER JOIN search_total t ON i.word = t.word 
INNER JOIN search_dataset d ON i.sid = d.sid AND i.type = d.type 
WHERE (n.status = '1') 
AND( (i.word = 'turtles') )
AND (i.type = 'node') 

/* this is the problem line... */
AND( (d.data LIKE '% turtles %' ESCAPE '\\') )
/* ...end problem line */

GROUP BY i.type, i.sid 
HAVING (COUNT(*) >= '1') 
ORDER BY calculated_score DESC 
LIMIT 10 OFFSET 0

...我需要阅读“问题线”...

AND( (d.data LIKE '% turtles %' ESCAPE '\\') OR (d.sid IN (SELECT nid FROM mos_search_result_forcer)) )

...我可以使用什么钩子来添加 OR 条件?

我不想破解 Drupal 的核心。 我不想更改联合/子查询(不是我的决定)。 我稍后会优化查询 - 功能更重要。

谢谢,聪明的人!

【问题讨论】:

【参考方案1】:

基本原理是获取conditions 数组,循环并找到问题条件的索引,将其删除,然后重新添加相同条件和新条件作为db_or() 的一部分

这是未经测试的,很可能无法逐字使用,但它应该为您提供一个起点:

$conditions =& $query->conditions();
$index = FALSE;
$removed_condition = NULL;

for ($i = 0, $l < count($conditions); $i < $l; $i++) 
  if ($conditions[$i]['field'] == 'd.data' && $conditions[$i]['operator'] == 'LIKE') 
    $index = $i;
    $removed_condition = $condition;

    break;
  


if ($index !== FALSE) 
  unset($conditions[$index]);

  $sub_query = db_select('mos_search_result_forcer')->fields('mos_search_result_forcer', array('nid'));
  $new_condition = db_or()
    ->condition('d.data', $removed_condition['value'], 'LIKE')
    ->condition('d.sid', $sub_query, 'IN');

  $query->condition($new_condition);

【讨论】:

谢谢!该代码实际上与我正在试验的代码非常相似;但是,问题是我需要修改的条件被添加到查询对象 after hook_query_alter 被调用。我希望在调用查询的执行方法之前可能会调用另一个钩子,或者在查询对象向自身添加条件之前调用的钩子? hook_query_alter() 之后查询被更改的可能性很小(实际上我认为不可能)......可能发生的是另一个模块也实现了hook_query_alter(),它的实现只是在你的之后运行。您可以通过实现 hook_module_implements_alter() 并将模块的 hook_query_alter() 钩子移动到列表末尾来解决这个问题 再次感谢您!我可以在 var_dumping 查询对象时看到条件。查询 arg(传递给 hook_query_alter)的类型为 PagerDefault,它具有类型为 SearchQuery 的受保护属性 $query。受保护的SearchQuery 属性具有我需要修改的条件数组。有没有办法像这样修改嵌套查询的条件? 有趣的@chaseisabelle,以前从未真正尝试过。我在PagerDefault 上看不到任何可以让您访问基础查询的内容。我认为像您所做的那样扩展该课程是正确的方法。顺便说一句,如果您可以从问题中取出解决方案并将其放入答案中,那就太好了(回答您自己的问题很好,甚至受到鼓励)。这样人们就不会错过了。如果您这样做,请告诉我,以便我之后删除此答案 谢谢,@Clive。您对hook_module_implements_alter 的建议对您有很大帮助!添加了我的解决方案作为答案。【参考方案2】:

感谢@Clive 和hook_module_implements_alter 的一些有用建议,以及大量的反复试验,我终于解决了这个问题。

这是最终代码...

function mos_search_result_forcer_module_implements_alter(&$imps, $hook) 
    if ($hook !== 'query_alter' || !array_key_exists('mos_search_result_forcer', $imps)) 
        return;
    

    $imp = $imps['mos_search_result_forcer'];

    unset($imps['mos_search_result_forcer']);

    $imps['mos_search_result_forcer'] = $imp;


function mos_search_result_forcer_query_alter(QueryAlterableInterface &$query) 
    if (get_class($query) !== 'PagerDefault')  //<< check this because this function will mod all queries elsewise
        return;
    

    // create unioned search index result set...
    $index = db_select('search_index', 's');

    $index->addField('s', 'sid');
    $index->addField('s', 'word');
    $index->addField('s', 'score');
    $index->addField('s', 'type');

    $msrfi = db_select('mos_search_result_forcer', 'm');

    $msrfi->addField('m', 'nid', 'sid');
    $msrfi->addField('m', 'keyword', 'word');
    $msrfi->addExpression('(SELECT MAX(score) + m.id / (SELECT MAX(id) FROM mos_search_result_forcer) FROM search_index)', 'score');
    $msrfi->addExpression(':type', 'type', array(':type' => 'node'));

    $index->union($msrfi);

    $tables =& $query->getTables();

    $tables['i']['table'] = $index;

    // needs special "or" condition to keep from filtering out forced resutls...
    class MSRFPagerDefaultHelper extends PagerDefault  //<< override to gain access to protected props
        static function msrfHelp(PagerDefault &$pagerDefault) 
            $searchQuery =& $pagerDefault->query;

            MSRFSearchQueryHelper::msrfHelp($searchQuery);
        
    

    class MSRFSearchQueryHelper extends SearchQuery  //<< override to gain access to protected props
        static function msrfHelp(SearchQuery &$searchQuery) 
            $conditions =& $searchQuery->conditions;

            $condition = db_or()->condition($conditions)->condition('d.sid', db_select('mos_search_result_forcer')->fields('mos_search_result_forcer', array('nid')), 'IN');

            $searchQuery->conditions = $condition;
        
    

    MSRFPagerDefaultHelper::msrfHelp($query);

    return $query; //<< i don't think this is needed as var is reffed - just for good measure, i guess

【讨论】:

以上是关于在 Drupal 7 中更改数据集的搜索查询条件的主要内容,如果未能解决你的问题,请参考以下文章

Drupal 7 - 暴露过滤器 - 全文搜索

Drupal 7搜索API挂钩注册的回调类没有被触发

Drupal 6 中的自定义搜索表单:视图/面板还是自定义 sql?

如何在 Drupal 7 Search API 模块中将内容实体标记为脏

Drupal 7 OR条件具有一系列AND条件

如果大型查询集的条件非常慢