пятница, 12 ноября 2010 г.

Полнотекстовый поиск и mongodb.

Не буду вдаваться в подробности каким образом я дошел до использования не-sql базы данных, скажу лишь, что в определенный момент мой мозг отказался вталкивать предметную область  одного из проектов в чужеродную ей реляционную модель. Данные не были формализованы, формат представления грозился сильно меняться со временем, а отдельные объекты угрожали обрасти атрибутами, о которых на момент проектирования я мог и не подозревать. Атакованный со всех сторон заманчивыми лозунгами nosql, я пустился во все тяжкие и, махнув рукой на неизведанность, принялся выбирать nosql решение. Среди многообразия предлагаемых систем моим разумом удалось завладеть только MongoDB. MongoDB - документоориентированная база данных, хранящая объекты в JSON формате, предоставляющая интерфейс запросов, mapReduce, автоматические репликации, шардинг и много чего другого вкусного, от чего очень сложно устоять. Более подробно и детально о всех преимуществах и недостатках можно легко узнать из  раздела документации проекта.
За время работы над информационными системами у меня сложилось стойкое представление о том, что эффективность информационной системы в большей степени формируется качеством и скоростью работы поискового механизма. А так как все более и более превалируют объемы неструктурированной информации на первый план выходит возможность полнотектового поиска.
Являясь поклонником postgresql, с удовольствием использовал предоставляемый этой СУБД механизм создания индексов для полнотекстового поиска. PostgreSQL позволяет получить качественный полнотекстовый поиск "из коробки" сведя к минимуму необходимые затраты на модификацию приложения.
Сложно, привыкнув к теплу и комфорту, сталкиваться с проблемой отсутствия аналогичного встроенного решения в MongoDB. Очень хотелось обойтись малой кровью и не влезать в долгосрочное изучение таких монстров как Lucene и Sphinx. Возможно решение на основе данных вариантов было бы гораздо лучше, но дефицит времени не оставлял выбора.
Стоит признать, что разработчики знают о данной проблеме и в списках рассылки поднималась тема внедрения механизма полнотекстового поиска, на трекере проекта создан тикет с точкой фиксации на версии 1.7.x. Так, что вполне возможно, что через год данная статья потеряет свою актуальность.
Благодаря возможности создавать индексы для вложенных в документ массивов, в качестве частичного решения можно рассмотреть следующий вариант. Разработчику предлагается выделить в рамках документа специальный атрибут, хранящий массив слов для поиска. Этот массив индексируется средствами mongodb и используется для поиска. Формирование данного массива полностью ложится на плечи разработчика. Создание индекса на данный массив позволит получить адекватную скорость выборки. Сразу можно отметить недостатки, а именно: необходимость дополнительной разработки функционала уровня DAO, а также разнообразие словоформ и малозначащие слова, которые существенно влияют на качество поиска. Относительно первого пункта не остается ничего кроме как мириться, ибо лезть внутрь сервера mongod и прикручивать полнотекстовый поиск - явно не та задача над которой хотелось тратить свое время. Со вторым пунктом можно справиться при помощи стемминга и фильтрации стоп-слов. Инструмент для реализации задуманного долго искать не пришлось, - все необходимое оказалось в Lucene.
Рассмотрим ситуацию, когда у документа есть текстовое поле, по которому необходимо будет проводить поиск. Назовем его text:
{
    _id: "id",
    text: "content of War and Peace of Leo Tolsoy here"
}
Можно предложить разделить простое содержимое на два поля: text.content и text.tsearch, где в content непосредственно будет располагаться оригинал текста, а tsearch будет содержать массив нормализованных слов для поиска. В результате документ примет вид:
{
    _id: "id",
    text: {
        content: "content of War and Peace of Leo Tolsoy here",
        tsearch: ["content", "war", "peac", "leo", "tolstoy", "here"]
    }
}

Важно не забыть создать индекс на tsearch: db.mydocs.ensureIndex({"text.tsearch": 1}).
Остальная работа сводится к написанию вспомогательной функции, которая на вход получает строку а на выходе выдает список слов, прошедших стемминг и фильтрацию на стоп-слова. Используем классы, предоставляемые библиотекой lucene-core:

private List<String> getTSearchList(String text) {
    List<String> searchTerms = new ArrayList<String>();
    Analyzer analyzer = new SnowballAnalyzer(LUCENE_30, "English", StandardAnalyzer.STOP_WORDS_SET);
    TokenStream tokenStream = analyzer.tokenStream("contents", new StringReader(text));
    TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
    try {
        while( tokenStream.incrementToken() ) {
            searchTerms.add(termAttribute.term());
        } 
    } catch (IOException ignore) {}
    return searchTerms;
}

Существует несколько алгоритмов анализа входного потока. Наиболее приемлемые результаты дает SnowballAnalyzer. В случае использования текстовых данных специфичной области можно переопределить список слов для фильтрации.
Теперь осталось строку запроса аналогичным образом перевести в массив слов, над которым проведен стемминг и фильтрация, и сформировать BSON-объект запроса к базе. Используем элементы запроса $elemMatch и $all. В общем виде запрос "War and Peace" будет выглядеть следующим образом:

{text: {$elemMatch: {tsearch: {$all: ["war", "peac"]}}}

Стоит учитывать, что данное решение не может предоставить того богатства функций, реализованных в полноценных поисковых движках. Речь идет о ранжировании результатов поиска, подсветки поисковых термов, поиск по атрибутам и прочих. Так что, если вам требуется что-то из перечисленного, думаю придется вникать во все прелести Lucene или Sphinx.

P.S. Из данного поста можно сделать дополнительный вывод, что полностью избавиться от костылей не получится. Решив хронические проблемы реляционных СУБД, вы неминуемо столкнетесь с проблемами nosql.

P.P.S. Если при разработке вы пользуетесь maven2, то все необходимые зависимости Lucene подключаются следующими блоками:
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>3.0.2</version>
</dependency>

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-snowball</artifactId>
    <version>3.0.2</version>
</dependency>

вторник, 2 ноября 2010 г.

Безопасность Oracle глазами аудитора: нападение и защита.

В связи с отпуском и вливанием в рабочий процесс не мог уделить внимание блогу в должной степени. Исправиться хотел бы обратив ваше внимание на книгу "Безопасность Oracle глазами аудитора: нападение и защита". Я уже говорил, что отечественный книжный рынок не балует любителей информационной безопасности большим разнообразием содержательных, а главное актуальных книг в данной области. В данном случае, данная книга является достойным представителем печатных изданий посвященных вопросам ИБ. Дополнительным стимулом к чтению данной книги является то, что ее автором является достаточно известный специалист в области защиты информации, один из основателей исследовательской лаборатории Digital Security Research Group - Александр Поляков, так же известный как @sh2kerr. Поляков - один из немногих в России, кто проводит сертификацию компаний по стандарту PCI DSS.
Книга позиционируется как продолжатель традиций "Атака на интернет". Именно с этой книги началось мое знакомство с миром компьютерной безопасности, так что не смог себе отказать в удовольствии ознакомиться с данным произведением.
Содержание книги посвящено такому важному вопросу как безопасность СУБД Oracle. Такая огромная и комплексная система как СУБД Oracle просто не может не содержать целую кладезь различного рода уязвимостей и слабостей, подвергающих угрозам не только хранимые данные, но и саму операционную систему, на которой Oracle функционирует.
Основной принцип построения книги - чтобы что-то хорошо защищать надо знать от чего защищать, а главное от кого, и как этот кто-то думает. Таким образом книга на 90% состоит из детального анализа сценариев и методов проникновения в Oracle, повышения привилегий, закрепления в системе и сокрытия признаков вторжения. Лишь 40-50 страниц посвящены вопросам аудита и корректной конфигурации сервера Oracle. Так что можно сказать, что книга больше подходит для интернет-злодеев, чем для администраторов.
В книге рассматривается комплексный последовательный подход к тестированию сервера Oracle на проникновение. Прочитав данную книгу можно получить представление о проблемах безопасности Oracle TNS Listener, раскрытия вспомогательной информации о базе, преодоление парольной защиты, PL/SQL инъекции, атаки на переполнение буфера, получения доступа к ОС через СУБД и другие атаки. В отдельную главу вынесена тема руткитов для Oracle.
Таким образом, данная книга может рассматриваться как начальное руководство для получения общего представления о вопросах безопасности Oracle. Ее можно рассматривать как отличную альтернативу The Oracle Hacker's Handbook, к тому же имеется однозначное преимущество в виде языка на котором она написана.

P.S. Пытался расшифровать QR-код на обложке книги - так ничего и не получилось. Интересно, содержит ли он какую либо реальную информацию.