Ох… я даже не думал запускать импортёр более одного раза для данных на продакшене. Большое спасибо за эту идею.
Итак, по сути, я сделаю дамп базы данных Drupal на продакшене и запущу полный импорт, сколько бы это ни заняло времени, оставив форум Drupal открытым для публики. Затем я переведу форум Drupal в офлайн-режим, сделаю ещё один дамп базы данных, загрузлю его в экземпляр MySQL импортёра и снова запущу скрипт импортёра?
Единственный недостаток, который я вижу, заключается в том, что любые правки постов в Drupal, удаления постов или изменения существующих профилей пользователей в течение этого интервала (назовём его 3 днями) — от момента первоначального импорта до второго импорта — не будут импортированы. Будут импортированы только совершенно новые пользователи и посты, появившиеся в этот период, верно?
Верно. Данные, которые будут отредактированы, будут утеряны. Альтернативой является временное отключение форума от сети. Можно предупредить пользователей. На практике никто не жаловался.
Только что после того, как я завершил финальный импорт в продакшн, я случайно проверил несколько импортированных тем форума и обнаружил серьёзную проблему. Как и ранее, это не вина Discourse или скрипта импорта. Оказывается, что при редактировании ответа на тему («комментария» в терминологии Drupal) иногда меняется метка времени created. Насколько я могу судить, должна изменяться метка времени changed. Несмотря на эту ошибку в Drupal, нитевидная структура комментариев сохраняется в правильном порядке. Однако скрипт импорта Drupal для Discourse, судя по всему, сортирует ответы по метке времени created (хотя в скрипте drupal.rb я не вижу никаких предложений ORDER). По результатам моих тестов в phpMyAdmin с базой данных Drupal, достаточно добавить ORDER BY c.cid ASC, чтобы сохранить правильный порядок нитей через исходный последовательный и неизменный идентификатор комментария Drupal — cid. Но я не уверен, позволит ли импортер Discourse, чтобы у последовательных ответов даты были не по порядку, и/или выполнит ли он собственную сортировку по дате публикации? Хотелось бы узнать мнение создателя скрипта импорта drupal.rb (и любого другого, конечно): сработает ли это и не повлечёт ли это каких-либо непредвиденных последствий?
Я думаю (но точно не знаю, база данных Drupal для меня не имеет смысла), что можно изменить запрос так, чтобы получать дату создания из оригинального поста, а не из отредактированного.
Кажется, есть таблица, содержащая оригинальный пост и время его создания, и другая — с правками.
Думаю, сортировка по cid ни к чему плохому не приведёт.
Это действительно не имеет никакого смысла. Проблема заключается в следующем: две выделенные строки должны быть вторым и третьим ответами в теме, но в Discourse они находятся где-то после 500-й позиции, потому что именно там стоит их временная метка.
И, разумеется, Drupal решил установить поля created и changed в одно и то же значение… И это происходит только иногда; я не могу воспроизвести это самостоятельно, редактируя старые посты. Однако у меня есть эта же проблема в нескольких длинных темах, где автор темы (OP) сначала опубликовал пост, а затем сразу же написал один или два дополнительных комментария со словом «reserved» в теле, чтобы позже добавить дополнительную информацию, что он и сделал несколько лет спустя.
Это имело бы смысл, но вот тот же проблемный отредактированный комментарий, распределённый по двум используемым им таблицам:
OK, похоже, это работает: исправляет две перемешанные ветки, которые я обнаружил, и, кажется, не вредит ничему другому.
def import_replies
…
batches(BATCH_SIZE) do |offset|
results = mysql_query(<<-SQL
SELECT c.cid, c.pid, c.nid, c.uid, c.created,
f.comment_body_value body,
f.comment_body_format format
FROM comment c,
field_data_comment_body f,
node n
WHERE c.cid = f.entity_id
AND n.nid = c.nid
AND c.status = 1
AND n.type IN ('poll', 'forum')
AND n.status = 1
AND c.created > UNIX_TIMESTAMP(STR_TO_DATE('#{IMPORT_AFTER}', '%Y-%m-%d'))
ORDER BY c.cid ASC #<--- Исправлено
LIMIT #{BATCH_SIZE}
OFFSET #{offset}
SQL
).to_a
Хм, похоже, это вернулось, чтобы укусить меня. Поскольку функция postprocess_posts заменяет старые внутренние ссылки на новый URL Discourse, я сделал исключение в коде для ссылок https://web.archive.org/web/20230101093741/https://MyOldForum.com/node/98765, которые мой импортер создал для старых опросов Drupal в Wayback Machine. Но, похоже, что-то пошло не так, потому что я только что заметил на перенесенном продакшн-сайте, что ссылки оказались в виде https://web.archive.org/web/20230101093741/https://MyOldForum.com/t/-/12345.
Теперь, когда я больше не нахожусь в контексте контейнера миграции, доступно ли пользовательское поле с оригинальным nid ноды Drupal в таблице DB тем Discourse? Если да, то кажется возможным выполнить замену строки в консоли Rails для всех тем с первым сообщением, содержащим View this poll on the Wayback Machine, а затем заменить https://web.archive.org/web/20230101093741/https://MyOldForum.com/t/-/[01234567890]*
на https://web.archive.org/web/20230101093741/http://MyOldForum.com/node/$original_nid
Вот моя оригинальная функция импорта опросов:
def import_poll_topics
puts '', "importing poll topics"
polls = mysql_query(<<-SQL
SELECT n.nid nid, n.title title, n.uid uid, n.created created, n.sticky sticky, taxonomy_index.tid tid, node_counter.totalcount views
FROM node n
LEFT JOIN taxonomy_index ON n.nid = taxonomy_index.nid
LEFT JOIN node_counter ON n.nid = node_counter.nid
WHERE n.type = 'poll'
AND n.status = 1
SQL
).to_a
create_posts(polls) do |topic|
{
id: "nid:#{topic['nid']}",
user_id: user_id_from_imported_user_id(topic['uid']) || -1,
category: category_id_from_imported_category_id(topic['tid']),
# Use TEMPmyoldforum.com или иначе postprocess_posts() попытается преобразовать ссылку /node/YYY из Wayback Machine
raw: "### View this poll on the Wayback Machine:\n**https://web.archive.org/web/20230101093741/http://TEMPmyoldforum.com/node/#{topic['nid']}**",
created_at: Time.zone.at(topic['created']),
pinned_at: topic['sticky'].to_i == 1 ? Time.zone.at(topic['created']) : nil,
title: topic['title'].try(:strip),
views: topic['views'],
custom_fields: { import_id: "nid:#{topic['nid']}" }
}
end
end