o7412369815963
пока не делал, но все равно пока вариантов нет. буду делать так.
Попробовал потестить. Создал таблицу, где один столбец - данные, другой - тег:
/*create table*/
CREATE TABLE entry (
    id SERIAL,
    data varchar(32),
    tag varchar(32)
);
Забил таблицу записями. На каждую единицу данных - пять различных тегов. Теги добавлялись в список последовательно, по мере заполнения таблицы, чтобы одни теги встречались чаще других.
import random
import hashlib
import psycopg2
CONN_STRING = "dbname=tags user=pmaster"
RECORDS = 1000000
TAGS = 1000
TAGS_FOR_RECORD = 5
def main():
    tags = ['a', 'b', 'c', 'd', 'e']
    progress = 0
    record_list = []
    for i in xrange(RECORDS/TAGS_FOR_RECORD):
        #generating data to be saved                                                                                                                         
	use_tags = random.sample(tags, 5)
	data = hashlib.md5(str(i)).hexdigest()
        record_list.append([data, use_tags])
        #splitting data into portions                                                                                                                        
        if i % (RECORDS/(TAGS_FOR_RECORD*TAGS)) == 0:
	    #printing progress                                                                                                                               
            progress += 1
            print progress
            save_records(record_list)
            record_list = []
            #adding new tags                                                                                                                                 
            tags.append('tag%s' %i)
    save_records(record_list)
    print '---'
    print 'records:', (i+1)*TAGS_FOR_RECORD, 'tags:', len(tags)
def save_records(record_list):
    conn = psycopg2.connect(CONN_STRING)
    cur = conn.cursor()
    for data, tags in record_list:
        for tag in tags:
            cur.execute('insert into entry (data, tag) values (%s, %s)', (data, tag))
    conn.commit()
    cur.close()
    conn.close()
Получаем начальное облако:
/* get tag cloud of most popular tags*/
select
    count(tag), tag
  from
    entry
  group by
    tag
  order by
    count(tag) desc fetch first 100 rows only
/*  0.39 s */
Сужаю теги:
select
    count(tag), tag
  from
    entry
  where (data in (
	select
		data
	from
		entry
	where
		tag='d'))
		and (tag!= 'd')
  group by
    tag
  order by
	count(tag) desc
    fetch first 100 rows only
/* 0.79 s */
А вот получение самих данных уже по-быстрее:
select distinct
     data
  from
    entry
  where
    (tag = 'b') and data in (
	select
		data
	from
		entry
	where
		tag = 'c')
        fetch first 100 rows only
/* 0.25 s */
Если убрать distinct, то еще вдвое быстрее.
В общем, не фонтан, конечно, тестил на ноутбуке core 2 duo 2.1 GHz, 2Gb mem. Вопрос в том какое будет железо, ну и запросы влоб, какие я для тестов использовал, нужно заменить скорее всего на что-то более адекватное.