142 lines
9.0 KiB
MySQL
142 lines
9.0 KiB
MySQL
|
|
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
|
|
-- Posimai Brain Full-Text Save Migration
|
||
|
|
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
|
|
-- Purpose: Add full_text column to store article body from Reader
|
||
|
|
-- Date: 2026-03-01
|
||
|
|
-- Impact: Critical data loss prevention
|
||
|
|
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 1: Backup existing data
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- Before running migration, create backup:
|
||
|
|
-- pg_dump -U brain_user -d brain_db -t articles > /backup/articles_$(date +%Y%m%d).sql
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 2: Add full_text column
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
BEGIN;
|
||
|
|
|
||
|
|
-- Add full_text column for storing article body
|
||
|
|
ALTER TABLE articles
|
||
|
|
ADD COLUMN IF NOT EXISTS full_text TEXT;
|
||
|
|
|
||
|
|
-- Add images column (optional, for future use)
|
||
|
|
ALTER TABLE articles
|
||
|
|
ADD COLUMN IF NOT EXISTS images TEXT[];
|
||
|
|
|
||
|
|
-- Add updated_at if it doesn't exist
|
||
|
|
ALTER TABLE articles
|
||
|
|
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP DEFAULT NOW();
|
||
|
|
|
||
|
|
COMMIT;
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 3: Create indexes for performance
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
BEGIN;
|
||
|
|
|
||
|
|
-- Full-text search index (Japanese language support)
|
||
|
|
-- This enables fast text search in Brain UI
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_articles_full_text_search
|
||
|
|
ON articles USING gin(to_tsvector('english', COALESCE(full_text, '')));
|
||
|
|
|
||
|
|
-- Note: PostgreSQL doesn't have built-in Japanese tokenizer by default
|
||
|
|
-- For better Japanese search, consider installing pg_bigm extension:
|
||
|
|
-- CREATE EXTENSION IF NOT EXISTS pg_bigm;
|
||
|
|
-- CREATE INDEX IF NOT EXISTS idx_articles_full_text_bigm
|
||
|
|
-- ON articles USING gin(full_text gin_bigm_ops);
|
||
|
|
|
||
|
|
-- Index for finding articles without full_text (for migration/cleanup)
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_articles_missing_full_text
|
||
|
|
ON articles (id) WHERE full_text IS NULL;
|
||
|
|
|
||
|
|
COMMIT;
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 4: Verify schema changes
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- \d articles
|
||
|
|
|
||
|
|
-- Expected output should include:
|
||
|
|
-- | Column | Type | Nullable | Description |
|
||
|
|
-- |-------------|-----------|----------|--------------------------------|
|
||
|
|
-- | id | SERIAL | NOT NULL | Primary key |
|
||
|
|
-- | user_id | INTEGER | NOT NULL | Foreign key to users table |
|
||
|
|
-- | url | TEXT | NOT NULL | Original article URL |
|
||
|
|
-- | title | TEXT | NOT NULL | Article title |
|
||
|
|
-- | full_text | TEXT | NULL | ← NEW! Article body from Reader|
|
||
|
|
-- | summary | TEXT | NULL | AI-generated 3-sentence summary|
|
||
|
|
-- | topics | TEXT[] | NULL | AI-generated topic tags |
|
||
|
|
-- | source | TEXT | NULL | Source (reader/feed/bookmarklet)|
|
||
|
|
-- | reading_time| INTEGER | NULL | Estimated reading time (minutes)|
|
||
|
|
-- | favicon | TEXT | NULL | Site favicon URL |
|
||
|
|
-- | og_image | TEXT | NULL | OGP image URL |
|
||
|
|
-- | images | TEXT[] | NULL | ← NEW! Article images (future) |
|
||
|
|
-- | status | TEXT | NOT NULL | inbox/favorite/shared/archived |
|
||
|
|
-- | created_at | TIMESTAMP | NOT NULL | Record creation time |
|
||
|
|
-- | updated_at | TIMESTAMP | NULL | ← NEW! Record update time |
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 5: Test query (verify columns exist)
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
SELECT
|
||
|
|
id,
|
||
|
|
title,
|
||
|
|
LENGTH(full_text) as text_length,
|
||
|
|
LEFT(full_text, 50) as text_preview,
|
||
|
|
summary,
|
||
|
|
topics,
|
||
|
|
created_at
|
||
|
|
FROM articles
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
LIMIT 5;
|
||
|
|
|
||
|
|
-- Expected: All existing articles will have NULL for full_text
|
||
|
|
-- New articles saved after API update will have full_text populated
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 6: Clean up old articles (optional)
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- If you want to delete old articles without full_text after migration:
|
||
|
|
-- (Run this ONLY after confirming new articles are saving correctly)
|
||
|
|
|
||
|
|
-- Count articles without full_text
|
||
|
|
SELECT COUNT(*) as articles_without_fulltext
|
||
|
|
FROM articles
|
||
|
|
WHERE full_text IS NULL;
|
||
|
|
|
||
|
|
-- Optionally delete old articles without full_text (BE CAREFUL!)
|
||
|
|
-- DELETE FROM articles WHERE full_text IS NULL AND created_at < NOW() - INTERVAL '30 days';
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 7: Add constraint for new articles (optional, recommended)
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- After confirming Reader is sending full_text correctly,
|
||
|
|
-- you can add a constraint to ensure all new articles have full_text:
|
||
|
|
|
||
|
|
-- ALTER TABLE articles
|
||
|
|
-- ADD CONSTRAINT full_text_required
|
||
|
|
-- CHECK (
|
||
|
|
-- (source = 'reader' AND full_text IS NOT NULL) OR
|
||
|
|
-- (source != 'reader')
|
||
|
|
-- );
|
||
|
|
|
||
|
|
-- This ensures Reader-sourced articles MUST have full_text
|
||
|
|
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- STEP 8: Grant permissions (if needed)
|
||
|
|
-- ──────────────────────────────────────────────────────────────────────
|
||
|
|
-- If using a separate API user, grant access to new columns:
|
||
|
|
-- GRANT SELECT, INSERT, UPDATE ON articles TO brain_api_user;
|
||
|
|
|
||
|
|
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
|
|
-- Migration Complete!
|
||
|
|
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
|
|
-- Next steps:
|
||
|
|
-- 1. Update Brain API server code (see BRAIN_FULLTEXT_IMPLEMENTATION_GUIDE.md)
|
||
|
|
-- 2. Test saving an article from Reader
|
||
|
|
-- 3. Verify full_text is populated in database
|
||
|
|
-- 4. Deploy Brain API changes to production
|
||
|
|
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|