views:

154

answers:

3

I'm creating custom forum software for a site I'm building, which includes 2 tables (that are relevant to this question): topics and posts. A post belongs to a topic, and the topic contains the subject, while each post contains the body.

Here is the basic table structures with the columns relevant to my question:

CREATE TABLE topics (
  id bigserial NOT NULL,
  title varchar(128) NOT NULL,
  created timestamp with time zone NOT NULL default NOW(),
  updated timestamp with time zone NOT NULL default NOW(),
  PRIMARY KEY (id)
);

CREATE TABLE posts (
  id bigserial NOT NULL,
  topic_id bigint NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
  body text NOT NULL,
  created timestamp with time zone NOT NULL default NOW(),
  updated timestamp with time zone NOT NULL default NOW(),
  PRIMARY KEY (id)
);

Here are my two options on building fulltext indexes.

Option 1: Create dynamic tsvector indexes on title/body columns.

CREATE INDEX topics_title_idx ON topics USING gin(to_tsvector(title));
CREATE INDEX posts_body_idx ON posts USING gin(to_tsvector(body));

Option 2: Create extra columns to hold tsvector-ized title/body data, and add indexes on those.

ALTER TABLE topics ADD COLUMN topics_vector tsvector NOT NULL;
CREATE TRIGGER topics_ins BEFORE INSERT OR UPDATE ON topics FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(title_vector, 'pg_catalog.english', title);
CREATE INDEX topics_title_idx ON topics USING gin(title_vector);

ALTER TABLE posts ADD COLUMN posts_vector tsvector NOT NULL;
CREATE TRIGGER posts_ins BEFORE INSERT OR UPDATE ON posts FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(body_vector, 'pg_catalog.english', body);
CREATE INDEX posts_body_idx ON posts USING gin(body_vector);

I'm debating between the two since option 1 will save me disk space, but provide slower searches, and option 2 will require additional disk space while providing faster searches.

Let's pretend there are 20 new topics & 100 new posts per day. Which would you choose? What if the number of topics/posts per day way twice that? Five times that? Ten times? Does your decision of one vs. the other change?

+2  A: 

Let's pretend there are 20 new topics & 100 new posts per day. Which would you choose? What if the number of topics/posts per day way twice that? Five times that? Ten times? Does your decision of one vs. the other change?

That's about 36,000 posts a year. Doesn't matter. Probably doesn't matter with ten times that, even on a cheap machine.

However, you might want a third table containing an explicit tsvector combining topic and body-text together. You can then use the built-in weighting system and run one search to provide the sort of search people generally expect on forums etc. That will mean writing custom triggers to update your tsvector when either source table is changed.

Richard Huxton
+2  A: 

Normally I'd go with storing the tsvector in a field, because that will also give you usable access to things like headline() and rank().

Magnus Hagander
+2  A: 

Using Option 1 will not make your searches more slow.

The GIN index will be used regardless of whether you created in on instantiated column or computed expression.

You just need to change the query syntax:

SELECT  *
FROM    posts
WHERE   TO_TSVECTOR('english', title) @@ myquery

in the first case, or

SELECT  *
FROM    posts
WHERE   title_vector @@ myquery

in the second case.

You probably can save a little time when using TS_RANK over the instantiated column.

Quassnoi
Hmm, maybe I misread something in the manual then, because I was left with the impression that it would be quicker to do something like option 2.
Matt Huggins
If you are doing operations with the `TS_VECTOR` itself, like `TS_RANK`, then option `2` will be faster. If you only use `TS_VECTOR` for searching, the performance will be the same. The persisted column will not even be referenced by the optimizer.
Quassnoi
Good to know. I'm going to want to sort by rank, so I'll likely end up using option 2. Thanks!
Matt Huggins