Разработчикам приложений PL/SQL, работающим с каталогами, цифровыми библиотеками и репозиториями, часто решают задачу поиска записей или документов, ближе всего соответствующих заданному критерию и намерениям пользователя. Это одно из принципиальных отличий получения информации от стандартных запросов SQL
, которые либо находят совпадения для критерия запроса, либо не находят их. Хорошая система информационного поиска может определить содержимое документа и вернуть документы, в наибольшей степени относящиеся к критерию поиска, даже при отсутствии точного совпадения. Вероятно, самой сложной задачей в области информационного поиска является поддержка индексирования и запросов на разных языках. Например, однобайтовый английский язык использует пробелы для разделения слов. При работе с японским языком, использующим многобайтовый набор символов без пробелов-разделителей, информационный поиск производится совершенно иначе.
Oracle
Text
— программа, входящая и в Oracle
Enterprise
Edition
, и в Oracle
Standard
Edition
— обеспечивает функциональность полнотекстового информационного поиска. Благодаря использованию SQL
для создания индексов, поиска и операций сопровождений, Oracle
Text
очень хорошо работает в сочетании с приложениями на базе PL/SQL
. Программа Oracle Text
(в предыдущих версиях называвшаяся ConText
and interMedia
) стала полноценным решением в области информационного поиска с выходом Oracle9i
. Oracle Text
:
- Поддерживает все наборы символов
NLS
. - Делает возможным поиск по документам на западных языках, а также на корейском, японском, традиционном и упрощенном китайском.
- Учитывает уникальные характеристики всех языков.
- По умолчанию выполняет поиск без учета регистра символов.
- Поддерживает межъязыковой поиск.
Прежде чем писать приложение PL/SQL
для поиска в источнике данных, необходимо создать индексы Oracle
Text
. Я создал индекс Oracle
Text
по столбцу publication
. short_description
в составе схемы glln
. Чтобы обеспечить поддержку нескольких языков, я определил настройки для разных языков, а также настройку MULTI_LEXER
, позволяющую проводить поиск по нескольким языкам в одном запросе.
Для тестирования некоторых многоязыковых средств можно воспользоваться функцией text_search_func
, являющейся частью схемы g11n
:
FUNCTION text_search_func (v_keyword IN VARCHAR2)
RETURN sys_refcursor
IS
v_title sys_refcursor;
BEGIN
OPEN v_title
FOR
SELECT title, LANGUAGE, score (1)
FROM publication
WHERE contains (short_description, v_keyword, 1) > 0
ORDER BY score (1) DESC;
RETURN v_title;
END text_search_func;
Вызов этой функции с передачей ключевого слова «pl
»:
variable x refcursor;
call text_search_func('pl') into :x;
print x;
возвращает следующий результат:
TITLE LANGUAGE SCORE(1)
---------------------------------------- -------- --------
Oracle PL/SQLプログラミング 基礎編 第3版 JA 18
Oracle PL/SQL Programming, 3rd Edition EN 13
Oracle PL/SQL Programmierung, 2. Auflage DE 9
Книга находится на всех трех языках, потому что «pl
» присутствует во всех вариантах ее названия. Обратите внимание: я провожу поиск по строке «pl
» в нижнем регистре, но в записи символы «PL
» хранятся в верхнем регистре. По умолчанию поиск проводится без учета регистра, несмотря на использование функции UPPER
.
Может оказаться, что в одних языках регистр символов должен игнорироваться, а в других — нет. Признак игнорирования регистра можно задать на уровне отдельных языков в языковых настройках. Просто добавьте атрибут mixed_case
со значением yes
; лексемы будут создаваться в том виде, в котором они хранятся в документе или столбце, — но только для языка, указанного в настройке.
В Oracle
Database
11g
многоязыковой информационный поиск упростился с появлением реализации AUTO_LEXER
. По количеству функций уровня отдельных языков она уступает MULTI_LEXER
, но зато почти не требует лишних усилий со стороны разработчика. Вместо того чтобы полагаться на содержимое столбца language
, AUTO_LEXER
идентифицирует текст на основании кодовых точек.
Oracle
также поддерживает реализацию WORLD_LEXER
. По широте функций она уступает MULTI_LEXER
и, как и AUTO_LEXER
, не предусматривает настройки на уровне отдельных языков. С другой стороны, она очень проста в настройке.
При использовании WORLD_LEXER
текст разбивается на лексемы в зависимости от категории, к которой он относится. Разбивка текстов на языках арабских и латинских категорий, в которых лексемы разделяются пробелами, не создает проблем. С азиатскими символами дело обстоит сложнее, потому что в них разделители-пробелы не используются, поэтому разбивка осуществляется с перекрытием. Например, трехсимвольная строка 尼崎市 разбивается на две лексемы, 尼崎 и 崎市.
Oracle
Text
также предоставляет дополнительные возможности в зависимости от языка. За дополнительной информацией, включая возможности и ограничения конкретных языков, обращайтесь к документации Oracle
Text
на сайте OTN
.
Информационный поиск и PL/SQL
Мне доводилось проектировать и реализовывать исключительно большие и сложные системы управления записями и цифровые библиотеки. По собственному опыту могу сказать, что ничто не сравнится с PL/SQL
для операций поиска и сопровождения в Oracle
Text
. Тесная интеграция PL/SQL
с сервером базы данных, а также улучшение его быстродействия в последних версиях делают хранимые программы PL/SQL
идеальным вариантом для программ такого рода.
Преимущества становятся еще более очевидными при работе на нескольких языках. Общий разбор SQL
и PL/SQL
означает логически последовательную обработку символов и символьной семантики независимо от языка, на котором производятся индексирование и поиск.
Один из первых проектов, за которые обычно берутся программисты Oracle
Text
, — форматирование строк для поиска. В следующем примере создается функция, которая форматирует поисковые строки для Oracle
Text
:
FUNCTION format_string (p_search IN VARCHAR2)
RETURN VARCHAR2
AS
-- Определение ассоциативного массива
TYPE token_table IS TABLE OF VARCHAR2 (500 CHAR)
INDEX BY PLS_INTEGER;
-- Определение переменной ассоциативного массива
v_token_array token_table;
v_temp_search_string VARCHAR2 (500 CHAR);
v_final_search_string VARCHAR2 (500 CHAR);
v_count PLS_INTEGER := 0;
v_token_count PLS_INTEGER := 0;
BEGIN
v_temp_search_string := TRIM (UPPER (p_search));
-- Определение максимального количества лексем
v_token_count :=
lengthc (v_temp_search_string)
- lengthc (REPLACE (v_temp_search_string, ' ', ''))
+ 1;
-- Заполнение ассоциативного массива
FOR y IN 1 .. v_token_count
LOOP
v_count := v_count + 1;
v_token_array (y) :=
regexp_substr (v_temp_search_string, '[^[:space:]]+', 1, v_count);
-- Обработка зарезервированных слов
v_token_array (y) := TRIM (v_token_array (y));
IF v_token_array (y) IN ('ABOUT', 'WITHIN')
THEN
v_token_array (y) := '{' || v_token_array (y) || '}';
END IF;
END LOOP;
v_count := 0;
FOR y IN v_token_array.FIRST .. v_token_array.LAST
LOOP
v_count := v_count + 1;
-- Обработана первая лексема
IF ( (v_token_array.LAST = v_count OR v_count = 1)
AND v_token_array (y) IN ('AND', '&', 'OR', '|')
)
THEN
v_final_search_string := v_final_search_string;
ELSIF (v_count <> 1)
THEN
-- Разделение запятой (если разделителя еще нет)
IF v_token_array (y) IN ('AND', '&', 'OR', '|')
OR v_token_array (y - 1) IN ('AND', '&', 'OR', '|')
THEN
v_final_search_string :=
v_final_search_string || ' ' || v_token_array (y);
ELSE
v_final_search_string :=
v_final_search_string || ', ' || v_token_array (y);
END IF;
ELSE
v_final_search_string := v_token_array (y);
END IF;
END LOOP;
-- Экранирование специальных символов в строке
v_final_search_string :=
TRIM (REPLACE (REPLACE (v_final_search_string,
'&',
' & '
),
';',
' ; '
)
);
RETURN (v_final_search_string);
END format_string;
Эта программа выделяет лексемы из строки по пробелам между символами. Она использует символьную семантику объявления переменных, включая объявление ассоциативного массива.
Чтобы протестировать ее со строкой на английском языке, я выполняю следующую команду SELECT
:
SELECT format_string('oracle PL/SQL') AS "Formatted String"
FROM dual
Команда возвращает следующий результат:
Formatted String
-----------------
ORACLE, PL/SQL
Функция F0RMAT_STRING
по умолчанию разделяет лексемы запятыми, так что точное совпадение не обязательно. Строка символов, не ограниченная пробелами, ищется в том виде, в котором она была введена. Следующий пример демонстрирует смешанное использование английских и японских символов:
SELECT format_string('Oracle PL/SQLプログラミング 基礎編 第3版') AS
"Formatted String" FROM dual;
При передаче этой смешанной строки функции F0RMAT_STRING
возвращается следующий результат:
Formatted String
-----------------
ORACLE, PL/SQLプログラミング, 基礎編, 第3版
В позициях разделения лексем пробелами независимо от языка включается запятая.
Следующий поиск CONTAINS
использует функцию F0RMAT_STRING
:
SELECT score (1) "Rank", title
FROM publication
WHERE contains (short_description, format_string('プログラム'), 1) > 0;
Результат выглядит так:
Rank TITLE
------------ ------------
12 Oracle SQL*Plus デスクトップリファレンス
Использование PL/SQL
и Oracle
Text
позволяет выполнять индексирование и полнотекстовый поиск в данных независимо от набора символов или языка.