Плагин для помощи в сопоставлении тем до миграции после миграции

Привет! Мы с нашей командой безумных идиотов почти завершили миграцию нашего форума Vbulletin 3 на Discourse. Для этого мы написали специальный скрипт, который наконец-то смог перенести все 21 миллион ответов из исходной базы данных в Discourse.

Теперь у нас возникла проблема со ссылками на темы и ответы, которые находятся внутри самих сообщений.

В нашей миграции мы создали сопоставление «старых» идентификаторов тем и постов с теми, на которые они ссылаются в Discourse.

Например:

   id   | topic_id |   name    | value  |         created_at         |         updated_at
--------+----------+-----------+--------+----------------------------+----------------------------
 581727 |   581736 | import_id | 599137 | 2023-02-08 16:30:01.600759 | 2023-02-08 16:30:01.600759

Теперь я думаю о создании плагина, который будет перехватывать ссылки на формат старого форума и преобразовывать их, ссылаясь на новую тему или ответ.

Например, что-то вроде:

https://oldforum.something.com/showthread.php?t=123456

Это должно инициировать запрос к таблице topics_custom_field для поиска значения 123456, найти соответствующий topic_id в Discourse, затем выполнить запрос к таблице topic_links с этим идентификатором и найти url. В итоге ссылка будет заменена в сообщении на стороне клиента (предполагается использование JavaScript для манипуляции содержимым).

Аналогичный подход применим и для постов.

Однако я не могу найти хороших примеров того, как вообще начать создание чего-то подобного для Discourse.
Не могли бы вы дать какие-то подсказки, примеры или указать на плагины, которые делают что-то похожее (проверяют ответы на наличие определённой подстроки и заменяют её, обращаясь к API или базе данных для получения одного значения на основе другого)?

Спасибо.

Это уже есть в ядре, это называется Постоянные ссылки, и существующий импортер VB4 содержит для этого код

В настройку permalink_normalizations следует ввести что-то вроде /showthread\.php\?(\d*)/thread/\1.

Просто для уточнения: я должен выполнить эту логику после завершения миграции, верно? То есть она пройдет по всем ответам снова и изменит постоянные ссылки.

Что вы имеете в виду под «изменения»? У вас уже есть постоянные ссылки?

Когда мы мигрируем содержимое ответа, например, с https://oldforum.something.com/showthread.php?t=123456, мы не знаем, какой id будет у этой темы в Discourse… верно?

Это сработает, если вы используете код выше для создания постоянных ссылок.

showthread.php?t= ссылается на тему/тред, а не на ответ, между прочим.

Я просто использовал эту ссылку в качестве примера :slight_smile:

К сожалению, мы не можем использовать этот код, потому что импорт занимает вечность для 20 миллионов постов, а массовый импорт просто не работает. Есть недостающие части.

Вот почему нам пришлось написать собственный скрипт миграции. Он делает всё (личные сообщения, пользователи, группы пользователей, категории, темы, ответы) примерно за 6 часов на 4 ядрах и 8 ГБ ОЗУ, но мы заметили, что у нас отсутствуют постоянные ссылки :slight_smile:

Возможно, стоит рассмотреть решение с использованием nginx map для предопределённых постоянных ссылок? Redirect vBulletin URLs to Discourse URLs

Мы обсудили это внутри команды и просто сделаем второй проход, когда все ответы будут перенесены.

Спасибо, что делились со мной идеями, Ричард :heart:

Создал ли ваш скрипт import_ids? Если да, то даже если вы не создали постоянные ссылки, вы можете довольно быстро обработать их для их создания.

Привет, да, Джей, у нас это есть.

Мы пытались избежать повторного прохода по всем 20+ миллионам ответов, но поняли, что альтернативные решения (плагин, перенаправление через nginx и т. д.) были бы довольно запутанными или зависели бы от внешних факторов, что сделало бы решение неполноценным. Поэтому мы просто ещё раз пройдёмся по всем ответам и обработаем постоянные ссылки. Это немного увеличит время миграции, но, надеемся, не слишком сильно.

Всё остальное формируется «на лету», так как мы знаем, как именно «сырой» текст должен быть преобразован в HTML.

С постоянными ссылками так сделать нельзя: если постоянная ссылка добавляется при редактировании, она может ссылаться на тему, которая ещё не была обработана (с более высоким идентификатором темы), и в момент обработки такой ссылки её не будет найдено в таблице topics_custom_field.

Не понимаю, как вы могли создать topic_custom_fields, не создав сначала тему. Я бы подумал, что можно сделать что-то вроде

TopicCustomField.each do |tcf|

и создать постоянные ссылки, но я многого не знаю о вашем коде.

Позвольте прояснить:

Темы и все их ответы импортируются в порядке возрастания идентификаторов тем из базы данных vBulletin. Это также означает, что импорт происходит в хронологическом порядке.

Однако это могло бы навести на мысль, что если вы когда-либо встретите ссылку на другую тему, она всегда будет указывать на уже существующую.

Но есть случаи, когда это не так, вот лишь несколько примеров:

  • разделение темы с комментарием, который привел к разделению. Разделенная тема будет иметь больший идентификатор, но она существует в теме с меньшим идентификатором.
  • редактирование для будущих читателей, когда в старых темах посты содержат ссылки на более новые.

Так что, да, хотя topic_custom_field генерируется и заполняется по ходу импорта, как объяснялось в самой первой теме, полагаться на это «на лету» нельзя, потому что нельзя быть уверенным, что всегда найдется правильное соответствие между идентификаторами.

Требуется дополнительный проход после завершения полного импорта.

Что касается TopicCustomField.each do |tcf|, я не уверен, что делает часть tcf. Ruby — язык, который я не изучал. Наш скрипт написан на C#, так как большинство людей, предложивших поработать с ним, уже используют его в своей работе.