2014-09-12 2 views
1

Я очень новичок в Drupal и пытаюсь создать модуль, который позволит администраторам отмечать узлы ключевыми словами, чтобы повысить количество узлов в верхней части результатов поиска.Изменение условия поиска для набора данных в Drupal 7

У меня есть отдельная таблица БД для ключевых слов и соответствующих идентификаторов узлов. Эта таблица UNIONed с таблицей search_index через hook_query_alter ...

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))) 

... какой крюк я могу использовать, чтобы добавить это условие ИЛИ?

  • Я не хочу взламывать ядро ​​Drupal.
  • Я не хочу менять союз/подзапросы (не мое решение).
  • Я буду оптимизировать запросы позже - функциональность важнее.

Спасибо, умные люди!

ответ

0

Благодаря некоторые полезные предложения от @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 
} 
0

Основной принцип заключается в том, чтобы захватить 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); 
} 
+0

спасибо! Этот код на самом деле очень похож на код, который я экспериментировал; однако проблема заключается в том, что условие, которое мне нужно изменить, добавляется в объект запроса * после вызова hook_query_alter. Я надеялся, что может быть еще один крючок, который вызывается непосредственно перед вызовом метода запроса, или крюк, который вызывается непосредственно перед тем, как объект запроса добавляет к нему условия? – chaseisabelle

+1

Очень маловероятно (возможно, на самом деле я думаю), что запрос изменяется после 'hook_query_alter()' ... что, вероятно, происходит, еще один модуль также реализует 'hook_query_alter()', и его реализация выполняется только после вашего. Вы можете обойти это, реализовав ['hook_module_implements_alter()'] (https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_module_implements_alter/7) и перемещая модуль ' hook_query_alter() 'hook в конец списка – Clive

+0

Еще раз спасибо! Я могу видеть условие, когда var_dumping объект запроса. Аргумент запроса (переданный в 'hook_query_alter') имеет тип' PagerDefault', который имеет защищенное свойство '$ query' типа' SearchQuery'. У защищенного свойства 'SearchQuery' есть массив условий, который мне нужно изменить. Есть ли способ изменить условия вложенного запроса? – chaseisabelle

Смежные вопросы