/** * Запланировать запуск переиндексирования области или правила в указанное время * @param string $area_string * @param integer $timestamp */ public static function schedule_indexing($area_string, $timestamp) { // Если данная область уже поставлена в очередь на более раннее или ближайшее // время, не нужно добавлять ещё раз $interval = $timestamp + nc_search::get_setting('MinScheduleInterval'); $intent = nc_search::load('nc_search_scheduler_intent', "SELECT * FROM `%t%`" . " WHERE `StartTime` <= {$interval}" . " AND `AreaString` = '" . nc_search_util::db_escape($area_string) . "'")->first(); // type is ignored if ($intent) { // уже есть такое расписание! if ($intent->get('start_time') > $timestamp) { $intent->set('start_time', $timestamp); // let's run it sooner } } else { $intent = new nc_search_scheduler_intent(array('start_time' => $timestamp, 'type' => nc_search_scheduler_intent::ON_REQUEST, 'area_string' => $area_string)); } $intent->save(); }
/** * @param array $terms * @return array */ public function filter(array $terms) { if (!nc_search::should('RemoveStopwords')) { return $terms; } $language = $this->context->get('language'); if (!isset(self::$lists[$language])) { $query = "SELECT * FROM `%t%` WHERE `Language`='" . nc_search_util::db_escape($language) . "'"; self::$lists[$language] = nc_search::load('nc_search_language_stopword', $query, 'word'); } $stop_list = self::$lists[$language]; if (!count($stop_list)) { return $terms; } $result = array(); foreach ($terms as $term) { if (is_array($term)) { // alternative forms foreach ($term as $i => $t) { if ($stop_list->has_key($t)) { unset($term[$i]); } } $terms_left = count($term); if ($terms_left == 1) { $result[] = $term[0]; } elseif ($terms_left > 1) { $result[] = $term; } } elseif (!$stop_list->has_key($term)) { // ordinary term $result[] = $term; } } return $result; }
/** * Обработать следующую ссылку из очереди * @return integer * nc_search_indexer::TASK_STEP_SKIPPED (ничего не сделано), * nc_search_indexer::TASK_STEP_FINISHED (сделан и обработан запрос), * nc_search_indexer::TASK_FINISHED (задача завершена) */ public function next() { $link = $this->task->get_next_link(); if (!$link) { return $this->finalize(); } // ССЫЛОК БОЛЬШЕ НЕТ $done_something = true; // флажок, означающий, что после выполнения задачи, возможно, // следует сделать паузу (в соответствии с настройками) $url = $link->get('url'); $is_disallowed = $this->task->is_url_disallowed($url); if (!$is_disallowed && $this->get_area()->includes($url)) { $response = $this->crawler->get($url); } elseif (!$is_disallowed && nc_search::should("CrawlerCheckLinks") && ($this->is_internal_link($url) || nc_search::should("CrawlerCheckOutsideLinks"))) { // так нам её проверить, да? $response = $this->crawler->head($url); } else { $response = false; $done_something = false; } if ($response) { $code = $response->get_code(); // 0, если ничего не получено (напр., не резолвится домен) $max_doc_size = nc_search::get_setting("CrawlerMaxDocumentSize"); if (!$code || $code == 400 || $code >= 402) { // их разыскивает пилиция (401==Authorization required) $link->set('is_broken', true); $this->task->increment('total_not_found'); } elseif ($response->has_body() && (!$max_doc_size || $response->get_body_length() <= $max_doc_size)) { // есть ответ и он не слишком длинный для нас $this->process_response($response, $link); $this->task->increment('total_processed'); } else { $this->task->increment('total_checked'); } } $link->set('is_processed', true); if ($link->get_id()) { // save the link status (broken, processed) $link->save(); // set ToDelete for the broken links from this page try { $this->query_db("UPDATE `Search_BrokenLink`\n SET `ToDelete` = 1\n WHERE `Referrer_URL` = '" . nc_search_util::db_escape($link->get('url')) . "'"); } catch (Exception $e) { trigger_error($e->getMessage(), E_USER_WARNING); } } return $done_something ? self::TASK_STEP_FINISHED : self::TASK_STEP_SKIPPED; }
/** * Сохраняет поле индекса в указанной таблице (Search_Index или Search_Index_FieldX), * при необходимости разбивает запрос на части таким образом, чтобы запрос * был не более mysql.max_allowed_packet * @param string $table_name * @param int $doc_id * @param string $all_content * @param string $all_raw_data */ protected function store_index_data($table_name, $doc_id, $all_content, $all_raw_data = '') { $db = $this->get_db(); $overhead = 1024; // команды SQL etc. $chunk_size = $this->max_allowed_packet - $overhead; $content_chunks = str_split($all_content, $chunk_size); $raw_chunks = strlen($all_raw_data) ? str_split($all_raw_data, $chunk_size) : array(); $doc_id = (int) $doc_id; unset($all_content, $all_raw_data); $n_content = $n_raw = 0; while (count($content_chunks) || count($raw_chunks)) { $update = $n_content || $n_raw; $query = ($update ? "UPDATE" : "REPLACE INTO") . " `{$table_name}` SET `Document_ID` = {$doc_id}"; $content = array_shift($content_chunks); if (strlen($content)) { $content = nc_search_util::db_escape($content); $query .= ", `Content` = " . ($n_content ? "CONCAT(`Content`, '{$content}')" : "'{$content}'"); $n_content++; } $add_raw = count($content_chunks) == 0 && isset($raw_chunks[0]) && strlen($content) + strlen($raw_chunks[0]) < $chunk_size; // adding raw_data will not cause overflow if ($add_raw) { $raw_data = nc_search_util::db_escape(array_shift($raw_chunks)); $query .= ", `RawData` = " . ($n_raw ? "CONCAT(`RawData`, '{$raw_data}')" : "'{$raw_data}'"); $n_raw++; } if ($update) { $query .= " WHERE `Document_ID` = {$doc_id}"; } $db->query($query); } }
/** * @param $table * @param $fts_qry * @return string */ protected function make_match($table, $fts_qry) { return "MATCH(`{$table}`.`Content`) AGAINST ('" . nc_search_util::db_escape($fts_qry) . "' IN BOOLEAN MODE)"; }
/** * @param $value * @return int|string */ protected function escape_number($value) { if (!is_numeric($value)) { $value = "'" . nc_search_util::db_escape($value) . "'"; } return $value; }
clear_link.click(function() { inp.val(''); clear_link.css('visibility', 'hidden'); }); update(); })($nc); </script> <?php // составляем и выполняем запрос к БД $order_by = "`QueryCount` DESC"; if ($sort_by == 'time') { $order_by = "`Timestamp` DESC"; } elseif ($sort_by == 'query') { $order_by = "`QueryString` ASC"; } $where = array(1); if (strlen($this->get_input('fragment'))) { $where[] = "`QueryString` LIKE '%" . nc_search_util::db_escape($this->get_input('fragment')) . "%'"; } // если установлен день или час, считать, что речь идёт о текущем дне/месяце/годе $from_today = $this->get_input('datetime_from_d') || $this->get_input('datetime_from_H'); $timestamp_from = call_user_func_array('mktime', array((int) $this->get_input('datetime_from_H', 0), (int) $this->get_input('datetime_from_M', 0), 0, (int) $this->get_input('datetime_from_m', $from_today ? date("m") : 1), (int) $this->get_input('datetime_from_d', $from_today ? date("d") : 1), (int) $this->get_input('datetime_from_Y', $from_today ? date("Y") : 2000))); // если установлен день или час, считать, что речь идёт о текущем дне/месяце/годе $to_today = $this->get_input('datetime_to_d') || $this->get_input('datetime_to_H'); $timestamp_to = call_user_func_array('mktime', array((int) $this->get_input('datetime_to_H', 23), (int) $this->get_input('datetime_to_M', 59), 59, (int) $this->get_input('datetime_to_m', $to_today ? date("m") : 12), (int) $this->get_input('datetime_to_d', $to_today ? date("d") : 31), (int) $this->get_input('datetime_to_Y', $to_today ? date("Y") : 2037))); $sql_datetime_from = nc_search_util::sql_datetime($timestamp_from); $sql_datetime_to = nc_search_util::sql_datetime($timestamp_to); if ($sql_datetime_from != '2000-01-01 00:00:00' || $sql_datetime_to != '2037-12-31 23:59:59') { $where[] = "`Timestamp` BETWEEN '{$sql_datetime_from}' AND '{$sql_datetime_to}'"; } if ($results == 'none') { $where[] = '`ResultsCount` = 0'; } elseif ($results == 'matched') {
/** * @param string $input * @param string $language * @param integer $site_id * @return array */ public function suggest_titles($input, $language, $site_id) { $suggestions = array(); // собственно подсказки $titles = array(); $limit = nc_search::get_setting('NumberOfSuggestions'); // поиск в индексе (то есть будут варианты после обработки фильтрами - базовая форма) if (nc_search::should('SearchTitleBaseformsForSuggestions')) { $last_space = strrpos($input, " "); $as_phrase = nc_search::should('SearchTitleAsPhraseForSuggestions'); $b1 = $as_phrase ? '"' : '('; $b2 = $as_phrase ? '"' : ')'; /* @todo сделать проверку на то, что последнее слово является правильным/полным? */ $query_string = "(title:{$b1}{$input}{$b2}" . ($last_space ? " OR title:{$b1}" . trim(substr($input, 0, $last_space)) . $b2 : '') . ") AND site_id:{$site_id}"; $query = new nc_search_query($query_string); $query->set('limit', $limit)->set('options_to_fetch', array('title', 'site_id', 'path'))->set('language', $language); $documents = $this->find($query, false); foreach ($documents as $doc) { $suggestions[] = array("label" => $doc->get('title'), "url" => $doc->get('url')); $titles[] = '"' . nc_search_util::db_escape($doc->get('title')) . '"'; } $titles = array_unique($titles); } // поиск точного соответствия в таблице с документами // по-хорошему следовало бы сначала сделать запрос к БД, а потом к индексу, однако // в случае запроса к индексу не получится так же просто отфильтровать уже совпавшие запросы $query = "SELECT `Catalogue_ID`, `Path`, `Title` FROM `%t%` " . ' WHERE `Title` LIKE "' . nc_search_util::db_escape($input) . '%" ' . ($titles ? " AND `Title` NOT IN (" . join(", ", $titles) . ") " : "") . " ORDER BY `Title` " . " LIMIT {$limit}"; $documents = new nc_search_result(); $documents->select_from_database($query); foreach ($documents as $doc) { array_unshift($suggestions, array("label" => $doc->get('title'), "url" => $doc->get('url'))); } $suggestions = array_slice($suggestions, 0, $limit); return $suggestions; }
<?php if (!class_exists("nc_system")) { die; } $this->get_ui()->add_lists_toolbar(); $query_string = $this->get_input('query'); if (!strlen($query_string)) { $this->redirect("?view=queries"); } $per_page = 100; $offset = (int) $this->get_input('offset'); $query = "SELECT SQL_CALC_FOUND_ROWS q.*, u.Login\n FROM Search_Query AS q \n LEFT JOIN User AS u ON (q.User_ID = u.User_ID)\n WHERE q.QueryString='" . nc_search_util::db_escape($query_string) . "'\n ORDER BY Timestamp DESC\n LIMIT {$per_page} OFFSET {$offset}"; $db = $this->get_db(); $db->query("SET NAMES utf8"); $res = $db->get_results($query, ARRAY_A); $found_rows = $db->get_var("SELECT FOUND_ROWS()"); $db->query("SET NAMES " . nc_core('MYSQL_CHARSET')); // this is actually incorrect: echo "<div class='query_details_header'>", "<b>", sprintf(NETCAT_MODULE_SEARCH_ADMIN_QUERY_ALL_QUERIES, nc_search_util::convert($query_string)), "</b> (", NETCAT_MODULE_SEARCH_ADMIN_QUERY_OPEN_RESULTS_HINT, "):", "</div>"; // таблица с результатами echo "<table class='nc-table nc--large nc--hovered nc--striped list'>\n", "<tr>", "<th class='nc-text-center'>", NETCAT_MODULE_SEARCH_ADMIN_QUERY_TIME, "</th>", "<th class='nc-text-center' width='40%'>", NETCAT_MODULE_SEARCH_ADMIN_QUERY_AREA, "</th>", "<th class='nc-text-center'>", NETCAT_MODULE_SEARCH_ADMIN_QUERY_RESULTS_COUNT, "</th>", "<th class='nc-text-center'>", NETCAT_MODULE_SEARCH_ADMIN_QUERY_USER, "</th>", "<th class='nc-text-center'>", NETCAT_MODULE_SEARCH_ADMIN_QUERY_IP, "</th>", "</tr>\n"; foreach ($res as $row) { $has_area = strlen($row['Area']) > 0; $site_area = new nc_search_area("site{$row['Catalogue_ID']}"); list($site_description) = $site_area->get_description(false); if (!$has_area) { $area_cell = "<td>" . NETCAT_MODULE_SEARCH_ADMIN_RULE_AREA_DESCRIPTION_ALLSITES . "</td>"; } else { $area = new nc_search_area($row['Area'], $row['Catalogue_ID']); $description = array("included" => $area->get_description(false), "excluded" => $area->get_description(true));
protected function load_synonyms($language) { $query = "SELECT * FROM `%t%` WHERE `Language`='" . nc_search_util::db_escape($language) . "'"; self::$lists[$language] = nc_search::load('nc_search_language_synonyms', $query); }
/** * Получение кодов для массива терминов вынесено в отдельный метод для * удобства рекурсивного вызова, которое необходимо при обработке альтернативных * форм слов (когда значение в массиве $terms является массивом) * @param array $terms * @param boolean $create_new * @return array */ protected function get_term_codes(array $terms, $create_new) { $codes = array(); foreach ($terms as $t) { // skip empty terms (think nc_search_language_filter_stopwords) if ($t === null || is_scalar($t) && strlen($t) == 0 || is_array($t) && sizeof($t) == 0) { continue; } if (is_array($t)) { // "alternative forms" $res = join("|", $this->get_term_codes($t, $create_new)); if ($res) { $codes[] = $res; } } else { if (isset($this->terms[$t])) { // this is a known term $codes[] = $this->terms[$t]; } else { if ($create_new) { // should create new records in Search_Index_Term $new_code = $this->get_next_code(); $this->terms[$t] = $codes[] = $new_code; $this->new_term_data[] = "('" . nc_search_util::db_escape($t) . "', '{$new_code}', " . mb_strlen($t, 'UTF-8') . ")"; } } } // else (i.e. $create_new == false and term is unknown): do not add entry to the $codes } return $codes; }
/** * Получить документ из БД по URL (только поля 'id' и 'hash') * @static * @param $site_id * @param $path * @return self|FALSE */ public static function get_hash_by_path($site_id, $path) { $doc = new self(); return $doc->select_from_database("SELECT `Document_ID`, `Hash`\n FROM `{$doc->get_table_name()}`\n WHERE `Catalogue_ID` = " . (int) $site_id . "\n AND `Path` = '" . nc_search_util::db_escape($path) . "'\n LIMIT 1"); }
/** * @param $url * @return string */ protected function make_hash_statement($url) { $unhex = nc_search_util::can_use_binary_columns() ? "UNHEX" : ""; return "{$unhex}(SHA1('" . nc_search_util::db_escape($url) . "'))"; }
<?php /** * Входящие параметры: * - term * * @global $catalogue */ $NETCAT_FOLDER = realpath("../../../../"); require_once "{$NETCAT_FOLDER}/vars.inc.php"; require $INCLUDE_FOLDER . "index.php"; // получение параметров $input = trim($nc_core->input->fetch_get('term')); if (!nc_search::should('EnableQuerySuggest') || nc_search::get_setting('SuggestMode') != 'queries' || mb_strlen($input) < nc_search::get_setting('SuggestionsMinInputLength')) { die("[]"); } $input = $nc_core->utf8->conv($nc_core->NC_CHARSET, 'utf-8', $input); // поиск запросов, начинающихся с указанной подстроки $db->query("SET NAMES 'utf8'"); $query = "SELECT DISTINCT(`QueryString`) AS `label` FROM `Search_Query` \n WHERE `QueryString` LIKE '" . nc_search_util::db_escape($input) . "%'\n AND `ResultsCount` > 0\n ORDER BY `QueryString`\n LIMIT " . (int) nc_search::get_setting('NumberOfSuggestions'); $suggestions = (array) $db->get_results($query, ARRAY_A); if (!$nc_core->NC_UNICODE) { $suggestions = $nc_core->utf8->array_utf2win($suggestions); } print nc_array_json($suggestions);
/** * */ protected function get_path_sql_condition($operator = '=', $template = '') { $site_cond = array(); foreach ($this->get_sites() as $site) { $site_cond[] = $site->get_sql_condition(); } $q = $site_cond ? "(" . join(" OR ", $site_cond) . ")" : "1"; $q .= " AND `{$this->document_table_name}`.`Path` {$operator} '" . nc_search_util::db_escape($this->get_path()) . $template . "'"; return $q; }