require 'sqlite3' module ImportScripts class GenericDatabase def initialize(directory, batch_size:, recreate: false) filename = "#{directory}/index.db" File.delete(filename) if recreate && File.exists?(filename) @db = SQLite3::Database.new(filename, results_as_hash: true) @batch_size = batch_size configure_database create_category_table create_user_table create_topic_table create_post_table end def insert_category(category) @db.execute(<<-SQL, prepare(category)) INSERT OR REPLACE INTO category (id, name, description, position, url) VALUES (:id, :name, :description, :position, :url) SQL end def insert_user(user) @db.execute(<<-SQL, prepare(user)) INSERT OR REPLACE INTO user (id, email, username, name, created_at, last_seen_at, active) VALUES (:id, :email, :username, :name, :created_at, :last_seen_at, :active) SQL end def insert_topic(topic) @db.execute(<<-SQL, prepare(topic)) INSERT OR REPLACE INTO topic (id, title, raw, category_id, closed, user_id, created_at, url) VALUES (:id, :title, :raw, :category_id, :closed, :user_id, :created_at, :url) SQL end def insert_post(post) @db.execute(<<-SQL, prepare(post)) INSERT OR REPLACE INTO post (id, raw, topic_id, user_id, created_at, reply_to_post_id, url) VALUES (:id, :raw, :topic_id, :user_id, :created_at, :reply_to_post_id, :url) SQL end def sort_posts_by_created_at @db.execute 'DELETE FROM post_order' @db.execute <<-SQL INSERT INTO post_order (id) SELECT id FROM post ORDER BY created_at, topic_id, id SQL end def fetch_categories @db.execute(<<-SQL) SELECT * FROM category ORDER BY position, name SQL end def count_users @db.get_first_value(<<-SQL) SELECT COUNT(*) FROM user SQL end def fetch_users(last_id) rows = @db.execute(<<-SQL, last_id) SELECT * FROM user WHERE id > :last_id ORDER BY id LIMIT #{@batch_size} SQL add_last_column_value(rows, 'id') end def count_topics @db.get_first_value(<<-SQL) SELECT COUNT(*) FROM topic SQL end def fetch_topics(last_id) rows = @db.execute(<<-SQL, last_id) SELECT * FROM topic WHERE id > :last_id ORDER BY id LIMIT #{@batch_size} SQL add_last_column_value(rows, 'id') end def count_posts @db.get_first_value(<<-SQL) SELECT COUNT(*) FROM post SQL end def fetch_posts(last_row_id) rows = @db.execute(<<-SQL, last_row_id) SELECT o.ROWID, p.* FROM post p JOIN post_order o USING (id) WHERE o.ROWID > :last_row_id ORDER BY o.ROWID LIMIT #{@batch_size} SQL add_last_column_value(rows, 'rowid') end def execute_sql(sql) @db.execute(sql) end def get_first_value(sql) @db.get_first_value(sql) end private def configure_database @db.execute 'PRAGMA journal_mode = OFF' @db.execute 'PRAGMA locking_mode = EXCLUSIVE' end def create_category_table @db.execute <<-SQL CREATE TABLE IF NOT EXISTS category ( id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL, description TEXT, position INTEGER, url TEXT ) SQL end def create_user_table @db.execute <<-SQL CREATE TABLE IF NOT EXISTS user ( id TEXT NOT NULL PRIMARY KEY, email TEXT, username TEXT, name TEXT, created_at DATETIME, last_seen_at DATETIME, active BOOLEAN NOT NULL DEFAULT true ) SQL end def create_topic_table @db.execute <<-SQL CREATE TABLE IF NOT EXISTS topic ( id TEXT NOT NULL PRIMARY KEY, title TEXT, raw TEXT, category_id TEXT NOT NULL, closed BOOLEAN NOT NULL DEFAULT false, user_id TEXT NOT NULL, created_at DATETIME, url TEXT ) SQL @db.execute 'CREATE INDEX IF NOT EXISTS topic_by_user_id ON topic (user_id)' end def create_post_table @db.execute <<-SQL CREATE TABLE IF NOT EXISTS post ( id TEXT NOT NULL PRIMARY KEY, raw TEXT, topic_id TEXT NOT NULL, user_id TEXT NOT NULL, created_at DATETIME, reply_to_post_id TEXT, url TEXT ) SQL @db.execute 'CREATE INDEX IF NOT EXISTS post_by_user_id ON post (user_id)' @db.execute <<-SQL CREATE TABLE IF NOT EXISTS post_order ( id TEXT NOT NULL PRIMARY KEY ) SQL end def prepare(hash) hash.each do |key, value| if value.is_a?(TrueClass) || value.is_a?(FalseClass) hash[key] = value ? 1 : 0 elsif value.is_a?(Date) hash[key] = value.to_s end end end def add_last_column_value(rows, *last_columns) return rows if last_columns.empty? result = [rows] last_row = rows.last last_columns.each { |column| result.push(last_row ? last_row[column] : nil) } result end end end