Commit 7e3f5c0f authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Add tags table to database.

Implement the tags in nice relational fashion, but set up triggers
to avoid quering the tags table with each select of comments, and
instead precalculate a textual tags column inside the comments
table.

Also added a migration script.
parent 5f35507f
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -435,4 +435,12 @@ describe("Comments", function() {
            });
        });
    });

    it("#getById includes concatenated list of tags in the returned comment", function(done) {
        comments.getById(1, function(err, com) {
            expect(com.tags).toEqual("bug\tfeature");
            done();
        });
    });

});
+82 −0
Original line number Diff line number Diff line
-- Add new column and related tables

ALTER TABLE comments ADD COLUMN tags TEXT NOT NULL DEFAULT '' AFTER vote;

CREATE TABLE tags (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    domain VARCHAR(50) NOT NULL,
    tagname VARCHAR(100) NOT NULL,
    -- unique tags within domain
    CONSTRAINT unique_tags UNIQUE KEY (domain, tagname)
) ENGINE = InnoDB, CHARACTER SET = utf8;

CREATE TABLE comment_tags (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    comment_id INT NOT NULL,
    tag_id INT NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
    FOREIGN KEY (comment_id) REFERENCES comments (id) ON DELETE CASCADE,
    FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE,
    -- a comment can't have the same tag twice
    CONSTRAINT unique_comment_tags UNIQUE KEY (tag_id, comment_id)
) ENGINE = InnoDB, CHARACTER SET = utf8;


-- recreate all old views

CREATE OR REPLACE VIEW visible_comments AS SELECT * FROM comments WHERE deleted = 0;

CREATE OR REPLACE VIEW full_visible_comments AS SELECT
    c.*,
    users.username,
    users.external_id,
    users.email,
    users.moderator,
    targets.domain,
    targets.type,
    targets.cls,
    targets.member
FROM visible_comments AS c
    LEFT JOIN users ON c.user_id = users.id
    LEFT JOIN targets ON c.target_id = targets.id;

CREATE OR REPLACE VIEW full_comments AS SELECT
    c.*,
    users.username,
    users.external_id,
    users.email,
    users.moderator,
    targets.domain,
    targets.type,
    targets.cls,
    targets.member
FROM comments AS c
    LEFT JOIN users ON c.user_id = users.id
    LEFT JOIN targets ON c.target_id = targets.id;


-- add new view and triggers

CREATE OR REPLACE VIEW concatenated_tags AS
    SELECT
        comment_id,
        GROUP_CONCAT(tagname ORDER BY tagname SEPARATOR '\t') AS tagstring
    FROM comment_tags
    JOIN tags on tags.id = comment_tags.tag_id
    GROUP BY comment_id;

DROP TRIGGER IF EXISTS on_tag_added;
CREATE TRIGGER on_tag_added AFTER INSERT ON comment_tags
FOR EACH ROW
    UPDATE comments
    SET tags = (SELECT tagstring FROM concatenated_tags ctags WHERE ctags.comment_id = comments.id)
    WHERE id = NEW.comment_id;

DROP TRIGGER IF EXISTS on_tag_deleted;
CREATE TRIGGER on_tag_deleted AFTER DELETE ON comment_tags
FOR EACH ROW
    UPDATE comments
    SET tags = (SELECT tagstring FROM concatenated_tags ctags WHERE ctags.comment_id = comments.id)
    WHERE id = OLD.comment_id;
+49 −0
Original line number Diff line number Diff line
DROP TABLE IF EXISTS comment_tags;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS readings;
DROP TABLE IF EXISTS subscriptions;
DROP TABLE IF EXISTS updates;
@@ -31,6 +33,7 @@ CREATE TABLE comments (
    content TEXT NOT NULL,
    content_html TEXT NOT NULL,
    vote INT NOT NULL DEFAULT 0,
    tags TEXT NOT NULL DEFAULT '',
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    deleted BOOLEAN NOT NULL DEFAULT 0,
    FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
@@ -81,6 +84,28 @@ CREATE TABLE readings (
    CONSTRAINT unique_readings UNIQUE KEY (user_id, comment_id)
) ENGINE = InnoDB, CHARACTER SET = utf8;

CREATE TABLE tags (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    domain VARCHAR(50) NOT NULL,
    tagname VARCHAR(100) NOT NULL,
    -- unique tags within domain
    CONSTRAINT unique_tags UNIQUE KEY (domain, tagname)
) ENGINE = InnoDB, CHARACTER SET = utf8;

CREATE TABLE comment_tags (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    comment_id INT NOT NULL,
    tag_id INT NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
    FOREIGN KEY (comment_id) REFERENCES comments (id) ON DELETE CASCADE,
    FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE,
    -- a comment can't have the same tag twice
    CONSTRAINT unique_comment_tags UNIQUE KEY (tag_id, comment_id)
) ENGINE = InnoDB, CHARACTER SET = utf8;


CREATE OR REPLACE VIEW visible_comments AS SELECT * FROM comments WHERE deleted = 0;

-- comments table joined with users and targets for easier quering
@@ -129,3 +154,27 @@ FOR EACH ROW
    UPDATE comments
    SET vote = (SELECT SUM(value) FROM votes WHERE votes.comment_id = comments.id)
    WHERE id = OLD.comment_id;

-- set up triggers to recalculate the tags column automatically

CREATE OR REPLACE VIEW concatenated_tags AS
    SELECT
        comment_id,
        GROUP_CONCAT(tagname ORDER BY tagname SEPARATOR '\t') AS tagstring
    FROM comment_tags
    JOIN tags on tags.id = comment_tags.tag_id
    GROUP BY comment_id;

DROP TRIGGER IF EXISTS on_tag_added;
CREATE TRIGGER on_tag_added AFTER INSERT ON comment_tags
FOR EACH ROW
    UPDATE comments
    SET tags = (SELECT tagstring FROM concatenated_tags ctags WHERE ctags.comment_id = comments.id)
    WHERE id = NEW.comment_id;

DROP TRIGGER IF EXISTS on_tag_deleted;
CREATE TRIGGER on_tag_deleted AFTER DELETE ON comment_tags
FOR EACH ROW
    UPDATE comments
    SET tags = (SELECT tagstring FROM concatenated_tags ctags WHERE ctags.comment_id = comments.id)
    WHERE id = OLD.comment_id;
+10 −0
Original line number Diff line number Diff line
@@ -189,3 +189,13 @@ INSERT INTO readings SET `user_id` = 1, `comment_id` = 3, `created_at` = '2011-0
INSERT INTO readings SET `user_id` = 1, `comment_id` = 4, `created_at` = '2011-01-01 00:00:00';
INSERT INTO readings SET `user_id` = 1, `comment_id` = 5, `created_at` = '2011-01-01 00:00:00';
INSERT INTO readings SET `user_id` = 1, `comment_id` = 6, `created_at` = '2011-01-01 00:00:00';


-- tags
INSERT INTO tags SET id = 1, domain = 'ext-js-4', tagname = 'bug';
INSERT INTO tags SET id = 2, domain = 'ext-js-4', tagname = 'feature';

-- comment_tags
INSERT INTO comment_tags SET tag_id = 1, comment_id = 1, user_id = 1;
INSERT INTO comment_tags SET tag_id = 2, comment_id = 1, user_id = 1;
INSERT INTO comment_tags SET tag_id = 2, comment_id = 2, user_id = 1;