Пример #1
0
 /**
  *
  */
 public function get_query_string()
 {
     if (!$this->query) {
         return null;
     }
     return $this->query->get('query_string');
 }
Пример #2
0
 /**
  * @param nc_search_query $query
  * @return string|null
  */
 public function translate(nc_search_query $query)
 {
     $root = $query->parse();
     // empty query?
     if ($root instanceof nc_search_query_expression_empty) {
         return null;
     }
     // queries with only excluded terms are forbidden
     if ($root->is_excluded() || $root instanceof nc_search_query_expression_not) {
         return null;
     }
     // initialize variables used for a translation of the query
     $this->query_builder = $builder = new nc_search_provider_index_querybuilder($query, $this);
     $this->stack = array();
     $this->unknown_required_terms = array();
     $this->can_skip_fts_query = false;
     $this->implicit_field_match = $this->expression_requires_implicit_match($root);
     // get language filters chain to use in this.translate_term() etc.
     $language = $query->get('language');
     if (!$language) {
         $language = nc_Core::get_object()->lang->detect_lang();
     }
     $query_context = new nc_search_context(array("search_provider" => get_class($this->provider), "language" => $language, "action" => "searching"));
     $this->text_filters = nc_search_extension_manager::get('nc_search_language_filter', $query_context)->stop_on(array());
     // Ready to go!
     // translate the expression tree
     $index_query = $this->dispatch_translate($root);
     if (!$this->can_skip_fts_query) {
         // interval search at the root for a numeric field
         // index query is required almost always
         if (!strlen($index_query)) {
             return null;
         }
         // e.g. if query string consists of stop words
         if ($index_query == "____" || $index_query == "(____)") {
             // empty query
             return null;
         }
     }
     // set query for the combined index in the query builder
     if ($index_query && !$root->get_field()) {
         $builder->set_index_match($index_query);
     }
     // there are required terms that are not in the index, so don’t make a query
     if ($this->unknown_required_terms) {
         return null;
     }
     // использовать временную таблицу для уточняющих запросов при поиске фраз?
     $use_temp_table = !nc_search::get_setting('DatabaseIndex_AlwaysGetTotalCount') && $this->expression_has_phrase($root);
     // return SQL query string
     $result = $builder->get_sql_query($use_temp_table);
     return $result;
 }
Пример #3
0
 /**
  *
  * @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;
 }
Пример #4
0
 /**
  * Получить массив с заголовками страниц для autocomplete
  * @param string $input
  * @param string $language
  * @param integer $site_id
  * @return array   элементы массива: array("label" => "Page Title", "url" => "/path/")
  */
 public function suggest_titles($input, $language, $site_id)
 {
     $limit = nc_search::get_setting('NumberOfSuggestions');
     $index_search_results = "";
     $document_order_by_id = "1";
     // поиск в индексе (то есть будут варианты после обработки фильтрами - базовая форма)
     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 : '') . ")";
         $query = new nc_search_query($query_string);
         $query->set('limit', $limit)->set('options_to_fetch', array('title', 'site_id', 'path'))->set('language', $language)->set('area', "site" . (int) $site_id);
         // some lower level magic
         $translator = new nc_search_provider_index_translator($this);
         $document_ids = $this->get_db()->get_col($query->translate($translator));
         if ($document_ids) {
             $document_ids = join(", ", $document_ids);
             $index_search_results = "OR `Document_ID` IN ({$document_ids})\n";
             $document_order_by_id = "FIELD(`Document_ID`, {$document_ids})";
         }
     }
     // поиск точного соответствия в таблице с документами
     $like_expression = '`Title` LIKE "' . nc_search_util::db_escape($input) . '%" ';
     $query = "SELECT `Catalogue_ID`, `Path`, `Title` FROM `%t%`\n" . "WHERE {$like_expression}\n" . $index_search_results . "ORDER BY {$like_expression}, {$document_order_by_id}\n" . "LIMIT {$limit}";
     $documents = new nc_search_result();
     $documents->select_from_database($query);
     $suggestions = array();
     // собственно подсказки
     /** @var $doc nc_search_document */
     foreach ($documents as $doc) {
         $suggestions[] = array("label" => $doc->get('title'), "url" => $doc->get('url'));
     }
     return $suggestions;
 }
Пример #5
0
 /**
  * @param bool $use_temp_table
  * @return string|array
  */
 public function get_sql_query($use_temp_table)
 {
     $index = $this->index_table;
     $has_conditions = count($this->condition_stack[0]) > 0;
     $create_temp_table = $use_temp_table && $has_conditions;
     if (!$create_temp_table) {
         foreach ($this->condition_joins as $table_name => $join_condition) {
             $this->add_left_join($table_name);
         }
     }
     // prepare ORDER BY and value for the `Score` column
     $ranking_select = "1 AS `Score` ";
     $order_by = "";
     $sort_field_name = $this->query->get('sort_by');
     if ($sort_field_name == "last_modified") {
         $this->add_join($this->document_table);
         $order_by = "ORDER BY `{$this->document_table}`.`LastModified` DESC";
     } elseif ($sort_field_name) {
         // some other field
         /** @var $sort_fields nc_search_provider_index_field_manager */
         $sort_fields = $this->translator->get_fields('name', $sort_field_name)->where('is_sortable', true);
         if (count($sort_fields)) {
             $order_by = array();
             /** @var $f nc_search_provider_index_field */
             foreach ($sort_fields as $f) {
                 $field_table_name = $f->get_field_table_name();
                 $this->add_left_join($field_table_name);
                 $order_by[] = "`{$field_table_name}`.`RawData`";
             }
             $order_by = "ORDER BY IFNULL(" . join(", ", $order_by) . ", 0) " . ($this->query->get('sort_direction') == SORT_DESC ? "DESC" : "ASC");
         }
     }
     if (!$order_by) {
         // standard sorting by relevance
         $ranking_select = $this->term_ranking_calculation();
         $order_by = "ORDER BY `Score` DESC";
     }
     // Prepare area condition (will need join)
     $area_condition = "";
     $area = $this->query->get('area');
     if ($area) {
         $this->add_join($this->document_table);
         if (!$area instanceof nc_search_area) {
             $area = new nc_search_area($area);
         }
         $area_condition = "AND " . $area->get_sql_condition() . "\n";
     }
     // Compose query
     // SELECT
     $query_string = "SELECT " . ($create_temp_table ? "" : "SQL_CALC_FOUND_ROWS ") . "`{$index}`.`Document_ID`,\n" . $ranking_select;
     // FROM
     $query_string .= "FROM `{$index}` FORCE INDEX (`Content`)\n";
     // INNER JOINs
     foreach ($this->joins as $j) {
         $query_string .= "{$j}\n";
     }
     // LEFT JOINs
     foreach ($this->left_joins as $j) {
         $query_string .= "{$j}\n";
     }
     // WHERE [index_match_condition]
     // (1) Index match;                  (2) site/path filter
     $query_string .= "WHERE {$this->index_match}\n{$area_condition}";
     // (3) Extra conditions
     if ($has_conditions && !$use_temp_table) {
         $query_string .= "AND " . join("\nAND ", $this->condition_stack[0]);
     }
     // ORDER
     $query_string .= "\n{$order_by}";
     if ($use_temp_table) {
         $t = $this->get_temporary_table_name();
         $queries = array("temp_table" => $t, "prefilter" => $query_string, "refinement" => "SELECT t.`Document_ID`, t.`Rank`\n" . "FROM `{$t}` AS t\n" . join("\n", $this->condition_joins) . "\n" . "WHERE t.`Rank` >= IFNULL(@rank_value,0) AND" . join("\nAND ", $this->condition_stack[0]) . "\n" . "ORDER BY t.`Rank`\n");
         return $queries;
     } else {
         // LIMIT, OFFSET
         $query_string .= "\nLIMIT " . (int) $this->query->get('limit') . "\nOFFSET " . (int) $this->query->get('offset');
         return $query_string;
     }
 }
Пример #6
0
 /**
  * @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;
 }
Пример #7
0
 /**
  * @param nc_search_query $query
  * @return mixed
  */
 public function translate(nc_search_query $query)
 {
     return $this->dispatch_translate($query->parse());
 }
Пример #8
0
 /**
  *
  * @param string|nc_search_query $query
  * @param boolean $highlight_matches
  * @throws nc_search_exception
  * @return nc_search_result
  */
 public static function find($query, $highlight_matches = true)
 {
     if (self::should('EnableSearch')) {
         if (is_string($query)) {
             $query = new nc_search_query($query);
         }
         nc_search_util::set_utf_locale($query->get('language'));
         $result = self::get_provider()->find($query, $highlight_matches);
         nc_search_util::restore_locale();
         return $result;
     } else {
         throw new nc_search_exception("Search module is disabled");
     }
 }