Согласно мой предыдущий вопрос рекрутер программного обеспечения может ввести логическую текстовую строку Такие какC++ AND ((UML OR Python) OR (not Perl))
, которую я переведу на SELECT * FROM candidates WHERE skill=C++ AND ((skill=UML OR skill=Python) OR (not skill=Perl))
.
[Обновление] Я выделил Такие как, потому что некоторые ответы, кажется, думают, что меня интересует только этот запрос. Это только пример. Я ищу общее решение, закодированное в PHP. Может регулярное выражение? Просто некоторый код, который находит каждый подтермин запроса, чтобы я мог запрашивать подтермины по отдельности. [/Обновлять]
Я мог бы COUNT(*)
указать количество совпадений, но мне также было бы очень интересно узнать, насколько каждый «подпункт» (если это правильный термин) запроса повлиял на результат.
Например. могло быть 200 кандидатов со знанием C++, но 50 не подходили, потому что у них не было опыта работы ни с UML, ни с Python.
Итак, используя либо PHP (и rexex?), либо MySql, как я могу разбить это, чтобы увидеть, какие части поискового запроса влияют на результат?
То есть разбить skill=C++ AND ((skill=UML OR skill=Python) OR (not skill=Perl))
на COUNT(*) WHERE skill=C++
и `СЧИТАТЬ (*) ГДЕ (навык=UML ИЛИ навык=Python) и т. д.
Я не знаю, есть ли в MySql какой-то EXPLAIN
для этого, но подозреваю, что нет, так что мне придется разбить SELECT
, как описано, и COUNT
каждый подпункт отдельно.
Я надеюсь, что объяснил это ясно; если нет, попросите разъяснений. я просто не знаю с чего начать
Лолкс. Да, но в этом и суть вопроса - как разбить сложный запрос на составляющие его условия?
Вам нужно будет написать парсер. Это задача не на один вечер.
Это несколько оффтоп и, возможно, глупый вопрос, но почему кто-то хочет, чтобы человек нет имел опыт в какой-то теме?
покажите нам, что вы пробовали, чтобы мы могли вам помочь. мы здесь не для того, чтобы писать код для вас
Ваш лучший выбор — это таблица сопоставления, в которой каждый навык находится в отдельной записи, и вы меняете ввод на что-то более стандартное, например C++ UML Python -Perl
, где -
подразумевает НЕ. Это используется большинством поисковых систем, и большинство пользователей понимают это. Принуждение пользователя к созданию ввода, например C++ AND ((UML OR Python) OR (not Perl))
, создаст ужасный пользовательский опыт, и большинство пользователей этого не поймут. Затем вы можете легко проверить, соответствует ли запись в вашей таблице каждому критерию, сгенерировав некоторый `MAX (skill = 'criteria') и сложив их вместе, чтобы увидеть, соответствует ли он количеству критериев.
Извините, но конечный пользователь попросил меня указать этот синтаксис. Вероятно, потому что это синтаксис поиска Google
@Mawg, награда заканчивается завтра :) У вас была возможность оценить ответы? (Я отредактировал свой день назад или около того, чтобы добавить логическое разделение, на случай, если вы этого не заметили)
Ваш вопрос мне интересен, я заметил, что вы хотите регулярное выражение и только php. в любом случае, я ответил на него, и это мой первый раз, когда я пишу полный сценарий в качестве ответа в stackoverflow или на любом другом форуме. Я не тестировал сценарий, но он должен работать. Ваш вопрос хорош, но не подходит для этого сообщества, так как участники здесь для помощи и общих идей, а не для написания сценариев.
Как насчет использования встроенных возможностей поиска MySQL полный текст? Возврат автоматически ранжируется с лучшими совпадениями вверху.
Вы можете создать новый столбец, содержащий все навыки и умения кандидата. Затем поиск по этому полю даст ранжированные результаты.
SELECT
count(*),
sum(skill=C++),
sum(skill=UML),
sum(skill=Python),
sum(not skill=Perl)
FROM candidates WHERE TRUE
AND skill=C++
AND (FALSE
OR (FALSE
OR skill=UML
OR skill=Python)
OR (not skill=Perl)
)
Хотя этот код может дать ответ на вопрос, предоставление дополнительного контекста относительно того, почему и/или как этот код отвечает на вопрос, повышает его ценность в долгосрочной перспективе.
На самом деле, как ОП, я должен сказать, что это не отвечает на вопрос. Он отвечает на пример, который я привел в вопросе, но будет полезно только общее решение. Возможно, мне нужно отредактировать вопрос, чтобы уточнить, что
SELECT skill, COUNT(*) FROM tbl
и дополнений.Чтобы стать более интересным, просто удалите скобки, ИЛИ и И из текстовой строки, чтобы получить различные упомянутые навыки. Затем отображать только те.
Но ни один из них не обрабатывает (UML OR Python)
или несмежные вещи, такие как (C++ and not Perl)
. Во всяком случае, сколько счетов вы ожидаете от вашего примера? Также есть (UML OR Python) AND C++
и еще несколько.
Даже не думайте парсить через SQL; использовать клиентский язык. Или задать вопрос кандидатам.
Подсказки кода
В Perl можно сделать:
$str =~ s{[()]|AND|OR|NOT}{ }ig;
$str =~ s{ +}{ }g;
@skills = split(' ', $str);
Код PHP будет использовать preg_replace
и explode
, но в остальном будет похож. В вашем примере C++ AND ((UML OR Python) OR (not Perl))
станет массивом ['C++', 'UML', 'Python', 'Perl']
Нам нужен метод разделения условий. Однако мы не можем разделить AND и OR на равные, потому что И имеют более высокий приоритет, чем ИЛИ..
Итак, в таком примере:
Cond1 AND Cond2 OR Cond3
Мы не можем разделить на AND|OR
, потому что нам не хватает Cond1 AND Cond2
в целом.
Итак, первое, что нужно сделать, это добавить дополнительные круглые скобки (с регулярными выражениями) там, где это необходимо, чтобы следующий алгоритм правильно разделил условия. В предыдущем примере это было бы (Cond1 AND Cond2) OR Cond3
.
После настройки мы используем регулярное выражение для получения условий для текущего уровня. Нам нужно использовать рекурсивные регулярные выражения, чтобы обнаружить открывающую/закрывающую скобку.
Каждое условие сохраняется в массиве, а затем отправляется на обработку (рекурсивно). Это связано с тем, что некоторые условия могут быть сложными и иметь вложенные условия.
Все эти условия и подусловия хранятся в массиве.
Когда у вас есть все условия (и подусловия), у вас есть две альтернативы для монтирования SQL.
Первый вариант — это один запрос без предложения WHERE и одна сумма SUM для каждого условия. Это, вероятно, лучше всего, если в таблице не так много строк.
Второй вариант — запуск нескольких запросов SELECT count(*)
со всеми условиями.
Я оставляю здесь php-код. Я также добавил возможность настроить максимальное количество уровней гнезда при разделении условий.
У вас есть демо на Идеоне, здесь.
<?php
$conditions = 'C++ AND ((UML OR Python) OR (not Perl))';
// Other tests...
//$conditions = "C++ AND Python OR Perl";
//$conditions = "C++ AND Python OR Perl OR (Perl AND (Ruby AND Docker AND (Lisp OR (C++ AND Ada) AND Java)))";
///////// CONFIGURATION /////////
$maxNest = 0; // Set to 0 for unlimited nest levels
/////////////////////////////////
print "Original Input:\n";
print $conditions . "\n\n";
// Add implicit parenthesis...
// For example: `A AND B OR C` should be: `(A AND B) OR C`
$addParenthesis = '/(?|(((?:\bNOT\b\s*+)?+[^)(\s]++|(?:\bNOT\b\s*+)?+[(](?:\s*+(?2)\s*+)*+[)])(?:\s*+\bAND\b\s*+((?2)))++)(?=\s*+\bOR\b\s*+)|\s*+\bOR\b\s*+\K((?1)))/im';
while (preg_match($addParenthesis, $conditions)) {
$conditions = preg_replace($addParenthesis, '(\1)', $conditions);
}
print "Input after adding implicit parenthesis (if needed):\n";
print $conditions . "\n\n";
// Optional cleanup: Remove useless NOT () parenthesis
$conditions = preg_replace('/[(]\s*((?:NOT\s*)?+(\S+))\s*[)]/i', '\1', $conditions);
// Optional cleanup: Remove useless NOT NOT...
$conditions = preg_replace('/\bNOT\s+NOT\b/i', '', $conditions);
$list_conditions = [$conditions];
function split_conditions($input, $level = 0) {
global $list_conditions, $maxNest;
if ($maxNest > 0 && $level >= $maxNest) { return; }
// If it is a logic operator, skip
if ( preg_match('/^\s*(?:AND|OR)\s*$/i', $input) ) {
return;
}
// Add condition to the list:
array_push($list_conditions, $input);
// Don't go on if this is a single filter
if ( preg_match('/^\s*(?:NOT\s+)?+[^)(\s]+\s*$/i', $input) ) {
return;
}
// Remove parenthesis (if exists) before evaluating sub expressions
// Do this only for level > 0. Level 0 is not guaranteed to have
// sorrounding parenthesis, so It may remove wanted parenthesis
// such in this expression: `(Cond1 AND Cond2) OR (Cond3 AND Cond4)`
if ($level > 0) {
$input = preg_replace('/^\s*(?:NOT\b\s*)?+[(](.*)[)]\s*$/i', '\1', $input);
}
// Fetch all sub-conditions at current level:
$next_conds = '/((?:\bNOT\b\s*+)?+[^)(\s]++|(?:\bNOT\b\s*+)?+[(](?:\s*+(?1)\s*+)*+[)])/i';
preg_match_all($next_conds, $input, $matches);
// Evaluate subexpressions
foreach ($matches[0] as $match) {
split_conditions($match, $level + 1);
}
}
split_conditions($conditions);
// Trim and remove duplicates
$list_conditions = array_unique(array_map(function($x){
return preg_replace('/^\s*|\s*$/', '', $x);
}, $list_conditions));
// Add columns
$list_conditions = array_map(function($x){
return preg_replace('/([^\s()]++)(?<!\bAND\b)(?<!\bOR\b)(?<!\bNOT\b)/i', "skill='$1'", $x);
}, $list_conditions);
print "Just the conditions...\n\n";
print_r($list_conditions);
print "\n\n";
print "Method 1) Single query with multiple SUM\n\n";
$sum_conditions = implode(",\n", array_map(function($x){
return " SUM( $x )";
}, $list_conditions));
$sumSQL = "SELECT\n$sum_conditions\nFROM candidates;";
print $sumSQL . "\n\n";
print "Method 2) Multiple queries\n\n";
$queries = implode("\n", array_map(function($x){
return "SELECT count(*) from candidates WHERE $x;";
}, $list_conditions));
print $queries . "\n\n";
К вашему сведению @mawg, я изменил свой ответ, чтобы дать вам код, который должен иметь возможность разделять любые условия на любом уровне вложенности, а также должен заботиться о приоритете И / ИЛИ. Код также выведет две опции для SQL.
Привет, это не большое дело, берегись
$sqlresult =array ('php, html, php, c++, perl');
//that is array result from MySQL and now we need to count every term alone only in php
//now I create this
function getcount ($word, $paragraphp){
if (preg_match("/$word/i", $paragraph))
$count = 1;
else
$count = 0;
return $count;
}
foreach ( $sqlresult as $key ) {
$finalresult = array ();
$finalresult['$key'] += getcount($key, $key);
}
//now retrieve results as following
$php = " results for php word is $finalresult[php]";
$perl = "results for perl word is $finalresult[perl]";
echo $php;
echo $perl;
Если у вас есть абзац с большим количеством слов, вы должны сначала преобразовать его в массив с функцией взрыва php и начать шаги, как указано выше.
В большом проекте, который не подходит, вам нужна хорошая замена MySQL. В этом случае я предлагаю поиск SPHINX После запуска запроса в SPHINX Запустите этот запрос
SHOW META;
Это даст каждое слово в вашем поиске с количеством попаданий, для получения более подробной информации проверьте этот http://sphinxsearch.com/docs/current/sphinxql-show-meta.html
Хотя это и не самое элегантное решение, функция WITH ROLLUP
Mysql может оказаться полезной. См. https://dev.mysql.com/doc/refman/8.0/en/group-by-modifiers.html
В самом простом методе вы можете написать этот запрос, чтобы зафиксировать уникальные навыки:
SELECT skill, COUNT(skill) AS mycount
FROM cands
GROUP BY skill WITH ROLLUP
Это вернет общее количество всех навыков со строкой NULL
внизу с общим количеством, например:
|skill |mycount | |--------|---------| |C++ | 2 | |Java | 3 | |Python | 4 | |NULL | 9 |
Добавляя логические операции, можно получить более сложный результат:
SELECT skill, COUNT(skill) AS mycount, SUM(IF(skill='C++' || skill='Python', 1, 0)) AS CorPython
FROM cands
GROUP BY skill WITH ROLLUP
С этим вторым вариантом столбец CorPython
будет суммировать — в последней NULL
строке — общее количество людей с «C или Python». Вы можете сделать эту логическую секцию настолько сложной, насколько это необходимо.
|skill |mycount |CorPython | |--------|---------|-----------| |C++ | 2 | 2 | |Java | 3 | 0 | |Python | 4 | 4 | |NULL | 9 | 6 | <-- This is the value you want (6)
Вы можете просто запустить отдельный запрос для отдельных условий, которые вас интересуют, верно?