-
-
Notifications
You must be signed in to change notification settings - Fork 169
Expand file tree
/
Copy path0016_migrate_comments.sql
More file actions
330 lines (292 loc) · 10.3 KB
/
Copy path0016_migrate_comments.sql
File metadata and controls
330 lines (292 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
-- Migration 0016: Migrate Legacy Comments to New comments Table
-- Migrates comments from "Comment" to "comments" with hierarchical ltree paths
--
-- Key Challenges:
-- - Comment.id is INTEGER, comments.id is UUID
-- - Comment.parentId references Comment.id (INTEGER)
-- - comments uses ltree paths for efficient hierarchical queries
-- - Must migrate parent comments before children
-- ============================================
-- PART 1: Add legacy_comment_id column to comments table
-- ============================================
ALTER TABLE comments ADD COLUMN IF NOT EXISTS legacy_comment_id INTEGER;
-- Create unique index for idempotency checks and lookups
CREATE UNIQUE INDEX IF NOT EXISTS comments_legacy_comment_id_idx
ON comments(legacy_comment_id)
WHERE legacy_comment_id IS NOT NULL;
-- ============================================
-- PART 2: Create temporary mapping table
-- ============================================
-- This table maps old Comment IDs to new comment UUIDs
-- Needed because we generate paths iteratively by depth level
CREATE TABLE IF NOT EXISTS _comment_migration_map (
legacy_comment_id INTEGER PRIMARY KEY,
new_comment_id UUID NOT NULL,
legacy_post_id TEXT NOT NULL,
legacy_parent_id INTEGER,
depth INTEGER NOT NULL DEFAULT 0,
migrated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
-- ============================================
-- PART 3: Migrate root-level comments (depth = 0)
-- ============================================
-- First, migrate all comments without parents
DO $$
DECLARE
comment_record RECORD;
new_post_id UUID;
new_comment_id UUID;
new_path LTREE;
upvote_count INTEGER;
BEGIN
-- Loop through root comments that haven't been migrated
FOR comment_record IN
SELECT
c.id,
c.body,
c."postId",
c."userId",
c."createdAt",
c."updatedAt"
FROM "Comment" c
WHERE c."parentId" IS NULL
AND NOT EXISTS (
SELECT 1 FROM _comment_migration_map m WHERE m.legacy_comment_id = c.id
)
LOOP
-- Find the new post ID
SELECT id INTO new_post_id
FROM posts
WHERE legacy_post_id = comment_record."postId";
-- Skip if post wasn't migrated
IF new_post_id IS NULL THEN
RAISE NOTICE 'Skipping comment % - post % not found in new posts table',
comment_record.id, comment_record."postId";
CONTINUE;
END IF;
-- Generate new UUID for comment
new_comment_id := gen_random_uuid();
-- Generate path (just the comment ID for root comments)
-- Use underscore-separated hex format for ltree compatibility
new_path := text2ltree(REPLACE(new_comment_id::TEXT, '-', '_'));
-- Count likes for this comment
SELECT COUNT(*)::INTEGER INTO upvote_count
FROM "Like" l
WHERE l."commentId" = comment_record.id;
-- Insert the comment
INSERT INTO comments (
id,
post_id,
author_id,
parent_id,
path,
depth,
body,
upvotes_count,
downvotes_count,
created_at,
updated_at,
deleted_at,
legacy_comment_id
) VALUES (
new_comment_id,
new_post_id,
comment_record."userId",
NULL, -- No parent for root comments
new_path,
0, -- Root level
comment_record.body,
upvote_count,
0, -- No downvotes in legacy system
COALESCE(comment_record."createdAt", NOW()),
COALESCE(comment_record."updatedAt", NOW()),
NULL, -- Not deleted
comment_record.id
);
-- Record the mapping
INSERT INTO _comment_migration_map (
legacy_comment_id,
new_comment_id,
legacy_post_id,
legacy_parent_id,
depth
) VALUES (
comment_record.id,
new_comment_id,
comment_record."postId",
NULL,
0
)
ON CONFLICT (legacy_comment_id) DO NOTHING;
END LOOP;
END $$;
-- ============================================
-- PART 4: Migrate nested comments (depth 1-10)
-- ============================================
-- Iteratively migrate child comments level by level
DO $$
DECLARE
current_depth INTEGER := 1;
max_depth INTEGER := 10;
comments_migrated INTEGER;
comment_record RECORD;
new_post_id UUID;
new_comment_id UUID;
parent_comment_id UUID;
parent_path LTREE;
new_path LTREE;
upvote_count INTEGER;
BEGIN
LOOP
EXIT WHEN current_depth > max_depth;
comments_migrated := 0;
-- Loop through comments at this depth level
FOR comment_record IN
SELECT
c.id,
c.body,
c."postId",
c."userId",
c."parentId",
c."createdAt",
c."updatedAt"
FROM "Comment" c
WHERE c."parentId" IS NOT NULL
-- Parent must already be migrated
AND EXISTS (
SELECT 1 FROM _comment_migration_map m
WHERE m.legacy_comment_id = c."parentId"
)
-- This comment must not be migrated yet
AND NOT EXISTS (
SELECT 1 FROM _comment_migration_map m
WHERE m.legacy_comment_id = c.id
)
LOOP
-- Find the new post ID
SELECT id INTO new_post_id
FROM posts
WHERE legacy_post_id = comment_record."postId";
-- Skip if post wasn't migrated
IF new_post_id IS NULL THEN
CONTINUE;
END IF;
-- Get parent's new ID and path
SELECT m.new_comment_id, nc.path INTO parent_comment_id, parent_path
FROM _comment_migration_map m
JOIN comments nc ON nc.id = m.new_comment_id
WHERE m.legacy_comment_id = comment_record."parentId";
-- Skip if parent not found
IF parent_comment_id IS NULL THEN
CONTINUE;
END IF;
-- Generate new UUID for this comment
new_comment_id := gen_random_uuid();
-- Extend parent path with this comment's ID
new_path := parent_path || text2ltree(REPLACE(new_comment_id::TEXT, '-', '_'));
-- Count likes for this comment
SELECT COUNT(*)::INTEGER INTO upvote_count
FROM "Like" l
WHERE l."commentId" = comment_record.id;
-- Insert the comment
INSERT INTO comments (
id,
post_id,
author_id,
parent_id,
path,
depth,
body,
upvotes_count,
downvotes_count,
created_at,
updated_at,
deleted_at,
legacy_comment_id
) VALUES (
new_comment_id,
new_post_id,
comment_record."userId",
parent_comment_id,
new_path,
current_depth,
comment_record.body,
upvote_count,
0, -- No downvotes in legacy system
COALESCE(comment_record."createdAt", NOW()),
COALESCE(comment_record."updatedAt", NOW()),
NULL,
comment_record.id
);
-- Record the mapping
INSERT INTO _comment_migration_map (
legacy_comment_id,
new_comment_id,
legacy_post_id,
legacy_parent_id,
depth
) VALUES (
comment_record.id,
new_comment_id,
comment_record."postId",
comment_record."parentId",
current_depth
)
ON CONFLICT (legacy_comment_id) DO NOTHING;
comments_migrated := comments_migrated + 1;
END LOOP;
RAISE NOTICE 'Depth %: migrated % comments', current_depth, comments_migrated;
-- Exit early if no more comments to migrate at this level
EXIT WHEN comments_migrated = 0;
current_depth := current_depth + 1;
END LOOP;
END $$;
-- ============================================
-- PART 5: Update post comment counts
-- ============================================
-- Recalculate comment counts on posts based on migrated comments
UPDATE posts p
SET comments_count = COALESCE((
SELECT COUNT(*)::INTEGER
FROM comments c
WHERE c.post_id = p.id
), 0)
WHERE p.legacy_post_id IS NOT NULL;
-- ============================================
-- PART 6: Summary logging
-- ============================================
DO $$
DECLARE
legacy_count INTEGER;
migrated_count INTEGER;
root_count INTEGER;
nested_count INTEGER;
orphan_count INTEGER;
BEGIN
SELECT COUNT(*) INTO legacy_count FROM "Comment";
SELECT COUNT(*) INTO migrated_count FROM _comment_migration_map;
SELECT COUNT(*) INTO root_count FROM _comment_migration_map WHERE depth = 0;
SELECT COUNT(*) INTO nested_count FROM _comment_migration_map WHERE depth > 0;
-- Count orphaned comments (parent not migrated or post not migrated)
SELECT COUNT(*) INTO orphan_count
FROM "Comment" c
WHERE NOT EXISTS (
SELECT 1 FROM _comment_migration_map m WHERE m.legacy_comment_id = c.id
);
RAISE NOTICE 'Migration 0016 complete:';
RAISE NOTICE ' Legacy comments: %', legacy_count;
RAISE NOTICE ' Migrated comments: % (% root, % nested)', migrated_count, root_count, nested_count;
IF orphan_count > 0 THEN
RAISE NOTICE ' Orphaned comments (not migrated): %', orphan_count;
END IF;
END $$;
-- ============================================
-- PART 7: Notes
-- ============================================
-- The _comment_migration_map table is kept for:
-- 1. Debugging and verification
-- 2. Potential future migrations (e.g., notifications referencing comments)
-- 3. Rollback capability
--
-- It can be dropped in a future migration after verification:
-- DROP TABLE IF EXISTS _comment_migration_map;