Создание теговой системы

Интересует какие инструменты лучше всего применить для создания теговой системы?
Допустим у меня есть база постов и укажого поста список тегов. И есть запрос содержащий несколько тегов. Нужно найти наилучшее соответствие.
Пример:
Данные:
{'post': '...', tags: ['foo', 'boo', 'goo', 'doo', 'too']}
{'post': '...', tags: ['boo', 'goo', 'too']}
{'post': '...', tags: ['foo', 'too']}
{'post': '...', tags: ['doo', 'too']}
{'post': '...', tags: ['too']}
....

Запрос: {tags: ['boo', 'too']}
Ответ:
{'post': '...', tags: ['boo', 'goo', 'too']}
{'post': '...', tags: ['foo', 'boo', 'goo', 'doo', 'too']}  
{'post': '...', tags: ['too']}
{'post': '...', tags: ['foo', 'too']}
{'post': '...', tags: ['doo', 'too']}
....
  • Вопрос задан
  • 3221 просмотр
Решения вопроса 1
@DenKrep
1) Извиняюсь, ошибся. Я не на хабре эту статью видел. Вот здесь была она: dou.ua/lenta/articles/suggester/
2) Да, MapReduce имеет как следствие полный перебор источника данных. Это один из недостатков. Но действительно ли вам нужна технология основанная map-reduce? Есть большие сомнения.
3) 100 ГБ это не очень большая база на сегодняшний день, скажем откровенно. Бывают базы-справочники (MDM) в десятки раз больше. Тем более если вы в базе храните сами документы, то это может быть даже мало.
3.1) Сколько строк планируется в таблице (списке) статей.
3.2) Сколько уникальных тегов ожидаете
3.3) Сколько, в среднем, к одной статье привязано тегов?
Если ответ — миллионы / десятки миллионов, то рядовая реляционка спокойно потянет ваши запросы за доли секунд.
4) Статей — множество. Описаний алгоритмов индексации — тоже. Один из примеров — ссылка в п.1 этого сообщения. Но уверенны что хотите изобретать велосипед и хотите самостоятельно создавать такой индекс?
5) PS: кстати, если вам нужно будет индексировать сами документы, то тут уже стоит посмотреть на какие-нибудь продукты основанные на Lucene (Solr, elastic search, ...). Но это уже совсем другая песня :)
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
copist
@copist
Empower people to give
CREATE TABLE POST (
  ID DECIMAL(20,0), -- AUTOINCREMENT
  CONTENT VARCHAR2(1024)
);

CREATE TABLE TAG (
  ID DECIMAL(20,0), -- AUTOINCREMENT
  TAG VARCHAR2(100)
);

CREATE TABLE POST_TAG (
  POST_ID DECIMAL(20,0),
  TAG_ID DECIMAL(20,0)
);
-- FOREIGN KEY FK_POST_TAG_POST ON POST(ID);
-- FOREIGN KEY FK_POST_TAG_TAG ON TAG(ID);

CREATE UNIQUE INDEX UQ_POST ON POST(ID);
CREATE UNIQUE INDEX UQ_TAG ON TAG(ID);
CREATE INDEX IDX_POST_TAG ON POST_TAG(POST_ID, TAG_ID);
CREATE INDEX IDX_POST_TAG_R ON POST_TAG(TAG_ID, POST_ID);

INSERT INTO TAG(ID, TAG) VALUES(1, 'foo');
INSERT INTO TAG(ID, TAG) VALUES(2, 'boo');
INSERT INTO TAG(ID, TAG) VALUES(3, 'goo');
INSERT INTO TAG(ID, TAG) VALUES(4, 'doo');
INSERT INTO TAG(ID, TAG) VALUES(5, 'too');

INSERT INTO POST(ID, CONTENT) VALUES(1, '...');
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(1, 1 /* foo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(1, 2 /* boo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(1, 3 /* goo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(1, 4 /* doo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(1, 5 /* too */);

INSERT INTO POST(ID, CONTENT) VALUES(2, '...');
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(2, 2 /* boo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(2, 3 /* goo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(2, 5 /* too */);

INSERT INTO POST(ID, CONTENT) VALUES(3, '...');
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(3, 1 /* foo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(3, 5 /* too */);

INSERT INTO POST(ID, CONTENT) VALUES(4, '...');
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(4, 4 /* doo */);
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(4, 5 /* too */);

INSERT INTO POST(ID, CONTENT) VALUES(5, '...');
INSERT INTO POST_TAG(POST_ID, TAG_ID) VALUES(5, 5 /* too */);


SELECT POST.ID, TAG.TAG
FROM POST, TAG, POST_TAG
WHERE POST.ID = POST_TAG.POST_ID AND TAG.ID = POST_TAG.TAG_ID
ORDER BY POST.ID, TAG.ID;


SELECT DISTINCT ID, POST_TAG_COUNT, POST_TAGS_MATCHED_COUNT, POST_TAG_COUNT - POST_TAGS_MATCHED_COUNT POST_TAGS_EXTRA
FROM (
  SELECT
    POST.ID,

    ( -- количество тегов всего
      SELECT COUNT(POST_TAG.TAG_ID) CNT
      FROM POST_TAG
      WHERE POST_TAG.POST_ID = POST.ID
    ) POST_TAG_COUNT,

    ( -- количество найденных по точному совпадению
      SELECT COUNT(POST_TAG.TAG_ID) CNT
      FROM POST_TAG
      JOIN TAG ON POST_TAG.TAG_ID = TAG.ID
      WHERE TAG.TAG IN ('boo', 'too') AND POST_TAG.POST_ID = POST.ID
    ) POST_TAGS_MATCHED_COUNT
  FROM POST
  JOIN POST_TAG ON POST_TAG.POST_ID = POST.ID
  JOIN TAG ON TAG.ID = POST_TAG.TAG_ID
  WHERE TAG.TAG IN ('boo', 'too')
)
WHERE POST_TAGS_MATCHED_COUNT > 0
ORDER BY POST_TAGS_MATCHED_COUNT DESC, POST_TAG_COUNT - POST_TAGS_MATCHED_COUNT ASC;



Смысл запроса на выборку:

Найти общее количество тегов у постов и количество совпавших тегов, вывести в порядке убывания количества совпадений, затем в порядке возрастания «лишних тегов».


Если нужен вывод именно в таком порядке, то практически полный перебор

Базы в 100 гигов текста тоже под рукой пока нет. Не замерял. Вес (оценка сложности исполнения) запроса с индексами небольшой, вполне возможно — миллисекунды.

Это только поиск ID постов.


И надо на всякий случай лимит на запрос поставить.
Ответ написан
@DenKrep
Насколько большой объём данных? Какие-то специфические требования к систему есть? Т.к. если нету каких-то особенных требований, но, судя по задаче, уже есть некоторая БД, где это уже хранится, то просто введение таблицы связки (многие ко многим между таблицами постов и тегов) позволит реализовывать то, что вы хотите.Если, конечно, сейчас оно хранится в реляционной БД. Если сейчас ваши посты хранятся в парах ключ-значение, то можете почитать про прямые и обратные индексы:
По сути хранить с постами список тегов (или указателей для оптимизации), к которым принадлежит статья, а также хранить теги у которых храним статьи которые к ним принадлежат типа так:
posts (
{post1: tags [1,2]};
{post2: tags[2,3]}
)
tags(
{tag1: posts[1]}
{tag2: posts[1,2]}
{tag3: posts[2]}
)
При выполнении поискового запроса вам останется только найти пересечения множеств из выбранных тегов.
Ответ написан
SiDChik
@SiDChik
использовать теги в полнотекстовом поле, выполняя поиск по этому полю отсортировать по релевантности?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы