在 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 6 中的自定义搜索表单:视图/面板还是自定义 sql?