I have two tables:
posts:
id | ... other stuff ... | tags
----+---------------------+--------------
1 | ... | <foo><bar>
2 | ... | <foo><baz><blah>
3 | ... | <bar><blah><goo>
and tags:
tag
--------------
<foo>
<bar>
<baz>
<blah>
<goo>
posts.tags and tags.tag are both of type text. What I want is a relation from tags.tag to rows in posts such that querying <foo> would give me rows corresponding to posts 1 and 2, querying <blah> gives me 2 and 3, <bar> gives me 1 and 3, etc.
I’ve looked at foreign keys, but I’m not sure it’s what I want. (and honestly, I’m not exactly sure what it does). From what I can tell a foreign key must be equal to a primary key/unique column of a table. But what I want is all rows such that posts.tags ~ '.*<foo>.*', etc. I also want to be able to, say, get all tags that start with b, eg:
CREATE VIEW startswithB AS
SELECT tag
FROM tags
WHERE tag ~ '<b.*>';
SELECT DISTINCT * FROM posts, startswithB WHERE posts.tags ~ ('.*' || startswithB || '.*');
How do I get the relation I’m looking for? Is it possible?
EDIT:
Okay, what I’ve done:
create post_tags:
SELECT posts.id, tags.tag
INTO post_tags
FROM posts, tags
WHERE posts.tags ~ ('.*' || tags.tag || '.*');
select all posts with tag <foo>:
SELECT *
FROM posts
WHERE posts.id IN (
SELECT id
FROM post_tags
WHERE tag = '<foo>'
);
What you actually have going on here is a many-to-many relationship. Think about it: each tag can be on several posts, and each post can have several tags.
The correct relational architecture for this is to add another table in the middle like this:
Then drop the
tagscolumn on your posts table.This solves all your issues, because you can get the set of tags on a post or the set of posts with a given tag by joining against post_tags in different directions. You can also get the list of tags that start with something using a regular LIKE query, which will be more difficult if you have a bunch of strings concatenated in one field.