Про флибусту
Dec. 10th, 2022 04:51 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Скачавши очередное (декабрьское) обновление флибусты, решил подумать о том, как бы себе сделать правильную искалку по ее метаинформации. На основании того, что я знаю о XML-схеме fb2-файлов и о том как устроены индексные (inpx) файлы флибусты. (ну и о том что флибустьеры не правят метаинфомрацию в файлах, поэтому корректная метаинформация содержится (если содержится) только в inp-файлах.
Ну то есть я знаю что у MyHomeLib схема базы кривая, ее какой-то девопс дизайнил, который не то что Дейта - Граббера не читал.
Ну для начала взял pgmodeller и нарисовал ER-диаграмму каталога библиотеки fb2-файлов. Из чисто флибустовских сущностей добавил туда только понятие архива. В смысле того zip-файла в котором в скачанном из торрентов мирроре лежит нужный f2.
Получилось 10 таблиц. Сущностей в принципе не так уж много
- book
- author (он же translator)
- sequence
- lang (классификатор для полей lang и srclang в таблице book)
- genre
Но везде же отношения m:n и нужны развязывающие таблички. А в табличке устанавливающей соответствие между книгой и серией еще и содержательное поле number лежит. И ведь на него даже NOT NULL не повесишь, я же знаю что полно есть в архиве книг у которых серия без номера.
Вообще я чувствую, что над жанрами еще табличка транзитивного замыкания нужна чтобы из них дерево выстроить. Но поскольку fb2-шной концепции жанров я никогда не понимал (и подозреваю что большая часть оцифровщиков тоже и жанры они в fb2-файлах ставили от балды) занимтаься этим мне лень.
Вот теперь думаю - писать парсилку inp-файлов в эту схему в sqlite, или уж сразу постгрес использовать. (и импортить туда весь текст fb2 на предмет организации полнотекстового поиска).
Для интересующихся сдизайненную схему положу под кат:
CREATE TABLE lang ( lang_code char(3) NOT NULL, lang_name text NOT NULL, CONSTRAINT lang_pk PRIMARY KEY (lang_code) ); CREATE TABLE archive ( archive_id serial NOT NULL, archive_name text NOT NULL, CONSTRAINT archive_pk PRIMARY KEY (archive_id) ); CREATE TABLE book ( book_id int8 NOT NULL, title text NOT NULL, file text NOT NULL, deleted boolean NOT NULL DEFAULT false, writedate datetime, lang char(3), srclang char(3), archive smallint NOT NULL, CONSTRAINT book_pk PRIMARY KEY (book_id), FOREIGN KEY (lang) REFERENCES lang(lang_code), FOREIGN KEY (srclang) REFERENCES lang(lang_code), FOREIGN KEY (archive) REFERENCES archive(archive_id) ); CREATE TABLE author ( author_id int4 NOT NULL, first_name text, middle_name text, last_name text, nickname text, CONSTRAINT author_pk PRIMARY KEY (author_id) ); CREATE TABLE seq ( sequence_id int4 NOT NULL, name text NOT NULL, CONSTRAINT seq_pk PRIMARY KEY (sequence_id) ); CREATE TABLE replaces ( old_book_id int8 NOT NULL, new_book_id int8 NOT NULL, CONSTRAINT replaces_pk PRIMARY KEY (old_book_id,new_book_id), FOREIGN KEY (old_book_id) REFERENCES book(book_id), FOREIGN KEY (new_book_id) REFERENCES book(book_id) ); CREATE TABLE book_author ( book_id int8 NOT NULL, author_id int4 NOT NULL, CONSTRAINT book_author_pk PRIMARY KEY (book_id,author_id), FOREIGN KEY (book_id) REFERENCES book(book_id), FOREIGN KEY (author_id) REFERENCES author(author_id) ); CREATE TABLE book_translator ( book_id int8 NOT NULL, translator_id int4 NOT NULL, CONSTRAINT book_translator_pk PRIMARY KEY (book_id,translator_id), FOREIGN KEY (book_id) REFERENCES book(book_id), FOREIGN KEY (translator_id) REFERENCES author(author_id) ); CREATE TABLE book_seq ( book_id int8 NOT NULL, sequence_id int4 NOT NULL, sequence_number int4, CONSTRAINT book_seq_pk PRIMARY KEY (book_id,sequence_id), FOREIGN KEY (book_id) REFERENCES book(book_id), FOREIGN KEY (sequence_id) REFERENCES seq(sequence_id) ); CREATE TABLE genre ( genre_id varchar(20) NOT NULL, genre_name text NOT NULL, CONSTRAINT genre_pk PRIMARY KEY (genre_id) ); CREATE TABLE book_genre ( book_id int8 NOT NULL, genre_id varchar(20) NOT NULL, CONSTRAINT book_genre_pk PRIMARY KEY (book_id,genre_id), FOREIGN KEY (book_id) REFERENCES book(book_id), FOREIGN KEY (genre_id) REFERENCES genre(genre_id) );
Что характерно, пока я причесывал этот SQL скрипт так, чтобы его сожрал sqlite, его объем сократился в 2.5 раза, а из содержательной информации потерялись только имена констрейнтов внешних ключей (и то можно было бы сохранить).
no subject
Date: 2022-12-10 04:25 pm (UTC)У меня вот полнотекстовый поиск для себя есть через спецплагин к calibre и recoll (штатный поиск в Calibre 6 - неудобный) но у меня объемы базы Calibre целиком - около 100 Gb и там не только fb2 а объем fb2 насколько помню где то 300-400 Gb архивов.
no subject
Date: 2022-12-10 04:53 pm (UTC)Ну у меня домашний сервер сейчас с неразумными требованиями по ресурсам. Рассчитанный на параллельный запуск минимум десяти регрессионных тестов того самого постгреса. То есть конечно не Big Beast Раймонда но всё же.
Но вообще требования к ресурсам у постгресовского полнотекстового поиска не такие уж большие.
Опять же большую часть тех архивов fb2 сославляют иллюстрации в base64. Если при покладании в поисковую базу аккуратно парсить fb2 и выдирать из них только текст, то объем существенно уменьшится.
Правда если быть гурманами до конца, там надо кое-какую метаинформацию в отдельные поля складывать чтобы при поиске учитываьть с большим весом. Но ее такой немного. Задача вообще-то состоит в том чтобы полнотекстовый поиск работал не на "домашнем сервере с разумными требованиями к ресурсам", а "на смартфоне с большой SDD-картой". Поскольку по нынешним временам полутерабайтная база считается маленькой, должно бы получиться. В конце концов будет короший тест-кейс для той команды, которая у нас сейчас полнтекстовый поиск поддерживает.
no subject
Date: 2022-12-10 05:32 pm (UTC)(пока выкручиваюсь кое как поиском на компе и развитой системой тегов)ю
Было бы интересно посмотреть что у вас в итоге получится если это хоть как то публично в итоге будет.
no subject
Date: 2022-12-10 05:38 pm (UTC)На самом деле на смартфоне у менся сейчас лежит не полный миррор флибусты а всего около 2 с половиной гигабайт книг (далеко не все из которых есть на флибусте, там полно всяких англоязычных с archive.org, gutenberg и даже с амазона).
Вот что с таким объемом текста постгрес на паре гигов справится (А столько в смартфоне есть) я уверен.
Но пока я развлекаюсь с полнотекстовым поиском в sqlite. Там правда с токенайзерами как-то плоховато - hunspell-овские в отличие от постгреса туда не прикручиваются. А поиск по русскому тексту без токенайзера это как-то даже не интересно.
На самом деле полнотекстовый поиск для меня задача далеко не первоочередная. Первоочередная это поддержка миррора флибусты + система исправления ошибок в нём. А то я знаю что на этой флибусте в fb2 есть несколько тысяч файлов с not well-formed xml, а уж сколько не соответствующих XML Schema FictionBook 2.1 - и подумать страшно. Полнотекстовый поиск по архиву fb2 когда-то делат
nataraj. Но не помню подробностей.
no subject
Date: 2022-12-10 06:02 pm (UTC)no subject
Date: 2022-12-11 08:11 am (UTC)А сколько нужно нынче места на миррор либогена? 30Тб? 50?
И там насколкьо я пониаю, совершенная труба с метаинформацией. Она стандартиована гораздо слабе, чем на флибусте, и в самих файлах не содержится.
Для избирательной синхронизации очень удобно когда каждаый файл self-contained, и вся метаинфрормация, которая должна попасть в каталог библиотеки содержится в самом файле.
no subject
Date: 2022-12-11 10:06 am (UTC)Основная метаинформация там конечно есть, но она частенько неправильная. В файлах она содержаться в принципе не может, т.к. там могут попадаться самые разные форматы. Разве что sidecar
С метаинформацией в самих файлах есть другая проблема, она никак не учитывает связи между файлами, к тому-же часть информации к файлам имеет отношение весьма условное. Учитывая это, избирательная синхронизация, основанная на метаинформации в файлах, будет работать только в ручном режиме, по сути пофайлово.
no subject
Date: 2022-12-11 10:42 am (UTC)Ну насчет того что "в принципе содержаться не может", не согласен. вот этот мой скрипт кроме fb2 поддерживает pdf, djvu и epub. И все эти форматы вполне умеют хранить в себе метаинформацию. Правда почему то ни в Doublin Core, ни в адобовских спецификациях понятие "серии" как в fb2 не предусмотрено. Поэтому связи между файлами действительно описать сложно.
Вообще на мой взгяд в библиотеке, по моему кроме алфавитного каталога по авторам и заглавиям и тематического каталога нужны еще региональный каталог (как у нас в факультетской библиотеке было). темпоральный каталог, причем по двум осям - период описываемый в книге и время ее написания, и каталог упоминаемых персоналий.
no subject
Date: 2022-12-11 11:28 am (UTC)Под связью между файлами я имел в виду что-то вида: "этот файл является улучшенной копией того файла", серии-же конструкция искусственная и весьма спорно, нужно ли для их поддержки городить отдельный огород.
Я долго размышлял насчет каталогов и пришел к выводу, что вот это вот все (алфавитный, тематический и прочее) нужно было в первую очередь для бумажных библиотек, чтобы однозначно расставлять книги по полкам и чтобы было возможно хоть что-то там найти. Для библиотек электронных это все неактуально, поиск лучше чем любой каталог.
Даже тематический каталог сильно подробный не нужен. Это в бумажной библиотеке нужно с помощью каталога отобрать считанные единицы книг, т.к. ни один библиотекарь не потащит вам для посмотреть пару сотен томов. В электронной-же библиотеке это совершенно не проблема.
no subject
Date: 2022-12-11 01:39 pm (UTC)Вот для такой связи между файлами я в вышепреведетнной схеме БД завел таблицу "replaces".
no subject
Date: 2022-12-11 03:45 pm (UTC)no subject
Date: 2022-12-11 06:13 pm (UTC)Более поздний файл совершенно не обязательно более новый файл, поскольку акт загрузки файла и акт генерации fb2 никак в общем случае не связаны с актом творения текста. В базе либрусека мне попадались и менее очевидные сочетания, нежели "кто-то загрузил юболее старую версию с более нововй датой". Акт простановки связи replaces в общем случае творческая интерпретация информации в файле и сопутствующей (напр. внешнего относительно файла знания о сущности обновлений текста автором).
no subject
Date: 2022-12-11 06:15 pm (UTC)no subject
Date: 2022-12-12 06:30 pm (UTC)no subject
Date: 2022-12-11 07:10 pm (UTC)Затем что там записей будет в тысячи раз меньше.
no subject
Date: 2022-12-11 07:24 pm (UTC)no subject
Date: 2022-12-11 02:06 pm (UTC)no subject
Date: 2022-12-11 03:20 pm (UTC)А собственно зачем? Вообще то не стоит задача сделать копию сайта флибусты. Стоит задача сделать локальную библиотеку содержащую все те же самые книжки с удобным поиском.
Лезть в inp-файлы при этом хочется только потому что известно, что в них информация более выверена, чем в самих fb2. А то бы обошелся сканирвоанием fb2.
Тем более что сканирвоать их все равно придется. На предмет выявления некорректного xml, несоответствия в схеме и всего прочего, что следует пофиксить прежде чем отдавать книгу читалке.
no subject
Date: 2022-12-11 03:42 pm (UTC)no subject
Date: 2022-12-12 06:05 am (UTC)Может быть. Но в торрент который я ежемесячно обнволяю, они sql-дамп не кладут.
no subject
Date: 2022-12-12 07:24 am (UTC)Здесь должно быть все, но наличие описаний авторов я не проверял.
no subject
Date: 2022-12-12 07:39 am (UTC)Будет проблема синхронизации. Есть основания верить что inpx файл в архиве соответствует тому же моменту времени, что и книги в том же архиве. А вот про отдельную страничку этого сказать нельзя.
no subject
Date: 2022-12-12 08:18 am (UTC)no subject
Date: 2022-12-12 09:05 am (UTC)Переделал на создание индексов в виде файлов (json и jsonl) и тупое чтение без сложных разборов, чтобы не грузить процессор. Правда, пришлось ещё делать многопроходное создание индексов по авторам, чтобы не упереться в память до oom, но это уже детали.
У меня парсится каждый fb2 (точнее, первые десятки кб, а не весь), а .inp использовался только для замены всякого, ибо рядом может лежать архив с файлами из samlib, к примеру. Плюс есть файлы замены метаданных (скажем, для автора "Луиза-Франсуаза" они понадобились, так как ник я ещё могу вспомнить, а фио - уже нет). В новом варианте индексация занимает примерно ночь, в старом - около суток (sqlite не самая быстрая штука на запись).
Надо будет выложить в доступное место, а то сейчас только в моём личном гите.
no subject
Date: 2022-12-12 09:21 am (UTC)Ну сутки на индексацию (и даже на доиндексацию ежемесячного обновления) это нормально. Опять же совершенно необязалетльно индексы создавать на той же машине, где будет производиться поиск, раз в месяц можно и более мощную задействовать.
По моему опыту основная проблема при разборе fb2 заключается в том что в самих файлах метаинформация как бог на душу положит. То first name s с last name у автора перепутаны, то пробелы перед/после вставлены.
Вот надо придумать хорошее воркфлоу борьбы с этими проблемами. Так чтобы при этом скачанный из торрентов архив библиотеки оставался неизменным архивом библиотеки и к нему можно было потом докачивать обновления.
Поэтому и хочется использвать inp-файлы, а не fb2 как таковые, поскольку в inp-файлах по-моему у флибусты метаинформация чуточку более вычитанная, чем в самих fb2.
no subject
Date: 2022-12-13 07:04 am (UTC)Когда делал разбор - наступил не только на проблему с метаинформацией (её в нужной мне части действительно проще взять из .inp), но и на то, что часть fb2 либо является некорректным xml (слава BeautifulSoap!), либо не соответствует формату FictionBook вплоть до того, что понадобилось писать такое:
и потом вызывать:
no subject
Date: 2022-12-13 07:08 am (UTC)Да, тоже с этими проблемами сталкивался.
Но я ставил себе задачу по-другому - не парсить fb2 файлы как они пришли из торрентов, а добиваться того, чтобя в читалку ко мне попадати корректные fb2-файлы. Во-первых well-formed, во-вторых соответствующие схеме, в третьих с правильной метаинформацией. Есть еще пункт 4 - правильное деление на главы, но это ручная работа.
no subject
Date: 2022-12-13 08:57 am (UTC)Так как народ копипастит буквы откуда ни попадя, то неотображаемого мусора там хватает.
\u00A0 (nbsp) - это уж просто повсеместно.
no subject
Date: 2022-12-13 09:02 am (UTC)"Там" это в inp-файлах? Буду мметь в виду. Вообще-то это один регексп
no subject
Date: 2022-12-13 03:18 pm (UTC)Происхождение довольно очевидное: копипаста из PDF и прочих вордов (особенно из таблиц).
И я бы сделал что-то вроде
line=" ".join(line.split()) # обрезать ведущие/висящие + сведение к одному
no subject
Date: 2022-12-13 06:18 pm (UTC)Вышеприведенно ерегулярное выраожение делает примерно то же самое - заменяет любую последовательность пробелных символов на один пробел.
А обрезать ведущие/висящие, это метод strip() строки.
no subject
Date: 2022-12-13 08:32 pm (UTC)no subject
Date: 2022-12-14 04:37 am (UTC)В надвиннии художественно произведения и имени его автора. Здесь же речь про метаинформацию, а не про текст.
no subject
Date: 2022-12-14 06:04 am (UTC)Вот Бобби Тейблз вырастет и напишет книгу с значащими сериями пробелов/табов/nbsp в названии ;)
no subject
Date: 2022-12-14 07:23 pm (UTC)Зачем inp-файлы?
Date: 2022-12-16 10:26 pm (UTC)Re: Зачем inp-файлы?
Date: 2022-12-17 07:25 am (UTC)Потому что inp-файлы лежат в том же торренте. А sql-файлы надо качать отдельно и они будут за другую дату.