From 01cfd53c44e297f7f2932f814a734446631307c0 Mon Sep 17 00:00:00 2001 From: yamamoto Date: Thu, 12 May 2022 00:26:24 +0900 Subject: [PATCH 1/6] refactor - evoSearch.snippet.php --- .../snippets/evoSearch/evoSearch.snippet.php | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/assets/snippets/evoSearch/evoSearch.snippet.php b/assets/snippets/evoSearch/evoSearch.snippet.php index e4df58d..8210a28 100644 --- a/assets/snippets/evoSearch/evoSearch.snippet.php +++ b/assets/snippets/evoSearch/evoSearch.snippet.php @@ -1,13 +1,27 @@ [+stat_request+] ничего не найдено. Смягчите условия поиска'; +if (!isset($params['noResult'])) { + $noResult = 'По запросу [+stat_request+] ничего не найдено. Смягчите условия поиска'; +} else { + $noResult = $params['noResult']; +} $txt_original = ''; include_once('snippet.class.php'); -$min_length = (isset($params['minlength']) && (int)$params['minlength'] > 0) ? (int)$params['minlength'] : 2; +if (!isset($params['minlength']) || (int)$params['minlength'] <= 0) { + $min_length = 2; +} else { + $min_length = (int)$params['minlength']; +} $eSS = new evoSearchSnippet($modx, $params, $min_length); if (isset($_GET[$eSS->search_field]) && $_GET[$eSS->search_field] != '') { @@ -18,7 +32,7 @@ //получаем массив подходящих под условие поиска id $ids = $eSS->makeSearch(); $ids[] = 4294967295; - if ($eSS->action == 'ids') {//работаем в режиме ids - сразу возвращаем ids + if ($eSS->action === 'ids') {//работаем в режиме ids - сразу возвращаем ids $modx->setPlaceholder("evoSearchIDs", $ids); if ($eSS->params['output'] && $eSS->params['output'] == '1') { $output = implode(',', $ids); @@ -44,11 +58,15 @@ } $params = array_merge($params, $DLparams); $output .= $modx->runSnippet("DocLister", $params); - if ($eSS->params['show_stat'] == '1') { + if ($eSS->params['show_stat'] == 1) { $output = $eSS->getSearchResultInfo() . $output; } } - $output = $output != '' ? $output : (!isset($_REQUEST[$eSS->search_field]) ? '' : '
' . $eSS->parseNoresult($noResult) . '
'); + if ($output == '') { + if (isset($_REQUEST[$eSS->search_field])) { + $output = '
' . $eSS->parseNoresult($noResult) . '
'; + } + } } } return $output; From f2dba3d86cd5d8b6a8489dc7ccd78f880df7553d Mon Sep 17 00:00:00 2001 From: yamamoto Date: Thu, 12 May 2022 00:52:09 +0900 Subject: [PATCH 2/6] code cleanup, indent and white spaces, etc... --- assets/snippets/evoSearch/snippet.class.php | 651 ++++++++++---------- 1 file changed, 324 insertions(+), 327 deletions(-) diff --git a/assets/snippets/evoSearch/snippet.class.php b/assets/snippets/evoSearch/snippet.class.php index 949b424..f250b0b 100644 --- a/assets/snippets/evoSearch/snippet.class.php +++ b/assets/snippets/evoSearch/snippet.class.php @@ -1,272 +1,256 @@ -modx = $modx; - $this->params = $params; - $this->search_field = isset($params['search_field']) ? $params['search_field'] : 'search'; - $this->phpmorphy_dir = MODX_BASE_PATH . 'assets/lib/phpmorphy'; - $this->dict_dir = $this->phpmorphy_dir . '/dicts'; - $this->dicts = array('rus', 'eng'); - -} + public function __construct($modx, $params) + { + $this->modx = $modx; + $this->params = $params; + $this->search_field = isset($params['search_field']) ? $params['search_field'] : 'search'; + $this->phpmorphy_dir = MODX_BASE_PATH . 'assets/lib/phpmorphy'; + $this->dict_dir = $this->phpmorphy_dir . '/dicts'; + $this->dicts = array('rus', 'eng'); + } -public function init($min_length = 2, $ext_content_field = 'content_with_tv', $ext_content_index_field = 'content_with_tv_index') -{ - $this->id = isset($this->params['id']) ? $this->params['id'] : 0; - $this->content_table = $this->modx->getFullTableName("site_content"); - $this->search_table = $this->modx->getFullTableName("evosearch_table"); - $this->ext_content_field = $ext_content_field; - $this->ext_content_index_field = $ext_content_index_field; - $this->action = isset($this->params['action']) ? $this->params['action'] : ''; - $this->setDefault( - array( - 'display' => '20', - 'show_stat' => '1', - 'extract' => '1', - 'statTpl' => '
По запросу [+stat_request+] найдено всего [+stat_total+]. Показано [+stat_display+], c [+stat_from+] по [+stat_to+]
', - 'rel' => '0.01', - 'min_length' => $min_length, - 'dedug' => '0' + public function init($min_length = 2, $ext_content_field = 'content_with_tv', $ext_content_index_field = 'content_with_tv_index') + { + $this->id = isset($this->params['id']) ? $this->params['id'] : 0; + $this->content_table = $this->modx->getFullTableName("site_content"); + $this->search_table = $this->modx->getFullTableName("evosearch_table"); + $this->ext_content_field = $ext_content_field; + $this->ext_content_index_field = $ext_content_index_field; + $this->action = isset($this->params['action']) ? $this->params['action'] : ''; + $this->setDefault( + array( + 'display' => '20', + 'show_stat' => '1', + 'extract' => '1', + 'statTpl' => '
По запросу [+stat_request+] найдено всего [+stat_total+]. Показано [+stat_display+], c [+stat_from+] по [+stat_to+]
', + 'rel' => '0.01', + 'min_length' => $min_length, + 'dedug' => '0' ) - ); - $this->min_length = $this->params['min_length']; - $this->stemmer = $this->getStemmer(); - return $this; -} + ); + $this->min_length = $this->params['min_length']; + $this->stemmer = $this->getStemmer(); + return $this; + } -public function setDefault($param, $default = '') -{ - if (is_array($param)) { - foreach ($param as $p => $v) { - $this->params[$p] = isset($this->params[$p]) ? $this->params[$p] : $v; + public function setDefault($param, $default = '') + { + if (is_array($param)) { + foreach ($param as $p => $v) { + $this->params[$p] = isset($this->params[$p]) ? $this->params[$p] : $v; + } + } else { + $this->params[$param] = isset($this->params[$param]) ? $this->params[$param] : $default; } - } else { - $this->params[$param] = isset($this->params[$param]) ? $this->params[$param] : $default; + return $this; } - return $this; -} -public function makeWordsFromText($text) -{ - $words = array(); - $words = preg_replace('#\[.*\]#isU', '', $text); - $words = str_replace(array('–', '»', '«', '↓', '→', '—'), array('', '', '', '', '', ''), $words); - $words = preg_split('#\s|[,.:;!?"\'()]#', $text, -1, PREG_SPLIT_NO_EMPTY); - return $words; -} + public function makeWordsFromText($text) + { + $words = array(); + $words = preg_replace('#\[.*\]#isU', '', $text); + $words = str_replace(array('–', '»', '«', '↓', '→', '—'), array('', '', '', '', '', ''), $words); + $words = preg_split('#\s|[,.:;!?"\'()]#', $text, -1, PREG_SPLIT_NO_EMPTY); + return $words; + } -public function makeBulkWords($words, $upper = true) -{ - $bulk_words = array(); - foreach ($words as $v) { - if (mb_strlen($v, "UTF-8") > $this->min_length) { - $bulk_words[] = $upper ? mb_strtoupper($v, "UTF-8") : $v; + public function makeBulkWords($words, $upper = true) + { + $bulk_words = array(); + foreach ($words as $v) { + if (mb_strlen($v, "UTF-8") > $this->min_length) { + $bulk_words[] = $upper ? mb_strtoupper($v, "UTF-8") : $v; + } } + return $bulk_words; } - return $bulk_words; -} -public function prepareWords() -{ - //string - //оригинальный запрос, очищенный от тегов - $this->Set('txt_original', $this->sanitarTag($_GET[$this->search_field]), true); - $this->original = $this->Get('txt_original'); - - //array() - //оригинальные слова из поиска длиннее min_length - $this->search_words = $this->makeWordsFromText($this->Get('txt_original')); - foreach ($this->search_words as $k => $word) { - if (mb_strlen($word, "UTF-8") <= $this->min_length) { - unset($this->search_words[$k]); + public function prepareWords() + { + //string + //оригинальный запрос, очищенный от тегов + $this->Set('txt_original', $this->sanitarTag($_GET[$this->search_field]), true); + $this->original = $this->Get('txt_original'); + + //array() + //оригинальные слова из поиска длиннее min_length + $this->search_words = $this->makeWordsFromText($this->Get('txt_original')); + foreach ($this->search_words as $k => $word) { + if (mb_strlen($word, "UTF-8") <= $this->min_length) { + unset($this->search_words[$k]); + } } - } - - //array() - //те же слова в верхнем регистре - $this->uppercase_search_words = $this->makeBulkWords($this->search_words); - - //нормализованные слова в базовой форме - $this->baseform_search_words = array(); - $tmp = $this->Words2BaseForm(implode(' ', $this->uppercase_search_words)); - foreach ($tmp as $v) { - if (is_array($v)) { - foreach ($v as $v1) { - if ($v1 && !empty($v1) && $v1 != '') { - $this->baseform_search_words[] = $v1; + + //array() + //те же слова в верхнем регистре + $this->uppercase_search_words = $this->makeBulkWords($this->search_words); + + //нормализованные слова в базовой форме + $this->baseform_search_words = array(); + $tmp = $this->Words2BaseForm(implode(' ', $this->uppercase_search_words)); + foreach ($tmp as $v) { + if (is_array($v)) { + foreach ($v as $v1) { + if ($v1 && !empty($v1) && $v1 != '') { + $this->baseform_search_words[] = $v1; + } + } + } else { + if ($v && !empty($v) && $v != '') { + $this->baseform_search_words[] = $v; } - } - } else { - if ($v && !empty($v) && $v != '') { - $this->baseform_search_words[] = $v; } } } -} -//функция для prepare-сниппета DocLister (готовим данные для плейсхолдера [+extract+] в чанк вывода результатов DocLister -public function prepareExtractor($data) -{ - $data = $this->makeHighlight ($data); - return $data; -} -//делаем подсветку на основе стеммера -public function makeHighlight ($data) -{ - if (is_array($this->bulk_words_stemmer) && !empty($this->bulk_words_stemmer)) { - $input = implode('|', $this->bulk_words_stemmer); - $input = str_replace(array('\\', '/'), array('', '\/'), $input); - $pattern = '/(' . $input . ')([^\.\s\;\:"\'\(\)!?,]*)?/ius'; - $replacement = '$1$2'; - if (isset($this->params['extract_with_tv']) && $this->params['extract_with_tv'] == '1') { - $text = $this->getTextForHighlight($data[$this->ext_content_field]); - } else{ - $text = $this->getTextForHighlight($data["content"]); - } - $pagetitle = $this->modx->stripTags($data["pagetitle"]); - $data["extract"] = preg_replace($pattern, $replacement, $text); - $data["pagetitle"] = preg_replace($pattern, $replacement, $pagetitle); + //функция для prepare-сниппета DocLister (готовим данные для плейсхолдера [+extract+] в чанк вывода результатов DocLister + public function prepareExtractor($data) + { + $data = $this->makeHighlight($data); + return $data; } - return $data; -} - -//вырезаем нужный кусок текста нужной длины (примерно) -private function getTextForHighlight($text) -{ - $max_length = isset($this->params['maxlength']) && (int)$this->params['maxlength'] != 0 ? (int)$this->params['maxlength'] : 350; - $limit = $max_length + 12; - $text = $this->modx->stripTags($text); - $pos = array(); - foreach ($this->bulk_words_stemmer as $word) { - $pos[$word] = mb_strripos(mb_strtolower($text, 'UTF-8'), $word, 0, 'UTF-8'); + //делаем подсветку на основе стеммера + public function makeHighlight($data) + { + if (is_array($this->bulk_words_stemmer) && !empty($this->bulk_words_stemmer)) { + $input = implode('|', $this->bulk_words_stemmer); + $input = str_replace(array('\\', '/'), array('', '\/'), $input); + $pattern = '/(' . $input . ')([^\.\s\;\:"\'\(\)!?,]*)?/ius'; + $replacement = '$1$2'; + if (isset($this->params['extract_with_tv']) && $this->params['extract_with_tv'] == '1') { + $text = $this->getTextForHighlight($data[$this->ext_content_field]); + } else { + $text = $this->getTextForHighlight($data["content"]); + } + $pagetitle = $this->modx->stripTags($data["pagetitle"]); + $data["extract"] = preg_replace($pattern, $replacement, $text); + $data["pagetitle"] = preg_replace($pattern, $replacement, $pagetitle); + } + return $data; } - foreach ($pos as $word => $position) { - $length = mb_strlen($text, 'UTF-8'); - if ($position == 0 && $length > $limit) { - $text = mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; - } else if ($position < $max_length && $length > $limit) { - $text = ' ... ' . mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; - } else if ($position + $limit >= $length && $length > $limit) { - $text = mb_substr($text, $position); - } else if ($length > $limit){ - $text = ' ... ' . mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; - } else { + //вырезаем нужный кусок текста нужной длины (примерно) + private function getTextForHighlight($text) + { + $max_length = isset($this->params['maxlength']) && (int)$this->params['maxlength'] != 0 ? (int)$this->params['maxlength'] : 350; + $limit = $max_length + 12; + $text = $this->modx->stripTags($text); + $pos = array(); + foreach ($this->bulk_words_stemmer as $word) { + $pos[$word] = mb_strripos(mb_strtolower($text, 'UTF-8'), $word, 0, 'UTF-8'); + } + foreach ($pos as $word => $position) { + $length = mb_strlen($text, 'UTF-8'); + if ($position == 0 && $length > $limit) { + $text = mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; + } elseif ($position < $max_length && $length > $limit) { + $text = ' ... ' . mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; + } elseif ($position + $limit >= $length && $length > $limit) { + $text = mb_substr($text, $position); + } elseif ($length > $limit) { + $text = ' ... ' . mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; + } else { + } } + return $text; } - return $text; -} -public function Words2BaseForm($text) -{ - require_once($this->phpmorphy_dir . '/src/common.php'); - - // set some options - $opts = array( - // storage type, follow types supported - // PHPMORPHY_STORAGE_FILE - use file operations(fread, fseek) for dictionary access, this is very slow... - // PHPMORPHY_STORAGE_SHM - load dictionary in shared memory(using shmop php extension), this is preferred mode - // PHPMORPHY_STORAGE_MEM - load dict to memory each time when phpMorphy intialized, this useful when shmop ext. not activated. Speed same as for PHPMORPHY_STORAGE_SHM type - 'storage' => PHPMORPHY_STORAGE_MEM, - // Extend graminfo for getAllFormsWithGramInfo method call - 'with_gramtab' => false, - // Enable prediction by suffix - 'predict_by_suffix' => true, - // Enable prediction by prefix - 'predict_by_db' => true - ); - - $words = $this->makeWordsFromText($text); - $bulk_words = $this->makeBulkWords($words); - - $w = array(); - foreach ($this->dicts as $dict) { - // Create descriptor for dictionary located in $dir directory with russian language - $dict_bundle = new phpMorphy_FilesBundle($this->dict_dir, $dict); - // Create phpMorphy instance - $morphy = new phpMorphy($dict_bundle, $opts); - $tmp = $morphy->getBaseForm($bulk_words); - $w = array_merge_recursive($w, $tmp); + public function Words2BaseForm($text) + { + require_once($this->phpmorphy_dir . '/src/common.php'); + + // set some options + $opts = array( + // storage type, follow types supported + // PHPMORPHY_STORAGE_FILE - use file operations(fread, fseek) for dictionary access, this is very slow... + // PHPMORPHY_STORAGE_SHM - load dictionary in shared memory(using shmop php extension), this is preferred mode + // PHPMORPHY_STORAGE_MEM - load dict to memory each time when phpMorphy intialized, this useful when shmop ext. not activated. Speed same as for PHPMORPHY_STORAGE_SHM type + 'storage' => PHPMORPHY_STORAGE_MEM, + // Extend graminfo for getAllFormsWithGramInfo method call + 'with_gramtab' => false, + // Enable prediction by suffix + 'predict_by_suffix' => true, + // Enable prediction by prefix + 'predict_by_db' => true + ); + + $words = $this->makeWordsFromText($text); + $bulk_words = $this->makeBulkWords($words); + + $w = array(); + foreach ($this->dicts as $dict) { + // Create descriptor for dictionary located in $dir directory with russian language + $dict_bundle = new phpMorphy_FilesBundle($this->dict_dir, $dict); + // Create phpMorphy instance + $morphy = new phpMorphy($dict_bundle, $opts); + $tmp = $morphy->getBaseForm($bulk_words); + $w = array_merge_recursive($w, $tmp); + } + return $w; } - return $w; -} - -public function getStemmer() -{ - include_once('stemmer.class.php'); - return $stemmer = new Lingua_Stem_Ru(); -} -public function Set($key, $value, $escape = false) -{ - if ($escape) { - $this->{$key} = $this->modx->db->escape($value); - } else { - $this->{$key} = $value; + public function getStemmer() + { + include_once('stemmer.class.php'); + return $stemmer = new Lingua_Stem_Ru(); } -} -public function Get($key, $default = '') -{ - return $this->{$key} ? $this->{$key} : $default ; -} - -public function makeSearch() -{ - //возвращаем id ресурсов, отобранные по полнотекстовому поиску и отсортированные по релевантности - $ids = array(); - $sql = $this->makeSearchSQL(); - //$sql2 = $this->makeSearchSQL('addlike'); - if ($this->params['debug'] == '1') { - echo $sql . '
'; - //echo $sql2 . '
'; - } - if ($sql != '') { - $q = $this->modx->db->query($sql); - while ($row = $this->modx->db->getRow($q)) { - $ids[] = $row['docid']; + public function Set($key, $value, $escape = false) + { + if ($escape) { + $this->{$key} = $this->modx->db->escape($value); + } else { + $this->{$key} = $value; } } - if ($this->params['debug'] == '1') { - echo 'найдены ' . implode(',', $ids) . '
'; + + public function Get($key, $default = '') + { + return $this->{$key} ? $this->{$key} : $default; } - if (empty($ids)) {//ничего не найдено, возможно требуется дополнительный поиск по like - $sql = $this->makeSearchSQL('addlike'); + + public function makeSearch() + { + //возвращаем id ресурсов, отобранные по полнотекстовому поиску и отсортированные по релевантности + $ids = array(); + $sql = $this->makeSearchSQL(); + //$sql2 = $this->makeSearchSQL('addlike'); if ($this->params['debug'] == '1') { echo $sql . '
'; + //echo $sql2 . '
'; } if ($sql != '') { $q = $this->modx->db->query($sql); @@ -277,130 +261,143 @@ public function makeSearch() if ($this->params['debug'] == '1') { echo 'найдены ' . implode(',', $ids) . '
'; } + if (empty($ids)) { //ничего не найдено, возможно требуется дополнительный поиск по like + $sql = $this->makeSearchSQL('addlike'); + if ($this->params['debug'] == '1') { + echo $sql . '
'; + } + if ($sql != '') { + $q = $this->modx->db->query($sql); + while ($row = $this->modx->db->getRow($q)) { + $ids[] = $row['docid']; + } + } + if ($this->params['debug'] == '1') { + echo 'найдены ' . implode(',', $ids) . '
'; + } + } + return $ids; } - return $ids; -} -public function makeSearchSQL($type = 'fulltext') -{ - switch ($type) { - case 'fulltext': - $sql = $this->buildFulltextSQL(); - break; - case 'addlike': - $sql = $this->buildAddLikeSQL(); - default: - break; + public function makeSearchSQL($type = 'fulltext') + { + switch ($type) { + case 'fulltext': + $sql = $this->buildFulltextSQL(); + break; + case 'addlike': + $sql = $this->buildAddLikeSQL(); + default: + break; + } + return $sql; } - return $sql; -} -public function buildFulltextSQL() -{ - $sql = ''; - if ($this->original != '' || !empty($this->baseform_search_words)) { - $txt_original = $this->original; - $search = $txt_original . ' ' . implode(' ', $this->baseform_search_words); - $sql = "SELECT docid, IF(`pagetitle` LIKE '%" . $txt_original . "%', 2, 0) as pt, IF(`" . $this->ext_content_field . "` LIKE '%" . $txt_original . "%', 1, 0) as ct, (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) as rel FROM " . $this->search_table . " WHERE IF(`pagetitle` LIKE '%" . $txt_original . "%', 2, 0)>0 OR IF(`" . $this->ext_content_field . "` LIKE '%" . $txt_original . "%', 1, 0)>0 OR (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) > " . $this->params['rel'] . " ORDER BY pt DESC, (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) DESC, ct DESC"; + public function buildFulltextSQL() + { + $sql = ''; + if ($this->original != '' || !empty($this->baseform_search_words)) { + $txt_original = $this->original; + $search = $txt_original . ' ' . implode(' ', $this->baseform_search_words); + $sql = "SELECT docid, IF(`pagetitle` LIKE '%" . $txt_original . "%', 2, 0) as pt, IF(`" . $this->ext_content_field . "` LIKE '%" . $txt_original . "%', 1, 0) as ct, (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) as rel FROM " . $this->search_table . " WHERE IF(`pagetitle` LIKE '%" . $txt_original . "%', 2, 0)>0 OR IF(`" . $this->ext_content_field . "` LIKE '%" . $txt_original . "%', 1, 0)>0 OR (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) > " . $this->params['rel'] . " ORDER BY pt DESC, (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) DESC, ct DESC"; + } + return $sql; } - return $sql; -} -public function buildAddLikeSQL() -{ - //ищем вхождение всех слов в заголовок либо в поле content_with_tv - $sql = ''; - $addPagetitle = $this->makeAddLikeCond('pagetitle', ' '); - $addContent = $this->makeAddLikeCond($this->ext_content_field, 'OR'); - if ($addPagetitle != '' && $addContent != '') { - $sql = "SELECT docid, IF(" . $addPagetitle . ", 2,0) as rel FROM " . $this->search_table . " WHERE " . $addPagetitle . " " . $addContent . " ORDER BY rel DESC"; + public function buildAddLikeSQL() + { + //ищем вхождение всех слов в заголовок либо в поле content_with_tv + $sql = ''; + $addPagetitle = $this->makeAddLikeCond('pagetitle', ' '); + $addContent = $this->makeAddLikeCond($this->ext_content_field, 'OR'); + if ($addPagetitle != '' && $addContent != '') { + $sql = "SELECT docid, IF(" . $addPagetitle . ", 2,0) as rel FROM " . $this->search_table . " WHERE " . $addPagetitle . " " . $addContent . " ORDER BY rel DESC"; + } + return $sql; } - return $sql; -} -public function makeStringFromQuery($q, $serapator = ',', $field = 'id') -{ - $out = array(); - while ($row = $this->modx->db->getRow($q)) { - $out[] = $row[$field]; + public function makeStringFromQuery($q, $serapator = ',', $field = 'id') + { + $out = array(); + while ($row = $this->modx->db->getRow($q)) { + $out[] = $row[$field]; + } + return implode($serapator, $out); } - return implode($serapator, $out); -} -public function getSearchResultInfo() -{ - $out = ''; - $DL_id = isset($this->params['id']) && !empty($this->params['id']) ? $this->params['id'] . '.' : ''; - $count = (int)$this->modx->getPlaceholder($DL_id . 'count'); - $display = (int)$this->modx->getPlaceholder($DL_id . 'display'); - $current = (int)$this->modx->getPlaceholder($DL_id . 'current'); - if (!$current) $current = 1; - $from = $to = 0; - if ($count > 0) { - $from = ($current - 1) * $this->params['display'] + 1; - $to = $from - 1 + $display; - $out .= $this->parseTpl( - array('[+stat_request+]', '[+stat_total+]', '[+stat_display+]', '[+stat_from+]', '[+stat_to+]'), - array($this->Get('txt_original'), $count, $display, $from, $to), - $this->params['statTpl'] - ); + public function getSearchResultInfo() + { + $out = ''; + $DL_id = isset($this->params['id']) && !empty($this->params['id']) ? $this->params['id'] . '.' : ''; + $count = (int)$this->modx->getPlaceholder($DL_id . 'count'); + $display = (int)$this->modx->getPlaceholder($DL_id . 'display'); + $current = (int)$this->modx->getPlaceholder($DL_id . 'current'); + if (!$current) $current = 1; + $from = $to = 0; + if ($count > 0) { + $from = ($current - 1) * $this->params['display'] + 1; + $to = $from - 1 + $display; + $out .= $this->parseTpl( + array('[+stat_request+]', '[+stat_total+]', '[+stat_display+]', '[+stat_from+]', '[+stat_to+]'), + array($this->Get('txt_original'), $count, $display, $from, $to), + $this->params['statTpl'] + ); + } + $this->setPlaceholders( + array( + 'stat_total' => $count, + 'stat_display' => $display, + 'stat_from' => $from, + 'stat_to' => $to, + 'stat_tpl' => $out, + 'stat_request' => $this->Get('txt_original') + ) + ); + return $out; } - $this->setPlaceholders( - array( - 'stat_total' => $count, - 'stat_display' => $display, - 'stat_from' => $from, - 'stat_to' => $to, - 'stat_tpl' => $out, - 'stat_request' => $this->Get('txt_original') - ) - ); - return $out; -} -public function parseTpl($arr1, $arr2, $tpl) -{ - return str_replace($arr1, $arr2, $tpl); -} + public function parseTpl($arr1, $arr2, $tpl) + { + return str_replace($arr1, $arr2, $tpl); + } -public function sanitarTag($data) -{ + public function sanitarTag($data) + { return is_scalar($data) ? str_replace( array('[', '%5B', ']', '%5D', '{', '%7B', '}', '%7D'), array('[', '[', ']', ']', '{', '{', '}', '}'), htmlspecialchars($data, ENT_COMPAT, 'UTF-8', false) ) : ''; -} + } -public function setPlaceholders($data = array()) -{ - if (is_array($data)) { - foreach ($data as $name => $value) { - $this->modx->setPlaceholder($name, $value); + public function setPlaceholders($data = array()) + { + if (is_array($data)) { + foreach ($data as $name => $value) { + $this->modx->setPlaceholder($name, $value); + } } } -} -public function parseNoresult($noResult) -{ - return $this->parseTpl(array('[+stat_request+]'), array($this->Get('txt_original')), $noResult); -} + public function parseNoresult($noResult) + { + return $this->parseTpl(array('[+stat_request+]'), array($this->Get('txt_original')), $noResult); + } -public function makeAddLikeCond($search_field = 'pagetitle', $separator = 'AND', $inner_separator = 'AND') -{ - $out = ''; - foreach ($this->search_words as $word) { - $word = mb_strtolower($word, "UTF-8"); - $tmp[] = " LOWER(`" . $search_field . "`) REGEXP '[[:<:]]" . $word . "[[:>:]]'"; - } - if (!empty($tmp)) { - $out = implode(' ' . trim($inner_separator) . ' ', $tmp); - } - if (!empty($out)) { - $out = ' ' . $separator . ' (' . $out . ')'; + public function makeAddLikeCond($search_field = 'pagetitle', $separator = 'AND', $inner_separator = 'AND') + { + $out = ''; + foreach ($this->search_words as $word) { + $word = mb_strtolower($word, "UTF-8"); + $tmp[] = " LOWER(`" . $search_field . "`) REGEXP '[[:<:]]" . $word . "[[:>:]]'"; + } + if (!empty($tmp)) { + $out = implode(' ' . trim($inner_separator) . ' ', $tmp); + } + if (!empty($out)) { + $out = ' ' . $separator . ' (' . $out . ')'; + } + return $out; } - return $out; } - -}//class end From 0508a3d35184734ca28c7336bea0e998ee005a23 Mon Sep 17 00:00:00 2001 From: yamamoto Date: Thu, 12 May 2022 00:52:39 +0900 Subject: [PATCH 3/6] refactor evoSearch.snippet.php --- .../snippets/evoSearch/evoSearch.snippet.php | 114 ++++++++++-------- 1 file changed, 65 insertions(+), 49 deletions(-) diff --git a/assets/snippets/evoSearch/evoSearch.snippet.php b/assets/snippets/evoSearch/evoSearch.snippet.php index 8210a28..2ec5d06 100644 --- a/assets/snippets/evoSearch/evoSearch.snippet.php +++ b/assets/snippets/evoSearch/evoSearch.snippet.php @@ -7,13 +7,6 @@ die('What are you doing? Get out of here!'); } -$documents = ''; -$output = ''; -if (!isset($params['noResult'])) { - $noResult = 'По запросу [+stat_request+] ничего не найдено. Смягчите условия поиска'; -} else { - $noResult = $params['noResult']; -} $txt_original = ''; include_once('snippet.class.php'); @@ -24,49 +17,72 @@ } $eSS = new evoSearchSnippet($modx, $params, $min_length); -if (isset($_GET[$eSS->search_field]) && $_GET[$eSS->search_field] != '') { - $eSS->init(); - $eSS->prepareWords(); - $modx->setPlaceholder('stat_request', $eSS->Get('txt_original')); +if (!isset($_GET[$eSS->search_field]) || $_GET[$eSS->search_field] == '') { + return null; +} + +$eSS->init(); +$eSS->prepareWords(); +$modx->setPlaceholder('stat_request', $eSS->Get('txt_original')); + +//получаем массив подходящих под условие поиска id +$ids = $eSS->makeSearch(); +$ids[] = 4294967295; - //получаем массив подходящих под условие поиска id - $ids = $eSS->makeSearch(); - $ids[] = 4294967295; - if ($eSS->action === 'ids') {//работаем в режиме ids - сразу возвращаем ids - $modx->setPlaceholder("evoSearchIDs", $ids); - if ($eSS->params['output'] && $eSS->params['output'] == '1') { - $output = implode(',', $ids); - } - } else {//работаем в полном режиме, возвращаем вместе с выводом результатов - if (!empty($ids)) { - $eSS->bulk_words_stemmer = array(); - foreach ($eSS->search_words as $v) { - $eSS->bulk_words_stemmer[] = $eSS->stemmer->stem_word($v); - } - $DLparams = array( - 'documents' => implode(',', $ids), - 'sortType' => 'doclist', - 'showParent' => '1', - 'addWhereList' => 'c.searchable=1' . (isset($params['addWhereList']) ? ' AND ' . $params['addWhereList'] : '') - ); - if (isset($params['parents'])) { - $DLparams['addWhereList'] .= ' AND c.id IN (' . $DLparams['documents'] . ') '; - unset($DLparams['documents']); - } - if ($eSS->params['extract'] == '1' && (!isset($params['prepare']) || $params['prepare'] == '')) { - $DLparams['prepare'] = array($eSS, 'prepareExtractor'); - } - $params = array_merge($params, $DLparams); - $output .= $modx->runSnippet("DocLister", $params); - if ($eSS->params['show_stat'] == 1) { - $output = $eSS->getSearchResultInfo() . $output; - } - } - if ($output == '') { - if (isset($_REQUEST[$eSS->search_field])) { - $output = '
' . $eSS->parseNoresult($noResult) . '
'; - } - } +if ($eSS->action === 'ids') {//работаем в режиме ids - сразу возвращаем ids + $modx->setPlaceholder("evoSearchIDs", $ids); + if (!$eSS->params['output'] || $eSS->params['output'] != 1) { + return null; } + return implode(',', $ids); } + +if (empty($ids)) { + if (!isset($_REQUEST[$eSS->search_field])) { + return null; + } + if (!isset($params['noResult'])) { + $noResult = 'По запросу [+stat_request+] ничего не найдено. Смягчите условия поиска'; + } else { + $noResult = $params['noResult']; + } + return '
' . $eSS->parseNoresult($noResult) . '
'; +} + +//работаем в полном режиме, возвращаем вместе с выводом результатов +$eSS->bulk_words_stemmer = []; +foreach ($eSS->search_words as $v) { + $eSS->bulk_words_stemmer[] = $eSS->stemmer->stem_word($v); +} +$DLparams = [ + 'documents' => implode(',', $ids), + 'sortType' => 'doclist', + 'showParent' => '1', + 'addWhereList' => 'c.searchable=1' + . (isset($params['addWhereList']) + ? ' AND ' . $params['addWhereList'] + : '' + ) +]; +if (isset($params['parents'])) { + $DLparams['addWhereList'] .= ' AND c.id IN (' . $DLparams['documents'] . ') '; + unset($DLparams['documents']); +} +if ($eSS->params['extract'] == '1' && (!isset($params['prepare']) || $params['prepare'] == '')) { + $DLparams['prepare'] = [$eSS, 'prepareExtractor']; +} + +$params = array_merge($params, $DLparams); +$output = $modx->runSnippet('DocLister', $params); +if ($eSS->params['show_stat'] == 1) { + return $eSS->getSearchResultInfo() . $output; +} + +if ($output == '') { + if (!isset($_REQUEST[$eSS->search_field])) { + return null; + } + return '
' . $eSS->parseNoresult($noResult) . '
'; +} + return $output; From 87b0a55dbfa720d0f1d222a2f693b7492345b2bf Mon Sep 17 00:00:00 2001 From: yamamoto Date: Thu, 12 May 2022 00:56:10 +0900 Subject: [PATCH 4/6] fix - evoSearchSnippet::makeWordsFromText() --- assets/snippets/evoSearch/snippet.class.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/assets/snippets/evoSearch/snippet.class.php b/assets/snippets/evoSearch/snippet.class.php index f250b0b..27adc8a 100644 --- a/assets/snippets/evoSearch/snippet.class.php +++ b/assets/snippets/evoSearch/snippet.class.php @@ -80,11 +80,16 @@ public function setDefault($param, $default = '') public function makeWordsFromText($text) { - $words = array(); - $words = preg_replace('#\[.*\]#isU', '', $text); - $words = str_replace(array('–', '»', '«', '↓', '→', '—'), array('', '', '', '', '', ''), $words); - $words = preg_split('#\s|[,.:;!?"\'()]#', $text, -1, PREG_SPLIT_NO_EMPTY); - return $words; + return preg_split( + '#\s|[,.:;!?"\'()]#', + str_replace( + ['–', '»', '«', '↓', '→', '—'], + ['', '', '', '', '', ''], + preg_replace('#\[.*]#isU', '', $text) + ), + -1, + PREG_SPLIT_NO_EMPTY + ); } public function makeBulkWords($words, $upper = true) From ead0dd98889d8a3e2cd3507f0ddcade4139e87e9 Mon Sep 17 00:00:00 2001 From: yamamoto Date: Thu, 12 May 2022 01:20:50 +0900 Subject: [PATCH 5/6] refactor - snippet.class.php --- assets/snippets/evoSearch/snippet.class.php | 199 ++++++++++++-------- 1 file changed, 116 insertions(+), 83 deletions(-) diff --git a/assets/snippets/evoSearch/snippet.class.php b/assets/snippets/evoSearch/snippet.class.php index 27adc8a..e1ee786 100644 --- a/assets/snippets/evoSearch/snippet.class.php +++ b/assets/snippets/evoSearch/snippet.class.php @@ -31,6 +31,8 @@ class evoSearchSnippet //словоформы всех слов из поисковой строки, строка. Слова разделены пробелами. Основной текст для организации полнотекстового поиска public $txt_ext = array(); + public $search_words; + public function __construct($modx, $params) { @@ -50,17 +52,15 @@ public function init($min_length = 2, $ext_content_field = 'content_with_tv', $e $this->ext_content_field = $ext_content_field; $this->ext_content_index_field = $ext_content_index_field; $this->action = isset($this->params['action']) ? $this->params['action'] : ''; - $this->setDefault( - array( - 'display' => '20', - 'show_stat' => '1', - 'extract' => '1', - 'statTpl' => '
По запросу [+stat_request+] найдено всего [+stat_total+]. Показано [+stat_display+], c [+stat_from+] по [+stat_to+]
', - 'rel' => '0.01', - 'min_length' => $min_length, - 'dedug' => '0' - ) - ); + $this->setDefault([ + 'display' => '20', + 'show_stat' => '1', + 'extract' => '1', + 'statTpl' => '
По запросу [+stat_request+] найдено всего [+stat_total+]. Показано [+stat_display+], c [+stat_from+] по [+stat_to+]
', + 'rel' => '0.01', + 'min_length' => $min_length, + 'dedug' => '0' + ]); $this->min_length = $this->params['min_length']; $this->stemmer = $this->getStemmer(); return $this; @@ -68,12 +68,12 @@ public function init($min_length = 2, $ext_content_field = 'content_with_tv', $e public function setDefault($param, $default = '') { - if (is_array($param)) { - foreach ($param as $p => $v) { - $this->params[$p] = isset($this->params[$p]) ? $this->params[$p] : $v; - } - } else { - $this->params[$param] = isset($this->params[$param]) ? $this->params[$param] : $default; + if (!is_array($param)) { + $this->params[$param] = !isset($this->params[$param]) ? $default : $this->params[$param]; + return $this; + } + foreach ($param as $p => $v) { + $this->params[$p] = isset($this->params[$p]) ? $this->params[$p] : $v; } return $this; } @@ -94,10 +94,10 @@ public function makeWordsFromText($text) public function makeBulkWords($words, $upper = true) { - $bulk_words = array(); + $bulk_words = []; foreach ($words as $v) { - if (mb_strlen($v, "UTF-8") > $this->min_length) { - $bulk_words[] = $upper ? mb_strtoupper($v, "UTF-8") : $v; + if (mb_strlen($v, 'UTF-8') > $this->min_length) { + $bulk_words[] = $upper ? mb_strtoupper($v, 'UTF-8') : $v; } } return $bulk_words; @@ -133,10 +133,10 @@ public function prepareWords() $this->baseform_search_words[] = $v1; } } - } else { - if ($v && !empty($v) && $v != '') { - $this->baseform_search_words[] = $v; - } + continue; + } + if (!empty($v)) { + $this->baseform_search_words[] = $v; } } } @@ -150,34 +150,39 @@ public function prepareExtractor($data) //делаем подсветку на основе стеммера public function makeHighlight($data) { - if (is_array($this->bulk_words_stemmer) && !empty($this->bulk_words_stemmer)) { - $input = implode('|', $this->bulk_words_stemmer); - $input = str_replace(array('\\', '/'), array('', '\/'), $input); - $pattern = '/(' . $input . ')([^\.\s\;\:"\'\(\)!?,]*)?/ius'; - $replacement = '$1$2'; - if (isset($this->params['extract_with_tv']) && $this->params['extract_with_tv'] == '1') { - $text = $this->getTextForHighlight($data[$this->ext_content_field]); - } else { - $text = $this->getTextForHighlight($data["content"]); - } - $pagetitle = $this->modx->stripTags($data["pagetitle"]); - $data["extract"] = preg_replace($pattern, $replacement, $text); - $data["pagetitle"] = preg_replace($pattern, $replacement, $pagetitle); + if (!is_array($this->bulk_words_stemmer) || empty($this->bulk_words_stemmer)) { + return $data; } + $input = implode('|', $this->bulk_words_stemmer); + $input = str_replace(array('\\', '/'), array('', '\/'), $input); + $pattern = '/(' . $input . ')([^.\s;:"\'()!?,]*)?/ius'; + $replacement = '$1$2'; + if (isset($this->params['extract_with_tv']) && $this->params['extract_with_tv'] == '1') { + $text = $this->getTextForHighlight($data[$this->ext_content_field]); + } else { + $text = $this->getTextForHighlight($data["content"]); + } + $pagetitle = $this->modx->stripTags($data["pagetitle"]); + $data['extract'] = preg_replace($pattern, $replacement, $text); + $data['pagetitle'] = preg_replace($pattern, $replacement, $pagetitle); return $data; } //вырезаем нужный кусок текста нужной длины (примерно) private function getTextForHighlight($text) { - $max_length = isset($this->params['maxlength']) && (int)$this->params['maxlength'] != 0 ? (int)$this->params['maxlength'] : 350; + if (!isset($this->params['maxlength']) || (int)$this->params['maxlength'] == 0) { + $max_length = 350; + } else { + $max_length = (int)$this->params['maxlength']; + } $limit = $max_length + 12; $text = $this->modx->stripTags($text); $pos = array(); foreach ($this->bulk_words_stemmer as $word) { $pos[$word] = mb_strripos(mb_strtolower($text, 'UTF-8'), $word, 0, 'UTF-8'); } - foreach ($pos as $word => $position) { + foreach ($pos as $position) { $length = mb_strlen($text, 'UTF-8'); if ($position == 0 && $length > $limit) { $text = mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; @@ -187,7 +192,6 @@ private function getTextForHighlight($text) $text = mb_substr($text, $position); } elseif ($length > $limit) { $text = ' ... ' . mb_substr($text, $position, $max_length, 'UTF-8') . ' ... '; - } else { } } return $text; @@ -198,7 +202,7 @@ public function Words2BaseForm($text) require_once($this->phpmorphy_dir . '/src/common.php'); // set some options - $opts = array( + $opts = [ // storage type, follow types supported // PHPMORPHY_STORAGE_FILE - use file operations(fread, fseek) for dictionary access, this is very slow... // PHPMORPHY_STORAGE_SHM - load dictionary in shared memory(using shmop php extension), this is preferred mode @@ -210,7 +214,7 @@ public function Words2BaseForm($text) 'predict_by_suffix' => true, // Enable prediction by prefix 'predict_by_db' => true - ); + ]; $words = $this->makeWordsFromText($text); $bulk_words = $this->makeBulkWords($words); @@ -230,21 +234,21 @@ public function Words2BaseForm($text) public function getStemmer() { include_once('stemmer.class.php'); - return $stemmer = new Lingua_Stem_Ru(); + return new Lingua_Stem_Ru(); } public function Set($key, $value, $escape = false) { - if ($escape) { - $this->{$key} = $this->modx->db->escape($value); - } else { + if (!$escape) { $this->{$key} = $value; + } else { + $this->{$key} = $this->modx->db->escape($value); } } public function Get($key, $default = '') { - return $this->{$key} ? $this->{$key} : $default; + return $this->{$key} ?: $default; } public function makeSearch() @@ -263,24 +267,27 @@ public function makeSearch() $ids[] = $row['docid']; } } - if ($this->params['debug'] == '1') { + if ($this->params['debug'] == 1) { echo 'найдены ' . implode(',', $ids) . '
'; } - if (empty($ids)) { //ничего не найдено, возможно требуется дополнительный поиск по like - $sql = $this->makeSearchSQL('addlike'); - if ($this->params['debug'] == '1') { - echo $sql . '
'; - } - if ($sql != '') { - $q = $this->modx->db->query($sql); - while ($row = $this->modx->db->getRow($q)) { - $ids[] = $row['docid']; - } - } - if ($this->params['debug'] == '1') { - echo 'найдены ' . implode(',', $ids) . '
'; + if (!empty($ids)) { + return $ids; + } + + //ничего не найдено, возможно требуется дополнительный поиск по like + $sql = $this->makeSearchSQL('addlike'); + if ($this->params['debug'] == '1') { + echo $sql . '
'; + } + if ($sql != '') { + $q = $this->modx->db->query($sql); + while ($row = $this->modx->db->getRow($q)) { + $ids[] = $row['docid']; } } + if ($this->params['debug'] == '1') { + echo 'найдены ' . implode(',', $ids) . '
'; + } return $ids; } @@ -300,25 +307,49 @@ public function makeSearchSQL($type = 'fulltext') public function buildFulltextSQL() { - $sql = ''; - if ($this->original != '' || !empty($this->baseform_search_words)) { - $txt_original = $this->original; - $search = $txt_original . ' ' . implode(' ', $this->baseform_search_words); - $sql = "SELECT docid, IF(`pagetitle` LIKE '%" . $txt_original . "%', 2, 0) as pt, IF(`" . $this->ext_content_field . "` LIKE '%" . $txt_original . "%', 1, 0) as ct, (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) as rel FROM " . $this->search_table . " WHERE IF(`pagetitle` LIKE '%" . $txt_original . "%', 2, 0)>0 OR IF(`" . $this->ext_content_field . "` LIKE '%" . $txt_original . "%', 1, 0)>0 OR (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) > " . $this->params['rel'] . " ORDER BY pt DESC, (MATCH(" . $this->ext_content_field . "," . $this->ext_content_index_field . ") AGAINST('" . $search . "')) DESC, ct DESC"; + if ($this->original == '' && empty($this->baseform_search_words)) { + return ''; } - return $sql; + + $search = $this->original . ' ' . implode(' ', $this->baseform_search_words); + return sprintf( + "SELECT docid, IF(`pagetitle` LIKE '%%%s%%', 2, 0) as pt, IF(`%s` LIKE '%%%s%%', 1, 0) as ct, (MATCH(%s,%s) AGAINST('%s')) as rel FROM %s WHERE IF(`pagetitle` LIKE '%%%s%%', 2, 0)>0 OR IF(`%s` LIKE '%%%s%%', 1, 0)>0 OR (MATCH(%s,%s) AGAINST('%s')) > %s ORDER BY pt DESC, (MATCH(%s,%s) AGAINST('%s')) DESC, ct DESC", + $this->original, + $this->ext_content_field, + $this->original, + $this->ext_content_field, + $this->ext_content_index_field, + $search, + $this->search_table, + $this->original, + $this->ext_content_field, + $this->original, + $this->ext_content_field, + $this->ext_content_index_field, + $search, + $this->params['rel'], + $this->ext_content_field, + $this->ext_content_index_field, + $search + ); } public function buildAddLikeSQL() { //ищем вхождение всех слов в заголовок либо в поле content_with_tv - $sql = ''; $addPagetitle = $this->makeAddLikeCond('pagetitle', ' '); $addContent = $this->makeAddLikeCond($this->ext_content_field, 'OR'); - if ($addPagetitle != '' && $addContent != '') { - $sql = "SELECT docid, IF(" . $addPagetitle . ", 2,0) as rel FROM " . $this->search_table . " WHERE " . $addPagetitle . " " . $addContent . " ORDER BY rel DESC"; + if ($addPagetitle == '' || $addContent == '') { + return null; } - return $sql; + + return sprintf( + "SELECT docid, IF(%s, 2,0) as rel FROM %s WHERE %s %s ORDER BY rel DESC", + $addPagetitle, + $this->search_table, + $addPagetitle, + $addContent + ); } public function makeStringFromQuery($q, $serapator = ',', $field = 'id') @@ -377,10 +408,11 @@ public function sanitarTag($data) public function setPlaceholders($data = array()) { - if (is_array($data)) { - foreach ($data as $name => $value) { - $this->modx->setPlaceholder($name, $value); - } + if (!is_array($data)) { + return; + } + foreach ($data as $name => $value) { + $this->modx->setPlaceholder($name, $value); } } @@ -392,17 +424,18 @@ public function parseNoresult($noResult) public function makeAddLikeCond($search_field = 'pagetitle', $separator = 'AND', $inner_separator = 'AND') { - $out = ''; + if (!$this->search_words) { + return ''; + } + foreach ($this->search_words as $word) { $word = mb_strtolower($word, "UTF-8"); $tmp[] = " LOWER(`" . $search_field . "`) REGEXP '[[:<:]]" . $word . "[[:>:]]'"; } - if (!empty($tmp)) { - $out = implode(' ' . trim($inner_separator) . ' ', $tmp); - } - if (!empty($out)) { - $out = ' ' . $separator . ' (' . $out . ')'; - } - return $out; + return sprintf( + ' %s (%s)', + $separator, + implode(' ' . trim($inner_separator) . ' ', $tmp) + ); } } From cd01fb7c7242cf124a3b2706bf47dabb8b3f3260 Mon Sep 17 00:00:00 2001 From: yamamoto Date: Thu, 12 May 2022 01:42:10 +0900 Subject: [PATCH 6/6] refactor --- assets/plugins/evoSearch/evoSearch.plugin.php | 68 +-- assets/plugins/evoSearch/plugin.class.php | 406 ++++++++++-------- assets/snippets/evoSearch/snippet.class.php | 2 +- 3 files changed, 254 insertions(+), 222 deletions(-) diff --git a/assets/plugins/evoSearch/evoSearch.plugin.php b/assets/plugins/evoSearch/evoSearch.plugin.php index 90ddebd..302d714 100644 --- a/assets/plugins/evoSearch/evoSearch.plugin.php +++ b/assets/plugins/evoSearch/evoSearch.plugin.php @@ -1,38 +1,42 @@ Event; -if ($e->name == 'OnDocFormSave') { - include_once('plugin.class.php'); - $eSP = new evoSearchPlugin($modx, $e->params); - //проверяем наличие и создаем при необходимости нужные поля и индекса - $eSP->prepareRun(); - //теперь начинаем работу - $sql = $eSP->makeSQLForSelectWords(); - //echo $sql;die(); - $q = $modx->db->query($sql); - $content_fields = explode(',', $eSP->cleanIn($eSP->search_fields)); - while ($row = $modx->db->getRow($q)) { - $tmp = array(); - foreach($content_fields as $k => $v) { - if (isset($row[$v]) && !empty($row[$v])) { - $tmp[] = $row[$v]; - } +if ($modx->event->name !== 'OnDocFormSave') { + return; +} + +include_once('plugin.class.php'); +$eSP = new evoSearchPlugin($modx, $modx->event->params); +//проверяем наличие и создаем при необходимости нужные поля и индекса +$eSP->prepareRun(); +//теперь начинаем работу +$sql = $eSP->makeSQLForSelectWords(); +//echo $sql;die(); +$q = $modx->db->query($sql); +$content_fields = explode(',', $eSP->cleanIn($eSP->search_fields)); +while ($row = $modx->db->getRow($q)) { + $tmp = []; + foreach ($content_fields as $k => $v) { + if (isset($row[$v]) && !empty($row[$v])) { + $tmp[] = $row[$v]; } - $content_original = implode(' ', $tmp); - $content = $modx->stripTags($content_original); - $content = $eSP->injectTVs($row['id'], $content); - $words = $eSP->Words2BaseForm(mb_strtoupper($content, 'UTF-8')); - $fields = array( - $eSP->ext_content_field => $modx->db->escape($content), - $eSP->ext_content_index_field => $modx->db->escape($words), - 'docid' => $row['id'], - 'pagetitle' => isset($row['pagetitle']) ? $modx->db->escape($row['pagetitle']) : '', - 'table' => $eSP->content_table_name - ); - $up = $eSP->updateSearchTable($fields); - //die(); } - //удалим строки индексов для исключенных шаблонов и ресурсов - $eSP->emptyExcluded(); + $content_original = implode(' ', $tmp); + $content = $modx->stripTags($content_original); + $content = $eSP->injectTVs($row['id'], $content); + $words = $eSP->Words2BaseForm(mb_strtoupper($content, 'UTF-8')); + $up = $eSP->updateSearchTable([ + $eSP->ext_content_field => $modx->db->escape($content), + $eSP->ext_content_index_field => $modx->db->escape($words), + 'docid' => $row['id'], + 'pagetitle' => !isset($row['pagetitle']) + ? '' + : $modx->db->escape($row['pagetitle']), + 'table' => $eSP->content_table_name + ]); } +//удалим строки индексов для исключенных шаблонов и ресурсов +$eSP->emptyExcluded(); diff --git a/assets/plugins/evoSearch/plugin.class.php b/assets/plugins/evoSearch/plugin.class.php index fa0f4b1..49fe33f 100644 --- a/assets/plugins/evoSearch/plugin.class.php +++ b/assets/plugins/evoSearch/plugin.class.php @@ -1,217 +1,249 @@ modx = $modx; - $this->params = $params; - $this->phpmorphy_dir = MODX_BASE_PATH . 'assets/lib/phpmorphy'; - $this->dict_dir = $this->phpmorphy_dir . '/dicts'; - $this->min_length = $min_length; - $this->dicts = $this->getDicts($this->params['dicts']); - $this->id = $this->params['id']; - $this->content_table_name = $content_table; - $this->content_table = $this->modx->getFullTableName($this->content_table_name); - $this->ext_content_field = $ext_content_field; - $this->ext_content_index_field = $ext_content_index_field; - $this->search_table = $this->modx->getFullTableName($search_table); - $this->search_fields = $this->cleanIn($search_fields); -} + public function __construct($modx, $params, $min_length = 2, $ext_content_field = 'content_with_tv', $ext_content_index_field = 'content_with_tv_index', $search_table = 'evosearch_table', $content_table = 'site_content', $search_fields = 'pagetitle,longtitle,description,introtext,content') + { + $this->modx = $modx; + $this->params = $params; + $this->phpmorphy_dir = MODX_BASE_PATH . 'assets/lib/phpmorphy'; + $this->dict_dir = $this->phpmorphy_dir . '/dicts'; + $this->min_length = $min_length; + $this->dicts = $this->getDicts($this->params['dicts']); + $this->id = $this->params['id']; + $this->content_table_name = $content_table; + $this->content_table = $this->modx->getFullTableName($this->content_table_name); + $this->ext_content_field = $ext_content_field; + $this->ext_content_index_field = $ext_content_index_field; + $this->search_table = $this->modx->getFullTableName($search_table); + $this->search_fields = $this->cleanIn($search_fields); + } -public function getDicts($dicts) -{ - return explode(',', $this->cleanIn($dicts)); -} + public function getDicts($dicts) + { + return explode(',', $this->cleanIn($dicts)); + } -public function Words2BaseForm($text) -{ - require_once($this->phpmorphy_dir . '/src/common.php'); - - // set some options - $opts = array( - // storage type, follow types supported - // PHPMORPHY_STORAGE_FILE - use file operations(fread, fseek) for dictionary access, this is very slow... - // PHPMORPHY_STORAGE_SHM - load dictionary in shared memory(using shmop php extension), this is preferred mode - // PHPMORPHY_STORAGE_MEM - load dict to memory each time when phpMorphy intialized, this useful when shmop ext. not activated. Speed same as for PHPMORPHY_STORAGE_SHM type - 'storage' => PHPMORPHY_STORAGE_MEM, - // Extend graminfo for getAllFormsWithGramInfo method call - 'with_gramtab' => false, - // Enable prediction by suffix - 'predict_by_suffix' => true, - // Enable prediction by prefix - 'predict_by_db' => true - ); - - $words = $this->makeWordsFromText($text); - $bulk_words = $this->makeBulkWords($words); - - $fullList = array(); - foreach ($this->dicts as $dict) { - // Create descriptor for dictionary located in $dir directory with russian language - $dict_bundle = new phpMorphy_FilesBundle($this->dict_dir, $dict); - - // Create phpMorphy instance - $morphy = new phpMorphy($dict_bundle, $opts); - - //get base form for all words - $base_form = array(); - foreach ($bulk_words as $bulk_word) { - $base_form[] = $morphy->getBaseForm($bulk_word); - } - - if ( is_array($base_form) && count($base_form) ) { - foreach ( $base_form as $k => $v ) { - if ( is_array($v) ) { - foreach ( $v as $v1 ) { - if ( strlen($v1) > $this->min_length ) { - $fullList[] = $v1; + public function Words2BaseForm($text) + { + require_once($this->phpmorphy_dir . '/src/common.php'); + + // set some options + $opts = array( + // storage type, follow types supported + // PHPMORPHY_STORAGE_FILE - use file operations(fread, fseek) for dictionary access, this is very slow... + // PHPMORPHY_STORAGE_SHM - load dictionary in shared memory(using shmop php extension), this is preferred mode + // PHPMORPHY_STORAGE_MEM - load dict to memory each time when phpMorphy intialized, this useful when shmop ext. not activated. Speed same as for PHPMORPHY_STORAGE_SHM type + 'storage' => PHPMORPHY_STORAGE_MEM, + // Extend graminfo for getAllFormsWithGramInfo method call + 'with_gramtab' => false, + // Enable prediction by suffix + 'predict_by_suffix' => true, + // Enable prediction by prefix + 'predict_by_db' => true + ); + + $words = $this->makeWordsFromText($text); + $bulk_words = $this->makeBulkWords($words); + + $fullList = array(); + foreach ($this->dicts as $dict) { + // Create descriptor for dictionary located in $dir directory with russian language + $dict_bundle = new phpMorphy_FilesBundle($this->dict_dir, $dict); + + // Create phpMorphy instance + $morphy = new phpMorphy($dict_bundle, $opts); + + //get base form for all words + $base_form = array(); + foreach ($bulk_words as $bulk_word) { + $base_form[] = $morphy->getBaseForm($bulk_word); + } + + if (is_array($base_form) && count($base_form)) { + foreach ($base_form as $k => $v) { + if (is_array($v)) { + foreach ($v as $v1) { + if (strlen($v1) > $this->min_length) { + $fullList[] = $v1; + } } } } } } + //$words = join(' ', array_keys($fullList)); + return implode(' ', $fullList); } - //$words = join(' ', array_keys($fullList)); - $words = implode(' ', $fullList); - return $words; -} -public function cleanIn($text, $quote = false) -{ - $out = $text; - if (!$quote) { - $out = str_replace(', ', ',', trim($text)); + public function cleanIn($text, $quote = false) + { + if ($quote) { + return $text; + } + return str_replace(', ', ',', trim($text)); } - return $out; -} -public function makeSQLForSelectWords () -{ - $where = ' AND id IN(' . $this->id . ')'; - $order = ' ORDER BY id ASC '; - $limit = ' LIMIT 0,1'; - if (isset($this->params['reindex']) && (int)$this->params['reindex'] != 0) { - $where = ''; - $limit = ' LIMIT ' . (isset($this->params['offset']) ? (int)$this->params['offset'] : 0) . ',' . (isset($this->params['rowsperonce']) ? (int)$this->params['rowsperonce'] : 1); + public function makeSQLForSelectWords() + { + if (!isset($this->params['reindex']) || (int)$this->params['reindex'] == 0) { + $where = ' AND id IN(' . $this->id . ')'; + $limit = ' LIMIT 0,1'; + } else { + $where = ''; + if (!isset($this->params['rowsperonce'])) { + if (!isset($this->params['offset'])) { + $limit = ' LIMIT 0,1'; + } else { + $limit = ' LIMIT ' . ((int)$this->params['offset']) . ',1'; + } + } else { + if (!isset($this->params['offset'])) { + $limit = ' LIMIT 0,' . ((int)$this->params['rowsperonce']); + } else { + $limit = ' LIMIT ' . ((int)$this->params['offset']) . ',' . ((int)$this->params['rowsperonce']); + } + } + } + if ($this->params['unpublished'] == '0') { + $where .= ' AND published=1 '; + } + if ($this->params['deleted'] == '0') { + $where .= ' AND deleted=0 '; + } + if ($this->params['excludeTmpls'] != '') { + $where .= ' AND template NOT IN(' . $this->params['excludeTmpls'] . ') '; + } + if ($this->params['excludeIDs'] != '') { + $where .= ' AND id NOT IN(' . $this->params['excludeIDs'] . ') '; + } + return sprintf( + "SELECT id,%s FROM %s WHERE searchable = 1 %s ORDER BY id ASC %s", + $this->search_fields, + $this->content_table, + $where, + $limit + ); } - $where .= ($this->params['unpublished'] == '0' ? ' AND published=1 ' : ''); - $where .= ($this->params['deleted'] == '0' ? ' AND deleted=0 ' : ''); - $where .= ($this->params['excludeTmpls'] != '' ? ' AND template NOT IN(' . $this->params['excludeTmpls'] . ') ' : ''); - $where .= ($this->params['excludeIDs'] != '' ? ' AND id NOT IN(' . $this->params['excludeIDs'] . ') ' : ''); - $sql = "SELECT id," . $this->search_fields . " FROM " . $this->content_table . " WHERE searchable = 1" . $where . $order . $limit; - return $sql; -} -public function emptyExcluded() -{ - if (isset($this->params['reindex']) && (int)$this->params['reindex'] != 0) { - $where = ''; + public function emptyExcluded() + { + if (!isset($this->params['reindex']) || (int)$this->params['reindex'] == 0) { + return; + } $ids = array(); $where = ($this->params['excludeTmpls'] != '' ? ' template IN(' . $this->cleanIn($this->params['excludeTmpls']) . ') ' : ''); $where = $where . ($this->params['excludeIDs'] != '' ? ($where != '' ? ' OR ' : '') . ' id IN(' . $this->cleanIn($this->params['excludeIDs']) . ') ' : ''); if ($where != '') { - $q = $this->modx->db->select("id" , $this->content_table , $where); + $q = $this->modx->db->select("id", $this->content_table, $where); while ($row = $this->modx->db->getRow($q)) { $ids[] = $row['id']; } } - if (!empty($ids)) { - $del = $this->modx->db->delete( - $this->search_table, - "docid IN(" . implode(',', $ids) . ") AND `table`='" . $this->content_table_name . "'" - ); + + if (empty($ids)) { + return; } + + $this->modx->db->delete( + $this->search_table, + "docid IN(" . implode(',', $ids) . ") AND `table`='" . $this->content_table_name . "'" + ); } -} -public function makeWordsFromText($text) -{ - $words = array(); - $words = preg_replace('#\[.*\]#isU', '', $text); - $words = str_replace(array('–', '»', '«', '↓', '→', '—'), array('', '', '', '', '', ''), $words); - $words = preg_split('#\s|[,.:;!?"\'()]#', $text, -1, PREG_SPLIT_NO_EMPTY); - return $words; -} + public function makeWordsFromText($text) + { + $words = preg_replace('#\[.*]#isU', '', $text); + $words = str_replace( + array('–', '»', '«', '↓', '→', '—'), + array('', '', '', '', '', ''), + $words + ); + $words = preg_split('#\s|[,.:;!?"\'()]#', $text, -1, PREG_SPLIT_NO_EMPTY); + return $words; + } -public function makeBulkWords($words, $upper = true) -{ - $bulk_words = array(); - foreach ($words as $v) { - if (strlen($v) > $this->min_length) { - //$bulk_words[] = $upper ? strtoupper($v) : $v; - $bulk_words[] = $v; + public function makeBulkWords($words, $upper = true) + { + $bulk_words = array(); + foreach ($words as $v) { + if (strlen($v) > $this->min_length) { + //$bulk_words[] = $upper ? strtoupper($v) : $v; + $bulk_words[] = $v; + } } + return $bulk_words; } - return $bulk_words; -} -public function injectTVs($id, $content) -{ - $tvs = array(); - if ($this->params['TvNames'] != '') { - $TvNames = explode(',', $this->params['TvNames']); - $TvValues = $this->modx->getTemplateVarOutput($TvNames, $id); - $TvElements = $this->modx->getTemplateVars($TvNames, 'name,elements', $id, 'all'); - if (!empty($TvElements) && !empty($TvValues)) { - foreach ($TvElements as $k => $el) { - $tv_name = $el['name']; - //если мы получаем значения TV из дерева с помощью сниппета multiParams, то в поле elements у него будет getParamsFromTree - //для таких элементов берем из дерева их истинные значения - //для остальных - оставляем оригинальные значения TV - if (stristr($el['elements'], 'getParamsFromTree') === FALSE) { - $tvs[] = str_replace('||', ' ', $TvValues[$tv_name]); - } else { - $docs = str_replace('||', ',', $TvValues[$tv_name]); - if ($docs && $docs != '') { - $q = $this->modx->db->query("SELECT id,pagetitle FROM " . $this->content_table . " WHERE id IN(" . $docs . ")"); - while ($row = $this->modx->db->getRow($q)) { - $tvs[] = $row['pagetitle']; + public function injectTVs($id, $content) + { + $tvs = array(); + if ($this->params['TvNames'] != '') { + $TvNames = explode(',', $this->params['TvNames']); + $TvValues = $this->modx->getTemplateVarOutput($TvNames, $id); + $TvElements = $this->modx->getTemplateVars($TvNames, 'name,elements', $id, 'all'); + if (!empty($TvElements) && !empty($TvValues)) { + foreach ($TvElements as $k => $el) { + $tv_name = $el['name']; + //если мы получаем значения TV из дерева с помощью сниппета multiParams, то в поле elements у него будет getParamsFromTree + //для таких элементов берем из дерева их истинные значения + //для остальных - оставляем оригинальные значения TV + if (stristr($el['elements'], 'getParamsFromTree') === FALSE) { + $tvs[] = str_replace('||', ' ', $TvValues[$tv_name]); + } else { + $docs = str_replace('||', ',', $TvValues[$tv_name]); + if ($docs && $docs != '') { + $q = $this->modx->db->query("SELECT id,pagetitle FROM " . $this->content_table . " WHERE id IN(" . $docs . ")"); + while ($row = $this->modx->db->getRow($q)) { + $tvs[] = $row['pagetitle']; + } } } } } } + $content .= !empty($tvs) ? ' ' . implode(' ', $tvs) . ' ' : ''; + $prepare = $this->invokePrepare([ + 'id' => $id, 'content' => $content, 'event' => 'OnAfterInjectTVs' + ]); + if (!$prepare || !is_array($prepare) || !isset($prepare['content'])) { + return $content; + } + return $prepare['content']; } - $content .= !empty($tvs) ? ' ' . implode(' ', $tvs) . ' ' : ''; - $prepare = $this->invokePrepare(array('id' => $id, 'content' => $content, 'event' => 'OnAfterInjectTVs')); - if ($prepare && is_array($prepare) && isset($prepare['content'])) { - $content = $prepare['content']; - } - return $content; -} -private function checkColumnExists($columnname, $table) -{ - $columns = $this->modx->db->getTableMetaData($table); - return isset($columns[$columnname]); -} + private function checkColumnExists($columnname, $table) + { + $columns = $this->modx->db->getTableMetaData($table); + return isset($columns[$columnname]); + } -private function createSearchTable() -{ - $sql = ' + private function createSearchTable() + { + $sql = ' CREATE TABLE IF NOT EXISTS ' . $this->modx->getFullTableName('evosearch_table') . ' ( `id` int(10) NOT NULL AUTO_INCREMENT, `docid` int(10) NOT NULL, @@ -225,32 +257,34 @@ private function createSearchTable() FULLTEXT KEY `pagetitle` (`pagetitle`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; '; - $this->modx->db->query($sql); - //$sql = "ALTER TABLE " . $this->content_table . " ADD FULLTEXT(`pagetitle`)"; - //$this->modx->db->query($sql); -} + $this->modx->db->query($sql); + //$sql = "ALTER TABLE " . $this->content_table . " ADD FULLTEXT(`pagetitle`)"; + //$this->modx->db->query($sql); + } -public function prepareRun() -{ - $this->createSearchTable(); -} + public function prepareRun() + { + $this->createSearchTable(); + } -public function updateSearchTable($fields) -{ - $up = false; - if (!empty($fields)) { + public function updateSearchTable($fields) + { + if (empty($fields)) { + return false; + } $keys = '`' . implode('`,`', array_keys($fields)) . '`'; $values = "'" . implode("','", array_values($fields)) . "'"; $sql = "INSERT INTO " . $this->search_table . " (" . $keys . ") VALUES (" . $values . ") ON DUPLICATE KEY UPDATE `" . $this->ext_content_field . "`='" . $fields[$this->ext_content_field] . "', `" . $this->ext_content_index_field . "`='" . $fields[$this->ext_content_index_field] . "', `pagetitle`='" . $fields['pagetitle'] . "'"; - $up = $this->modx->db->query($sql); + + return $this->modx->db->query($sql); } - return $up; -} -public function invokePrepare($data) -{ - $out = false; - if (isset($this->params['prepare']) && !empty(trim($this->params['prepare']))) { + public function invokePrepare($data) + { + if (!isset($this->params['prepare']) || empty(trim($this->params['prepare']))) { + return false; + } + $prepare = trim($this->params['prepare']); if (strpos($prepare, '->') > 0) { //вариант className->classMethod @@ -258,16 +292,10 @@ public function invokePrepare($data) } switch (true) { case is_callable($prepare): - $out = call_user_func($prepare, $data); - break; + return call_user_func($prepare, $data) ?: false; case is_scalar($prepare): - $out = $this->modx->runSnippet($prepare, array('data' => $data)); - break; - default: - break; + return $this->modx->runSnippet($prepare, array('data' => $data)) ?: false; } + return false; } - return ($out && !empty($out)) ? $out : false; -} - } diff --git a/assets/snippets/evoSearch/snippet.class.php b/assets/snippets/evoSearch/snippet.class.php index e1ee786..5ad124e 100644 --- a/assets/snippets/evoSearch/snippet.class.php +++ b/assets/snippets/evoSearch/snippet.class.php @@ -427,7 +427,7 @@ public function makeAddLikeCond($search_field = 'pagetitle', $separator = 'AND', if (!$this->search_words) { return ''; } - + foreach ($this->search_words as $word) { $word = mb_strtolower($word, "UTF-8"); $tmp[] = " LOWER(`" . $search_field . "`) REGEXP '[[:<:]]" . $word . "[[:>:]]'";