Как сделать поиск по нескольким ключевым словам? - PullRequest
2 голосов
/ 14 февраля 2009

У меня есть 2 таблицы для поиска. Поиск фотографий по ключевым словам, названию и описанию. Ключевые слова были выделены в отдельную таблицу. Мой расширенный поиск позволит искать все 3, но основной будет таблица ключевых слов.

Базовая настройка таблицы:

ФОТО Стол

  • PhotoID
  • Имя
  • Название
  • Описание

WORD2PHOTO Таблица

  • WordID
  • PhotoID
  • Слово

Пробовал просмотры и хранимые процедуры, но не получал хороших результатов. Я получаю свои фотографии из просмотров, но в нескольких записях. Я бы занимался фильтрацией и тому подобным на стороне приложения, но я использую дозвуковую среду и хотел бы использовать встроенный пейджинг; это совсем другая проблема.

Во-первых, как мне искать по нескольким ключевым словам? Во-вторых, как мне добавить в поиск по названию и описанию?

У меня есть функция (f_Split), которая будет возвращать временную таблицу моих текущих слов для поиска,

DECLARE @Words TABLE (Word varchar(20))

INSERT INTO @Words (Word)
SELECT Keyword FROM dbo.f_Split('cars|auto|red|fast','|')

Теперь, как мне использовать таблицу, которую он генерирует, чтобы получить записи фотографий? Боролись уже несколько дней? Спасибо за любую помощь.

Ответы [ 5 ]

3 голосов
/ 17 февраля 2009

Допустим, у вас есть таблица Photo и таблица Word, и у вас есть отношение многие ко многим, используя дополнительную таблицу WordToPhoto:

DECLARE @Photo TABLE
(ID INT, Name VARCHAR(20), Title VARCHAR(50), Description VARCHAR(200))
INSERT INTO @Photo 
SELECT 1, 'mountain.jpg', 'Mountain trip', 'Mountain trip'
UNION
SELECT 2, 'beach.jpg', 'On the beach', 'On the beach'
UNION
SELECT 3, 'garden.jpg', 'Garden', 'Garden'

DECLARE @Word TABLE(ID INT, Value VARCHAR(20))
INSERT INTO @Word
SELECT 1, 'dog'
UNION
SELECT 2, 'flowers'
UNION
SELECT 3, 'sea'
UNION
SELECT 4, 'moon'
UNION
SELECT 5, 'mountain'
UNION
SELECT 6, 'seashell'
UNION
SELECT 7, 'shell'
UNION
SELECT 8, 'concert'

DECLARE @WordToPhoto TABLE(ID INT, PhotoID INT, WordID INT)
INSERT INTO @WordToPhoto
SELECT 1, 1, 2
UNION
SELECT 2, 1, 4
UNION
SELECT 3, 2, 1
UNION
SELECT 4, 2, 3
UNION
SELECT 5, 2, 6
UNION
SELECT 6, 3, 1
UNION
SELECT 7, 3, 2

И вы делаете поисковый запрос для ключевых слов "собачка" и "цветок":

DECLARE @Words TABLE (Word VARCHAR(20))
INSERT INTO @Words
SELECT 'doggy' UNION
SELECT 'flower'

Если у вас есть поиск с несколькими ключевыми словами, то вам может понадобиться система рейтинга. Я предлагаю суммировать баллы за каждую фотографию. Точка будет рассчитываться с использованием этого алгоритма:


If Photo Keyword is the same as Search Keyword then 1 
(dog = dog)
Else If Photo Keyword is a Search Keyword with postfix then 0.75 
(dog LIKE doggy)
Else If Search Keyword is a Photo Keyword with postfix then 0.75 
(doggy LIKE dog) 
Else If Photo Keyword is a Search Keyword with prefix then 0.5 
(dog LIKE bulldog) 
Else If Search Keyword is a Photo Keyword with prefix then 0.5 
(hotdog LIKE dog) 
Else If Photo Keyword is a Search Keyword with prefix and postfix then 0.25 
(dog LIKE Snoopdogg) 
Else If Search Keyword is a Photo Keyword with prefix and postfix then 0.25 
(overdogged LIKE dog) 

Точечные коэффициенты могут быть другими, это всего лишь пример.

Реализация:

SELECT R.Rating, P.* FROM @Photo P
INNER JOIN
(
    SELECT PhotoID, SUM(W.Point) AS Rating 
    FROM @WordToPhoto WTP 
    INNER JOIN (
        SELECT W.ID, 
        CASE 
            WHEN (LOWER(WS.Word) = LOWER(W.Value)) THEN 1 
            WHEN (LOWER(WS.Word) LIKE LOWER(W.Value)+'%') 
            OR (LOWER(W.Value) LIKE LOWER(WS.Word)+'%') THEN 0.75 
            WHEN (LOWER(WS.Word) LIKE '%'+LOWER(W.Value)) 
            OR (LOWER(W.Value) LIKE '%'+LOWER(WS.Word))  THEN 0.5
            ELSE 0.25
            END AS Point
        FROM @Word W
        INNER JOIN @Words WS ON LOWER(WS.Word) LIKE '%'+LOWER(W.Value)+'%' 
                OR LOWER(W.Value) LIKE '%'+LOWER(WS.Word)+'%'
    ) AS W ON W.ID = WTP.WordID
    GROUP BY PhotoID
) AS R ON P.ID = R.PhotoID

ORDER BY R.Rating DESC

Результат:

Rating ID      Name            Title           Description
1.50   3       garden.jpg      Garden          Garden
0.75   1       mountain.jpg    Mountain trip   Mountain trip
0.75   2       beach.jpg       On the beach    On the beach
1 голос
/ 20 февраля 2009

Я сделал это на своем сайте несколько лет назад. То, что я сделал, это сбрило все то, что SQL не подходит для приложения. По памяти это было что-то вроде:

table photos (
    photoid        number unique indexed,
    name           varchar2,
    title          varchar2,
    description    varchar2,
    keywords       varchar2,
    ... etc
);

table photosearch (
    wordid      number indexed,  -- ID of word, more or less
    photoid     number,          -- ref photos.photoid
    context     number,          -- 9=title, 7=name, 5=desc, ..
    ... etc 
)

Когда фотография была вставлена ​​/ обновлена, основной алгоритм был:

photoid = INSERT INTO PHOTOS VALUES (...)

foreach field in (name title description keywords) 
    int weight = getweight(field)
    foreach word in ( value(field) ) 
        # Discard useless words, e.g. "and, or, but, yes, ..."
        stem = word-stem-algorithm(word)
        key  = hash-to-number(stem)
        INSERT INTO PHOTOSEARCH VALUES 
            (key, photoid, weight)

Общий поиск тогда был примерно таким:

keys [] = hash(stem(word)) foreach word in query

SELECT photoid, sum(context) FROM photosearch
 WHERE wordid IN keys[]
 GROUP BY photoid
 ORDER BY 2 DESC

Уловка использования контекста == unique_weight позволила мне легко выполнять поиск «поле содержит слово» (оставлено как упражнение для читателя;) и позволила мне «настроить» порядок результатов, изменяя вес полей. 1010 *

1 голос
/ 14 февраля 2009

Для Postgres или MySQL вы можете проверить Sphinx для полнотекстового поиска на

http://www.sphinxsearch.com/

Есть хорошие адаптеры / плагины для различных веб-фреймворков. Например, ThinkingSphinx отлично работает в Ruby on Rails

http://github.com/freelancing-god/thinking-sphinx

Sphinx поддерживает полнотекстовый поиск по полям по вашему выбору, дельта-индексация и хорошо масштабируется.

1 голос
/ 14 февраля 2009

Вам нужно решить, как связаны несколько ключевых слов. Если в поиске кто-то вводит ключевое слово «ключевое слово1», он ищет оба ключевых слова, которые будут связаны с одной и той же фотографией (операция «И»), или ищет ключевое слово (или оба), которые будут связаны с одной и той же фотографией (ИЛИ операция). Как насчет предоставления обоих? А как насчет "этого ключевого слова, но не другого ключевого слова" и т. Д ...

Мне неясно, что предоставляет столбец WordID - кроме затрат дискового пространства. Если у вас была таблица с «WordID, Word» в качестве столбцов, а таблица перекрестных ссылок имела столбцы «PhotoID, WordID», это делает один разумный дизайн. Другой разумный дизайн имеет «PhotoID, Word». Наличие таблицы с «WordID, PhotoID, Word» не особенно разумно; это будет работать, но столбец WordID практически не используется. Вам понадобится уникальное ограничение на комбинацию PhotoID и Word, чтобы избежать повторения в этой таблице.

Учитывая вашу временную таблицу @Words, вы можете сделать это, чтобы получить опцию AND:

SELECT P.PhotoID, P.Name, P.Title, P.Description
    FROM Photo P, Word2Photo W
    WHERE P.PhotoID = W.PhotoID
    GROUP BY P.PhotoID, P.Name, P.Title, P.Description
    HAVING COUNT(*) = (SELECT COUNT(*) FROM @Words L, Word2Photo M
                           WHERE M.Word = L.Word
                             AND M.PhotoID = P.PhotoID
                      )

Это гарантирует, что количество записей в таблице Word2Photo совпадает с количеством записей в таблице @Words для данной фотографии. Это коррелированный подзапрос; это не эффективно, но эффективно. Полезно то, что структуру можно повторить в основном для параметра ИЛИ:

SELECT P.PhotoID, P.Name, P.Title, P.Description
    FROM Photo P, Word2Photo W
    WHERE P.PhotoID = W.PhotoID
    GROUP BY P.PhotoID, P.Name, P.Title, P.Description
    HAVING 1 <= (SELECT COUNT(*) FROM @Words L, Word2Photo M
                    WHERE M.Word = L.Word
                      AND M.PhotoID = P.PhotoID
                )

Это ищет фотографии, имеющие хотя бы одно из слов в списке слов.

Возможно, есть другие способы сделать это, но симметрия привлекательна. Очевидно, что если вы попадаете в более сложные критерии (смешивая AND и OR или добавляя NOT), структура меняется.

Протест

Не проверенный код.

0 голосов
/ 14 февраля 2009

Не совсем понятно, что вы имеете в виду, но звучит так, как будто вы просто хотите:

SELECT /* some columns */
FROM @Words #w
INNER JOIN WORD2PHOTO wp ON wp.Word = #w.Word
INNER JOIN PHOTO p NO p.PhotoID = wp.PhotoID

переименование и описание; ну, вы можете сделать что-то нехорошее, включая LIKE, но в качестве альтернативы, почему бы вам просто не разобрать заголовок и описание (разделить на пробелы / знаки пунктуации) и поместить их в таблицу WORD2PHOTO (с маркером, чтобы они из заголовка / описания) - тогда это становится:

SELECT /* some columns */
FROM @Words #w
INNER JOIN WORD2PHOTO wp
   ON wp.Word = #w.Word
   AND wp.Source IN ('K','T','D') -- keywords/title/description
INNER JOIN PHOTO p NO p.PhotoID = wp.PhotoID

И просто включите различные комбинации K / T / D, чтобы удовлетворить ...

Вам просто нужен триггер, чтобы при УСТАНОВКЕ / ОБНОВЛЕНИИ заголовка / описания он удалял все существующие записи T / D и заменял новые.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...