Времена, когда объемы ежедневной почтовой корреспонденции в нашей организации исчислялись килобайтами, давно ушли в прошлое. Лет 5-ть назад количество корреспонденции начало стремительно возрастать. Появилась заархивированная различными архиваторами корреспонденция в разных форматах: Ms Word/Excel, html, eml. Когда объем, совершенно разнородной, корреспонденции начал приближаться к гигабайту в год, я принял решение о написании программного обеспечения, для систематизации этого информационного потока. Основной головной болью стал поиск архивных документов содержащих номер письма или какую-то ключевую фразу. Очень редко было известно точное название файла. Нужно было создавать программу с возможностью контекстного поиска. То есть по какому-то слову или его части.
Вариант хранение всего текста в базе данных отпал сразу. Оперировать гигабайтами текста очень накладно. Пусть уж лучше файлы, хранятся там, где им положено . на жестком диске. А в базу поместим только слова и то, в каких файлах они встречаются. Каждый файл получает как бы инвентаризационную карточку. В русском языке слов около 30-ти тысяч, а большинство из них редко употребляемые. Даже если добавить украинские и английские слова, то все равно получим таблицу, с которой управится среднего веса СУБД. Подобную обработку текстов программисты часто называют индексацией, а для пользователей контекстным поиском.
Так как задача в основном ясна, перейдем к практической части. Заранее хочу предупредить читателей, что описана только принципиальная часть процедуры индексации слов в файлах, а структура данных максимально упрощена. Не в ущерб, надеюсь, пониманию вопроса.
Программа разрабатывалась в среде Borland Delphi 3. Это была последняя на тот момент версия. Позже ПО было переработано в Delphi 5. СУБД была избрана Sybase SQL Anywhere 5, так как это основной SQL сервер фирмы. Однако перенести ПО на другие СУБД довольно просто, если не считать проблем с синтаксисом запросов. Индексация файлов в СУБД построена на 4-х таблицах:
1. Таблица встреченных программой слов [<b>words</b>].
<b>CREATE TABLE "DBA"."words"
(
"ID" integer NOT NULL DEFAULT autoincrement,
"word" char(20) NOT NULL,
PRIMARY KEY ("ID")
)</b>
И индекс по базе слов
<b>CREATE UNIQUE INDEX "words_index" ON "DBA"."words"
(
"word" ASC
)</b>
Сюда программа запоминает все встреченные ею слова при индексации файлов. Вы можете добавить свои поля в эту таблицу, например количество цифр в слове. Однако помните, что нагрузка на эту таблицу, а особенно индекс [words_index] будет, пожалуй, самой сильной. Если Ваша СУБД позволяет, то размещайте ее на самом быстром пространстве сервера.
2. Таблица обработанных файлов .<b>Files</b>.
<b>
CREATE TABLE "DBA"."files"
(
"ID" integer NOT NULL DEFAULT autoincrement,
"FilePath" char(255) NOT NULL,
PRIMARY KEY ("ID")
)</b>
В эту таблицу заносятся все обработанные программой файлы. Я показал самое минимальное описание такой таблицы. На практике надо добавить разные вспомогательные поля: дата создания файла, короткое имя, тип файла и.т.д в зависимости от того, какую функциональность Вы хотите придать вашей программе. Размер данной таблицы будет расти постоянно, это будет самая объемная таблица.
3. Таблица наличия в файле определенных слов [<b>Links</b>]
<b>CREATE TABLE "DBA"."links"
(
"ID" integer NOT NULL DEFAULT autoincrement,
"FileID" integer NOT NULL,
"WordID" integer NOT NULL,
PRIMARY KEY ("ID")
)</b>
и два индекса
<b>
CREATE INDEX "words_id" ON "DBA"."links"
(
"WordID" ASC
)</b>
и
<b>
CREATE INDEX "files_id" ON "DBA"."links"
(
"FileID" ASC
)
</b>
В этой таблице для каждого файла указывается код встреченных в нем слов. Так, к примеру, если в некотором файле c кодом 52 (поле ID таблицы files) есть слово Borland с кодом 258 (поле ID таблицы words) то в базу будут занесена следующая информация:
<b>
Insert into dba.links values
(
default, 52, 258
)</b>
Заносятся только ключевые поля таблиц Files и Words, а не текст или полная информация о файле. Это позволяет уменьшить объем таблицы. Да и оперировать с числами СУБД намного легче, чем с текстом. Надо заметить, что таблице links будет довольно много записей. Количество можно оценить умножив количество проиндексированных файлов на количество уникальных слов в них.
Построив нашу базу данных . приступим непосредственно к индексации.
1. Получаем текстовое содержимое файла. Я, к сожалению, не могу здесь описать получение текста с файлов разных форматов. MS Word, к примеру, лучше всего вычитывать ч-з OLE Automatic.
2. Разбиваем текс на слова. Алгоритм за вами. Я, к примеру, использовал две функции библиотеки RxLIB
<b>function WordCount(const S: string; const WordDelims: TCharSet): Integer; </b>- возвращает количество слов в строке. Разделители слов (пробелы, запятые и.т.д.) указываем в множестве WordDelims.
<b>function ExtractWord(N: Integer; const S: string; const WordDelims: TCharSet): string; </b>- возвращает слово ≤ N строки S согласно множеству разделителей слов WordDelims.
Вполне допускаю, что есть и более быстрые алгоритмы определения отдельных слов.
3. Ищем слово из индексируемого файла в таблице words. Если оно отсутствует, то заносим в таблицу. Узнаем код слова (поле ID таблицы words).
4. По таблице links проверяем, не включали ли мы раньше связку код файла и код слова, задав, например такой запрос select * from dba.links where FileID=.код файла. and WordID =.код слова.. Если отсутствует, то записываем в базу.
Проделав такую же процедуру для всех слов файла, мы его проиндексируем.
Как же осуществить контекстный поиск по этой базе? Да запросто. Припустим, мы ищем файлы, в которых есть слово Borland. Тогда запросом на нашу базу данных:
<b>
select L.FileID,f. FilePath
from branch.link as l,branch.Files as f
where wordID in (select id from branch.words where word=.Borland.) and f.ID=l.FileID
</b>
мы получим список всех искомых файлов. Для мягкого поиска, по части слова, запрос модифицируется следующим образом:
<b>
select L.FileID,f. FilePath
from branch.link as l,branch.Files as f
where wordID in (select id from branch.words where word like.%Borland%.) and f.ID=l.FileID
</b>
Вот и все, что я хотел вам рассказать. Более чем уверен, что есть другие и, наверное, лучшие способы искать слова в безразмерном информационном потоке. Но свое начинание, как известно, "ближе к телу". Если кому-то подойдет этот способ, то пользуйтесь на здоровье. В последнее время в операционные системы начали встраивать подобные службы индексации. Однако их сложно приспособить под специфику работы конкретной фирмы. Так, кроме простого поиска файла, часто надо знать информацию о том кто в последний раз обрабатывал файл, кем он распечатан, до кого доведен. Тут уж без своей программы не обойтись.
Успехов в поисках. <i>Искренне ваш . RollBack</i>
|