Поиск текстов по тагам. Построение SQL запроса по КНФ запроса пользователя.

phprus

Moderator
Команда форума
Поиск текстов по тагам. Построение SQL запроса по КНФ запроса пользователя.

Этот вопрос является продолжением темы http://phpclub.ru/talk/showthread.php?s=&threadid=109792&rand=4.
Необходимость задать этот вопрос была вызвана тем, что к поиску был сделан язык запросов.

Есть 3 таблицы:

Код:
text:
    id - integer
    group_id - integer
    updated_at - timestamp

tag_to_text:
    text_id
    tag_id

text_tag:
    id
    group_id
    text - строка
таблицы text и tag_to_text связаны как один ко многим.

Есть поисковый запрос приведенный к форме:
(* OR * OR * OR ...) AND (* OR * OR * ...) AND (...) AND ...
где * - это либо слово, либо NOT слово.

К примеру для запроса
(test4_tag1 | test4_tag4 | ~test4_tag3) & ~test4_tag2
будет построен такой SQL запрос:
[sql]
SELECT t.*
FROM text AS t
WHERE
t.group_id IN (4)
AND
EXISTS (
SELECT 1
FROM text_to_tag as t2t
INNER JOIN text_tag as tt on (tt.id = t2t.tag_id)
WHERE
t.id = t2t.text_id
AND (
tt.text = 'test4_tag1'
OR
tt.text = 'test4_tag4'
OR
tt.text != 'test4_tag3'
)
)
AND
NOT EXISTS (
SELECT 1
FROM text_to_tag as t2t
INNER JOIN text_tag as tt on (tt.id = t2t.tag_id)
WHERE
t.id = t2t.text_id
AND
tt.text = 'test4_tag2'
)

ORDER BY t.updated_at DESC
[/sql]

Скажите пожалуйста, на сколько оптимально будет строить запросы подобные этому или есть более оптимальное решение?
А так-же не подскажите ли вы какие индексы лучше расставить по таблицам, чтобы они были наиболее оптимальными?
 

Mols

Новичок
ИМХО второй подзапрос не нужен. Можно в первом всё указать. Для приведённого примера достаточно в первый запрос добавить
[SQL]
AND tt.text != 'test4_tag2'
[/SQL]
 

phprus

Moderator
Команда форума
Mols
AND tt.text != 'test4_tag2'
Добавление этого даст неверный результат.
В таблице tag_to_text для одного text_id может быть много записей с разными tag_id. По этому если добавить AND tt.text != 'test4_tag2', то могут выбраться и те text_id в которым кроме всего прочего соответствует и tt.text != 'test4_tag2'.
 
Сверху