/** * Выполнение запроса * @param nc_search_query $query * @param boolean $should_highlight * @return nc_search_result */ public function find(nc_search_query $query, $should_highlight = true) { $db = $this->get_db(); $id_list = "0"; $translator = new nc_search_provider_index_translator($this); // get IDs $select_ids_query = $query->translate($translator); $limit = (int) $query->get('limit'); $offset = (int) $query->get('offset'); $total_hits_unknown = false; if ($select_ids_query) { // neither null nor empty string $db->last_error = null; if (is_array($select_ids_query)) { // will need to create a temporary table; no final row count $prefilter_query = $select_ids_query["prefilter"]; $refinement_query = $select_ids_query["refinement"]; $query_hash = sha1($prefilter_query); // (1) create temporary table $db->query("SET @rank=0"); $db->query("CREATE TEMPORARY TABLE `{$select_ids_query['temp_table']}` " . "(INDEX (`Rank`,`Document_ID`)) " . "SELECT filtered.`Document_ID`, @rank := @rank+1 AS `Rank` " . "FROM ({$prefilter_query}) AS filtered"); // (2) check for cached offset values so we won't need to check from the beginning of the pre-filtered list $rank_table = $this->last_rank_table_name; $db->query("DELETE FROM `{$rank_table}` WHERE `Time` < NOW() - INTERVAL {$this->rank_cache_interval}"); $cached_rank = $offset ? $db->get_row("SELECT `Offset`, `Rank` FROM `{$rank_table}`\n" . "WHERE `QueryHash` = '{$query_hash}' AND `Offset` <= {$offset}\n" . "ORDER BY `Offset` DESC\nLIMIT 1", ARRAY_A) : null; if ($cached_rank) { $refinement_offset = $offset - $cached_rank["Offset"]; $refinement_rank_value = $cached_rank["Rank"]; } else { $refinement_offset = $offset; $refinement_rank_value = 0; } $db->query("SET @rank_value = {$refinement_rank_value}"); // (3) select IDs $db->query("{$refinement_query}\n" . "LIMIT " . ($limit + 1) . " OFFSET {$refinement_offset}"); $ids = $db->get_col(); $id_list = join(', ', $ids); if (count($ids) > $limit) { // has next page // total result count is set to (current page + 1 page) // so there will be a paginator $total_hits = $offset + 2 * $limit; $total_hits_unknown = true; // save rank for the next page $last_rank = $db->get_var(null, 1, $limit); $db->query("REPLACE INTO `{$this->last_rank_table_name}`\n SET `Time` = NOW(),\n `QueryHash` = '{$query_hash}',\n `Offset` = " . ($offset + $limit) . ",\n `Rank` = {$last_rank}"); } else { $total_hits = $offset + count($ids); } } else { // select IDs in a single query $id_list = join(', ', $db->get_col($select_ids_query)); $total_hits = $db->last_error ? 0 : $db->get_var("SELECT FOUND_ROWS()"); } } else { // Translator thinks there won't be any results (terms in the query are not in the index) // we could get $translator->get_unknown_terms(), but it is of no use for the correctors... :( $total_hits = 0; } // make 'result' object $result = new nc_search_result(array(), $total_hits); $result->set_query($query); /* @todo use $total_hits_unknown to mark that total count is not known */ if (!$should_highlight) { $result->disable_highlighting(); } if ($total_hits && $id_list) { // get field list $doc = new nc_search_result_document(); $fields = $doc->get_column_names($query->get('options_to_fetch')); // get document data $doc_query = "SELECT {$fields} FROM `%t%` WHERE `Document_ID` IN ({$id_list}) " . "ORDER BY FIELD(`Document_ID`, {$id_list}) " . "LIMIT {$limit}"; $result->select_from_database($doc_query); } return $result; }
/** * * @param string $query_string * @param string|array $area * @param string $params Параметры, через амперсанд * - field - поле поиска. Допустимые значения: 'title' * - interval - непустое значение, если включена фильтрация по дате * - intervalvalue - значение интервала * - intervalunit - тип интервала (hour, day, week, month) * - sortby - сортировка. Если пустое значение - сортировка по релевантности. * Допустимые значения: last_updated или имя поля, по которому разрешена сортировка * - sortdirection - desc (по умолчанию), asc * - language - язык результатов, по умолчанию определяется автоматически * - curPos - текущая позиция (номер первого результата) * - recNum - количество результатов на странице, по умолчанию 10 (берется из * настроек компонента в разделе) * - correct - пытаться исправить запросы, не давшие результатов (по умолчанию * равно соответствующей настройки модуля) * - nologging - не записывать запрос в журнал запросов (при просмотре * результатов из админки, чтобы не искажать картину запросов) * @return nc_search_data_persistent_collection */ public function get_results($query_string, $area = "", $params = "") { if (!nc_search::should('EnableSearch')) { return new nc_search_result(); } // return empty collection $start_time = microtime(true); $query_string = (string) $query_string; global $nc_core; parse_str($params, $params); if (isset($params["field"]) && $params["field"] && nc_search::should('AllowFieldSearch')) { $query_string = "{$params['field']}:({$query_string})"; } $query = new nc_search_query($query_string); $has_interval = isset($params["interval"]) && isset($params["intervalvalue"]) && isset($params["intervalunit"]) && $params["interval"] && $params["intervalvalue"] && $params["intervalunit"]; if ($has_interval) { $timestamp = strtotime("-{$params['intervalvalue']} {$params['intervalunit']}"); $query->set('modified_after', strftime("%Y%m%d%H%M%S", $timestamp)); } $allow_sort = isset($params["sortby"]) && $params["sortby"] && nc_search::should('AllowFieldSearch'); if ($allow_sort) { $query->set('sort_by', $params["sortby"]); if (isset($params["sortdirection"]) && strtoupper($params["sortdirection"]) == 'ASC') { $query->set('sort_direction', SORT_ASC); } } if (isset($params["curPos"]) && $params["curPos"]) { $query->set('offset', (int) $params["curPos"]); } if (isset($params["recNum"]) && $params["recNum"]) { $query->set('limit', (int) $params["recNum"]); } if ($area) { if (is_array($area)) { $area = join(" ", $area); } $query->set('area', $area); } $language = isset($params["language"]) && $params["language"] ? $params["language"] : $nc_core->lang->detect_lang(1); $query->set('language', $language); $shutdown_page_path = nc_folder_path($nc_core->subdivision->get_current('Subdivision_ID')); register_shutdown_function('nc_search_shutdown', $shutdown_page_path, $query_string); $query_error = false; try { $results = nc_search::find($query); } catch (Exception $e) { $query_error = true; $results = new nc_search_result(); $results->set_query($query)->set_error_message($e->getMessage()); } $results->set_output_encoding(nc_core('NC_CHARSET')); // попробуем исправить, если не было результатов? $try_to_correct = $results->get_total_count() == 0 && !$query_error && (isset($params["correct"]) && $params["correct"] || nc_search::should('TryToCorrectQueries')) && preg_match_all('/[\\pL\\pN\\?\\*]+/u', $query_string, $tmp) <= nc_search::get_setting('MaxQueryLengthForCorrection'); if ($try_to_correct) { $context = new nc_search_context(array("language" => $language, "action" => "searching")); $correctors = nc_search_extension_manager::get('nc_search_language_corrector', $context)->get_all(); if (sizeof($correctors)) { $phrase = new nc_search_language_corrector_phrase($query_string); $rewritten_query = clone $query; foreach ($correctors as $corrector) { if ($corrector->correct($phrase)) { // что-то подправили // попробуем поискать! $rewritten_query->set('query_string', $phrase->to_string()); try { $corrected_results = nc_search::find($rewritten_query); if (sizeof($corrected_results)) { $results = $corrected_results; $results->set_correction_suggestion($phrase->get_suggestion()); $results->set_output_encoding(nc_core('NC_CHARSET')); break; // exit "foreach corrector" } } catch (Exception $e) { // может упасть, например, если у изменённого слова есть несколько базовых форм... } } // of "something changed" } // of "foreach corrector" } // end of "has correctors" } // end of "if ($try_to_correct)" $will_log = true; if (isset($params['nologging']) && $params['nologging'] && strlen($query_string)) { // только очень крутым чувакам разрешается не оставлять следов if (isset($GLOBALS['AUTH_USER_ID']) && isset($GLOBALS['perm']) && $GLOBALS["perm"]->isAccess(NC_PERM_MODULE)) { $will_log = false; } } if ($will_log && nc_search::should('SaveQueryHistory') && $query->get('offset') == 0) { $ip = ip2long($_SERVER['REMOTE_ADDR']); // achtung! не будет работать с IPv6! if ($ip > 0x7fffffff) { $ip -= 0x100000000; } // produce a signed 4-byte integer on 64-bit systems $query->set('results_count', $results->get_total_count())->set('user_ip', $ip)->set('user_id', $GLOBALS['AUTH_USER_ID'])->set('site_id', $GLOBALS['catalogue'])->save(); } $results->set_search_time(microtime(true) - $start_time); return $results; }
/** * Поиск по индексу * @param nc_search_query $query * @param boolean $should_highlight * @return nc_search_result */ public function find(nc_search_query $query, $should_highlight = true) { nc_search::set_current_context(new nc_search_context(array('search_provider' => get_class($this), 'action' => 'searching', 'language' => $query->get('language')))); $index = $this->get_index(); $lucene_query = $this->get_lucene_query($query); if ($query->get('sort_by')) { // custom sort $lucene_result = $index->find($lucene_query, $query->get('sort_by'), SORT_STRING, $query->get('sort_direction')); } else { $lucene_result = $index->find($lucene_query); } $total_hits = count($lucene_result); $result = new nc_search_result(array(), $total_hits); $result->set_query($query); if (!$should_highlight) { $result->disable_highlighting(); } nc_search::set_current_context(); // truncate to get the requested page only $lucene_result = array_slice($lucene_result, $query->get('offset'), $query->get('limit')); // сформировать nc_search_result $result_ids = array(); foreach ($lucene_result as $hit) { $result_ids[] = $hit->doc_id; } if (count($result_ids)) { foreach ($result_ids as $i => $id) { $result_ids[$i] = (int) trim($id, "x"); } $id_list = join(", ", $result_ids); $doc = new nc_search_result_document(); $fields = $doc->get_column_names($query->get('options_to_fetch')); $query = "SELECT {$fields} FROM `%t%` WHERE `Document_ID` IN ({$id_list}) " . "ORDER BY FIELD(`Document_ID`, {$id_list})"; $result->select_from_database($query); } return $result; }