Acabo de completar, en su mayor parte (aún en fase de pruebas, pero las «partes difíciles» están básicamente resueltas y la migración funciona bien), la migración de un foro vb3 de 15 años, que originalmente (¡créanlo o no!) era un foro vb2 del año 2000.
Este foro lleva en funcionamiento alrededor de 20 años (con código PHP, tablas, complementos, etc., personalizados de una magnitud inimaginable), y en mi opinión, Discourse es el único software de foros «digno» de un proyecto de migración, el cual comencé hace aproximadamente una semana, pasando de la viabilidad a la conclusión (básicamente).
En primer lugar, quiero agradecer a todo el equipo de Discourse por tal obra maestra moderna de foro. Discourse es verdaderamente notable, en mi opinión (y estoy seguro de que muchos otros opinan igual).
En segundo lugar, me gustaría agradecer al equipo que codificó el script original de migración vbulletin.rb. Este script estaba aproximadamente un 95% correcto. Lo modifiqué para que funcionara adecuadamente con vb3. Por ejemplo, vb3 no tiene una tabla filedata como vb4 (relacionada con los archivos adjuntos); por lo tanto, modifiqué vbulletin.rb para que funcionara. Además, hubo problemas al importar foros vb3 secundarios como categorías, pero modifiqué el código para importar todos los foros como categorías de nivel superior y luego escribí algo de postgres psql para crear nuevas relaciones de categorías padre-hijo (o las configuré manualmente). Hubo otros «detalles» (una historia para otro día), pero esos duendes hicieron que fuera divertido y desafiante, aunque no insuperable.
En tercer lugar, me gustaría agradecer a Sam y su código lithium.rb, donde utilicé su rutina import_likes como base para mi vb3 thank you (de un complemento heredado, no parte de vB OOTB) a discourse likes. Realicé cambios menores en la rutina import_likes y, después de unas pocas horas de depuración, logré que funcionara.
Como pueden ver en esta publicación de 2018, migrada hoy:
Pensé que era importante para los usuarios que sus vb thanks se migraran a discourse likes, y por eso aquí está el código que utilicé para migrar los likes para otras almas perdidas de foros vb3:
En primer lugar, creé una tabla mysql vb3 llamada user_actions y escribí un script PHP para llenarla desde la tabla heredada post_thanks de vb, así (no pulido, pero funcional):
ubuntu:/var/www/includes/cron# cat thanks_to_discourse.php
<?php
/**************************************************
+-----------+------------+-----------+-----------+
| thanker | unixtime | id | thanked |
+-----------+------------+-----------+-----------+
| 1 | 1584149592 | 303045211 | 302093876 |
| 302153369 | 1584136706 | 303045214 | 302116191 |
| 302108573 | 1584128526 | 303045211 | 302093876 |
| 302153369 | 1584126659 | 303042175 | 302116191 |
| 302153369 | 1584126400 | 303045174 | 302116191 |
| 302153369 | 1584117711 | 303045184 | 1 |
| 37898 | 1584108187 | 303045175 | 1 |
| 302181242 | 1584106664 | 303045201 | 302122047 |
| 302181242 | 1584104642 | 303045074 | 302052697 |
| 302025710 | 1584103722 | 303045184 | 1 |
+-----------+------------+-----------+-----------+
**************************************************/
$query = 'SELECT p.threadid AS threadid,t.userid AS thanker,t.date AS unixtime,t.postid AS postid,p.userid AS thanked from post_thanks as t LEFT JOIN post p ON p.postid = t.postid';
$allthanks = $vbulletin->db->query_read($query);
/****************************************
mysql> describe user_actions;
+-----------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| action_type | int(11) unsigned | NO | | NULL | |
| user_id | int(11) unsigned | NO | | NULL | |
| target_topic_id | int(11) unsigned | YES | | NULL | |
| target_post_id | varchar(16) | YES | | NULL | |
| target_user_id | int(11) unsigned | YES | | NULL | |
| acting_user_id | int(11) unsigned | YES | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+-----------------+------------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
*************************************/
$action_code = 2; // discourse like action == 2
$target_user_id = "";
while ($action = $vbulletin->db->fetch_array($allthanks)) {
#code to deal with how discourse identifies the first post in a topic in the post_custom_fields table
$query = 'SELECT firstpostid FROM thread WHERE threadid =' . $action['threadid'] . ' LIMIT 1';
$threadinfo = $vbulletin->db->query_first($query);
if ($threadinfo['firstpostid'] == $action['postid']) {
$action['postid'] = 'thread-' . $action['threadid'];
}
$update = 'INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, target_user_id,acting_user_id, created_at, updated_at)' .
' VALUES (' . $action_code . ',' .
'"' . $action['thanked'] . '",' .
'"' . $action['threadid'] . '",' .
'"' . $action['postid'] . '",' .
'"' . $target_user_id . '",' .
'"' . $action['thanker'] . '",' .
'FROM_UNIXTIME(' . $action['unixtime'] . '),' .
'FROM_UNIXTIME(' . $action['unixtime'] . '))';
$doit = $vbulletin->db->query_write($update);
}
Así, esta tabla mysql user_actions se incluye en el volcado de la migración.
Aquí está la rutina import_likes modificada que utilicé para completar la migración en el lado de Discourse:
def import_likes
puts "\nimporting likes..."
# created mysql user_actions table in vb3 using PHP script and included that table with migration dump
sql = "select acting_user_id as user_id, target_post_id as post_id, created_at from user_actions"
results = mysql_query(sql)
puts "length() method form : #{results.count}\n\n"
puts "skip loading unique id map"
existing_map = {}
PostCustomField.where(name: 'import_id').pluck(:post_id, :value).each do |post_id, import_id|
existing_map[import_id] = post_id
#puts "postcustomfield existing_map post_id: #{post_id} import_id #{import_id}\n"
end
puts "loading data into temp table"
#manually created the temp like_data table so I could check the table after session ends
#DB.exec("create temp table like_data(user_id integer, post_id integer, created_at timestamp without time zone)")
puts "like_data temp table created"
PostAction.transaction do
results.each do |result|
result["user_id"] = user_id_from_imported_user_id(result["user_id"].to_s)
result["post_id"] = existing_map[result["post_id"].to_s]
next unless result["user_id"] && result["post_id"]
puts "insert like table user_id: #{result["user_id"]} post_id #{result["post_id"]}\n"
DB.exec("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)",
user_id: result["user_id"],
post_id: result["post_id"],
created_at: result["created_at"]
)
end
end
puts "creating missing post actions"
DB.exec <<~SQL
INSERT INTO post_actions (post_id, user_id, post_action_type_id, created_at, updated_at)
SELECT l.post_id, l.user_id, 2, l.created_at, l.created_at FROM like_data l
LEFT JOIN post_actions a ON a.post_id = l.post_id AND l.user_id = a.user_id AND a.post_action_type_id = 2
WHERE a.id IS NULL
SQL
puts "creating missing user actions"
DB.exec <<~SQL
INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
SELECT pa.user_id, 1, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
FROM post_actions pa
JOIN posts p ON p.id = pa.post_id
LEFT JOIN user_actions ua ON action_type = 1 AND ua.target_post_id = pa.post_id AND ua.user_id = pa.user_id
WHERE ua.id IS NULL AND pa.post_action_type_id = 2
SQL
# reverse action
DB.exec <<~SQL
INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
SELECT p.user_id, 2, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
FROM post_actions pa
JOIN posts p ON p.id = pa.post_id
LEFT JOIN user_actions ua ON action_type = 2 AND ua.target_post_id = pa.post_id AND
ua.acting_user_id = pa.user_id AND ua.user_id = p.user_id
WHERE ua.id IS NULL AND pa.post_action_type_id = 2
SQL
puts "updating like counts on posts"
DB.exec <<~SQL
UPDATE posts SET like_count = coalesce(cnt,0)
FROM (
SELECT post_id, count(*) cnt
FROM post_actions
WHERE post_action_type_id = 2 AND deleted_at IS NULL
GROUP BY post_id
) x
WHERE posts.like_count <> x.cnt AND posts.id = x.post_id
SQL
puts "updating like counts on topics"
DB.exec <<-SQL
UPDATE topics SET like_count = coalesce(cnt,0)
FROM (
SELECT topic_id, sum(like_count) cnt
FROM posts
WHERE deleted_at IS NULL
GROUP BY topic_id
) x
WHERE topics.like_count <> x.cnt AND topics.id = x.topic_id
SQL
end
end
Espero que esta publicación ayude a otros que puedan migrar de vB3 a Discourse, ya que muchas personas utilizaron el complemento heredado de «gracias» de vb3 durante muchos años.
Además, pasé mucho tiempo en la base de datos de Discourse y dentro de muchos scripts ruby de Discourse, por lo que esta migración fue complicada (no para los de corazón débil sin duda), pero ciertamente factible.
De hecho, disfruté haciéndolo. Fue la primera vez que usé ruby; así que fue divertido empezar a aprender ruby y, como persona mysql de larga data, la parte de postgres fue mayormente rutinaria.
Pronto estaremos en línea… ¡Gracias al equipo de Discourse!!!



