Aaah, поисковые системы. Захватывающая тема, но я бы предпочел построить что-то с внутренним интеллектом, а не с использованием решения грубой силы. Да - проверка каждой таблицы/столбца в базе данных является грубой силой и может привести к медлительности и ложным срабатываниям.
Позвольте мне представить вам что-то, что я использовал бы вместо этого. С помощью решения ниже каждый столбец, заслуживающий сканирования, должен быть добавлен вручную, но все остальное автоматически. Вот использование:
$e = new SearchEngine();
$e->addTable('users', 'id', 'login'); // table, primary key name, column to be searched in
$e->addTable('users', 'id', 'last_name');
$e->addTable('towns', 'id', 'name');
print_r($e->search('austin')); // we search for exact match for word "austin"
И вот как это было реализовано:
class SearchEngine {
protected $tables = array();
public function addTable($table, $key, $column) {
$this->tables[] = array(
'table' => $table,
'key' => $key,
'column' => $column
);
}
public function search($term) {
$q = array();
foreach ($this->tables as $t) {
list($table, $key, $column) = $t;
$q[] = "
SELECT
$key AS searched_key,
'$key' AS searched_key_name,
'$table' AS searched_table,
'$column' AS searched_column,
$column AS searched_value
FROM $table
WHERE $column = $term
";
}
$sql = implode(' UNION ', $q);
// query the database
// return results
}
} // class SearchEngine
Рассмотрим пример вывода:
searched_key | searched_key_name | searched_table | searched_column | searched_value
-------------+-------------------+----------------+-----------------+---------------
276 | id | users | login | austin
1782 | id | users | last_name | austin
71 | id | towns | name | austin
Из приведенной выше таблицы вы можете выяснить, что фраза «Остина» был найден в таблице users
, колонка login
(первичный ключ 276) и столбец last_name
(первичный ключ 1782).Он также был найден в таблице towns
в столбце name
(первичный ключ 71);
Такой результат поиска может быть достаточным для вас. Или же, вы можете дополнительно обработать список, чтобы выбрать полный ряд из каждой таблицы:
$out = array();
foreach ($rows as $row) {
$sql = "
SELECT * FROM {$row['searched_table']}
WHERE {$row['searched_key_name']} = {$row['searched_key']}
LIMIT 1
";
// query the database
// append result to $out array
}
return $out;
Таким образом, вы будете в конечном итоге с полным результатом поиска (в отличие от промежуточных результатов предыдущей таблицы):
id: 276, login: austin, last_name: Powers, email: [email protected]
id: 1782, login: michael, last_name: austin, email: [email protected]
id: 71, name: austin, state: texas, country: usa
Поскольку текущая реализация ограничена фиксированным оператором сравнения (поле WHERE = значение), вы можете ввести некоторую гибкость здесь. Если да, то оператор поиска должен быть делегирована внешнему классу и вводили в search()
функции:
public function search(SearchOperator $operator, $term) {
...
Тогда SearchOperator
необходимо принимать во внимание при замене WHERE условие с ниже:
WHERE {$operator->toSQL($column, $term)}
Теперь давайте фокус на SearchOperator
реализации. Поскольку реализация оператора предоставляет только один метод, а именно toSQL
, нам не нужен полный класс или даже абстрактный класс. Интерфейс будет достаточно в этом случае:
interface SearchOperator {
public function toSQL($column, $term);
} // interface SearchOperator
И давайте определим несколько реализаций, представляющих =
(равно) и LIKE
операторов:
class Equals implements SearchOperator {
public function toSQL($column, $term) {
return "$column = '$term'";
}
} // class Equals
class Like implements SearchOperator {
public function toSQL($column, $term) {
return "$column LIKE '$term'";
}
} // class Like
Естественно, что любая другая реализация возможно - думать о классах называемых StartsWith, EndsWith или DoesNotContain.
Смотрите обновленный использование решения:
$e = new SearchEngine();
$e->addTable('users', 'id', 'login');
$e->addTable('users', 'id', 'last_name');
$e->addTable('towns', 'id', 'name');
print_r($e->search(new Like(), 'austin%')); // here we search for columns being LIKE 'austin%'
Время, чтобы оставить некоторые заключительные замечания:
- Приведенные выше примеры являются неполными. Для ясности код запроса базы данных был опущен.
- SQL, используемый в примерах, не дезинфицирует входные данные. Я настоятельно призываю вас использовать подготовленные операторы со связанными параметрами, чтобы избежать огромного риска для безопасности.
- Алгоритм поиска, представленный выше, является наивным. Можно выполнить некоторую оптимизацию (т. Е. Группировать запросы, относящиеся к одной и той же таблице). Но не оптимизируйте преждевременно - подождите, пока это не станет реальной проблемой.
Надеясь, что это было полезно.
Я подозреваю, что это можно легко сделать с динамическим SQL. Запросите некоторую мета-таблицу со всеми именами таблиц и именами столбцов, затем пропустите этот набор результатов и запросите таблицы и столбцы, как вы хотите. Не публиковать в качестве ответа, потому что я не уверен, что это возможно в MySQL (я знаю, что подобные трюки работают в Oracle, хотя) – FrustratedWithFormsDesigner 2010-12-03 21:24:12