Регулярное выражение для пользовательского поиска

sanu0074

Новичок
Сделал поиск в админке, добавил возможность пользователю искать совпадения в конкретных колонках таблицы с контентом, например он жмет на нужные кнопки и javascripto'm в строке поиска формируется подобное:
Код:
:name=первая && (:id>40 && :sect=content)
Рашифровка:
:name - это имя поля в таблице

&& и || - эквивалент AND и OR
скобки самособой понятно....

В итоге когда эта строка попадает на сервер, скрипт должен ее разбить и превратить в следующее,
будет строка такого вида:
Код:
name LIKE ? && (id> && sect LIKE ?)
И массив такого вида:
Код:
array('первая',40,'content')
Как это сделать? я голову ломаю, не могу составить регулярку кот все сделает. Тут следует понять, что данные могут меняться, скобок может не быть или быть несколько, знаки использую только >,<,= покачто других не придумал, знак "=" заменяем на "LIKE ?". Помогите решить задачку пожалуйста? И хочу видеть ваши замечания, насколько это тупо..) p.s. но хочу сделать именно так)
 

Василий М.

Новичок
так не правильно делать
не надо писать новый промежуточный язык, это никому не нужно, сложно и для этих целей не ругярками надо пользоваться
 
Последнее редактирование:

AnrDaemon

Продвинутый новичок
Есть же готовые решения для полнотекстового поиска. Сфинкс тот же.
 

ksnk

прохожий
sanu0074, язык получился перегруженный. Например фраза `:name=` - это такая обязательная конструкция?
Какие варианты поиска бывают? Нужно их всех выписать и определить минимальный набор слов и знаков препинаний, чтобы можно было их отличать друг от друга.
Например:
Код:
:первая(:id>40 & :sect=content)
Это конструкция предписывает искать в столбце `первая` начиная с id>40 и искать `content`.
Условий можно вставить в строку сколько угодно.

Если я правильно понял условия, то фраза может выглядеть и так
Код:
:id(>40):первая(content | content2):вторая(>10&<60)
 

sanu0074

Новичок
Если я правильно понял условия, то фраза может выглядеть и так
Код:
:id(>40):первая(content | content2):вторая(>10&<60)
нет, так не может, в таком случае, если я правильно понял, она например будет такая:
Код:
(:id>40) && (:первая=content || :первая=content2) && (:вторая>10 && :вторая<60)
 

ksnk

прохожий
sanu0074, Я предлагал более компактный вариант языка. Но это не особенно важно.
Нужно определить что будут искать. Нужно определиться какие названия столбцов будут. Что делать, если в данных для поиска или названиях есть символы &&, скобки и пробелы. В принципе - можно такие данные обрамлять кавычками. См С-шный квотинг или паскальное удвоение кавычек.
Получившийся результа будет довольно сложен для парсинга. Вообще говоря, не для одной регулярки.
Но сначала нужно зафиксировать входной язык. А для этого - придумать все возможные хотелки пользователя и попытаться вместить их в языковые конструкции.
 

sanu0074

Новичок
ksnk, мне советовали лексер, в php он есть, но я не понял как его использовать в этом случае
 

AnrDaemon

Продвинутый новичок
Если так хочется делать машинный поиск, посмотри в сторону LDAP и его синтаксиса. Он намного лучше парсится и не так сложно читается.
 

sanu0074

Новичок
вот код того что мне надо,
PHP:
$str = ':name=первая && (:id>40 && :sect=content)';
$param = array();

preg_match_all('/:([\w_]+)\s*([<>=]{1,2})\s*(["\'].*?["\']|[^\s()]+)/', $str, $out);
for($i=0; $i<count($out[0]); $i++){
    $key  = $out[1][$i];
    $sign = $out[2][$i];
    $val  = $out[3][$i];

    $str  = str_replace($out[0][$i], $key.(($sign == '=') ? ' LIKE' : $sign).' ?', $str);
    $param[] = $val;
}

echo $str;
print_r($param);
но немного не так работает, в результирующей строке:
Код:
name LIKE ? && (:id>40 && sect LIKE ?)
- здесь вместо ":id>40" - должно быть просто "id>" и в массиве $param всего два элемента, а должно быть три и вторым должен быть параметр "40".
Пробовал такую строку исходную -
Код:
:name=первая && :sect=abc
- работает
но если в строке уже есть знак > и число то все работает не так
Например при такой исходной строке
Код:
:name=первая && :sect>40
- результирующая выглядит так -
Код:
name LIKE ? && :sect>40
и массив так:
Код:
array(1) {
  [0]=>
  string(12) "первая"
}
т.е. такой же косячок что и с первоначальным вариантом.
Это единственное что осталось подправить, и покачто для той цели что мне надо - меня устроит...
 

AnrDaemon

Продвинутый новичок
Тебе нужен токенизатор, тут одним регэкспом на все случаи жизни не обойдёшься. (Верне, обойтись можно, но пользы от этого не будет - работать будет во столько же раз дольше, сколько альтернатив будет в регэкспе.)
 

sanu0074

Новичок
Тебе нужен токенизатор, тут одним регэкспом на все случаи жизни не обойдёшься. (Верне, обойтись можно, но пользы от этого не будет - работать будет во столько же раз дольше, сколько альтернатив будет в регэкспе.)
насчет этого не сказал бы, работает во всех случаях, их тут несколько, единственный косяк в том что я выше выложил, тут беда со знаками > и < в регулярке. немогу решить досихпор
 

ksnk

прохожий
Код:
name LIKE ? && (id> ? && sect LIKE ?)Array
(
    [0] => первая
    [1] => 40
    [2] => content
)
Вот такой вывод дает скрипт. Что в нем неправильно?
 
Сверху