Хранилище для обработки данных

Допустим у нас стоит задача: нужно собирать неструктурированные html данные и извлекать из них структуру, или, точнее, информацию, т.е. система Text Mining/Information Extraction.

Вcе элементы этого процесса, конечно, должны где то хранится. И если конечную информацию можно структурировать, завести с десяток таблиц в БД, настроить связи и складывать туда, то входная информация по определению у нас не структурирована, ну или, как минимум, слабо структурированная. Если делать сложную структуру то из-за специфики нашей задачи нам будет очень сложно отследить целостность данных. К слову о сложности работы с такой структурированностью замечу что я попытался было нарисовать это подход, но не смог сделать чтобы это было внятно, без всего лишнего.

Поэтому нужно придумывать какое то простое и универсальное решения для этой задачи. Одна из них это структура когда у нас для всего выделена одна таблица, в первые колонки которой заливается начальная структура. Далее они обрабатываются нашей программой для структурирования, и результат складывается рядом, в следующие колонки. Причем если результат мы получаем не за один шаг, что чаще и бывает, то в таком случае мы последовательно выполняем все шаги, складывая новые данные правее от текущих.

В итоге получаем примерно такие шаги:

  1. (первая колонка) список URL
  2. выкачиваем документ по этому адресу, складываем в колонку 2
  3. убираем весь мусор (стили, js, реклама и пр.) из html и кладем в колонку 3
  4. вырезаем текстовые контент (убираем навигацию, и пр.), результат в колонку 4
  5. делаем PDF, результат в колонку 5
1to1struct
Рис 1. Таблица с несколькими колонками
mtonstruct
Рис 2. Таблица с вертикальной обработкой

Мы складываем в колонку справа если у нас идет обработка 1 к 1 (см. рис 1), если же у нас идет какая либо вертикальная агрегация (см. рис 2), то новые данные стоит записывать под текущим блоком, в той же колонке, что есть более универсальный подход. В случае СУБД у нас получается для первого варианта таблица с колонками block_id, data_id, col_1...col_N, для второго варианта block_id, data_id, value. Где

  • block_id это у нас идентификатор обрабатываемого блока,
  • data_id идентификатор элемента данных, которым мы оперируем,
  • и col_1..col_n или value это те данные которые нужно обработать.

В случае файловой системы получается только второй вариант, идентификатором тут будет путь к файлу. Следуя этим подходам в Apache Hadoop есть HDFS для варианта хранения в файлах, и HBase для хранения в виде таблицы.

Наверное сразу возникает претензия что у нас всегда данные если не дублируются, то остается слишком много временных данных, старые на каждом этапе остаются, а новые (выведенные из предыдущих) складываются рядом. Зачем это? Затем что иначе нам нужно следить за транзакциями, усложнять процесс распараллеливания обработки. Гораздо проще складывать рядом, и когда придет время (например когда мы дойдем до конечного шага), можно будет выкинуть все лишнее. Если мы можем позволить себе это, то так получается гораздо удобнее. Дополнительные бонусы тут в том что на любом этапе мы можем переиграть обработку заново (как замена rollback'а) или сделать новую «ветку» обработки, выполнив другие функции. И, как минимум, всегда можем проследить историю операций.

Но еще более важно то что мы получаем независимые функции. Почти функции в том виде в котором они приняты в функциональном программировании. Т.е. у нас входные параметры и есть результат, и функция не должна больше ничего менять, не должна трогать входные данные, и не зависеть ни от каких данных кроме этих. Каждую из них может разрабатывать отдельный разработчик, не сильно заботясь об общей архитектуре приложения, и даже на совершенно разных языках можно писать, выбирая тот который больше подходит для конкретной задачи, которая к тому же имеет очень четкие границы.