2

I have 2 tables to search. Searching photos for keywords, title and description. The keywords have been split off into a separate table. My advanced search will allow searching on all 3 but the basic will just be the keyword table.

Basic table setup:

PHOTO Table

  • PhotoID
  • Name
  • Title
  • Description

WORD2PHOTO Table

  • WordID
  • PhotoID
  • Word

Been trying views and stored procs, and not getting any good results. I get my pics from the views, but in multiple records. I would do filtering and that stuff on the app side, but I'm using subsonic and would like to use the built in paging; that's a whole other issue.

First, How do I search on multiple keywords? Second, how do I add in searching on the title and description?

I have a function (f_Split) that will return a temp table of my current words to search,

DECLARE @Words TABLE (Word varchar(20))

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

Now how do I use the table it generates to get the photo records? Been struggling for days now? Thanks for any help.

4

5 回答 5

3

假设您有表格 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

并且您对“doggy”和“flower”关键字进行搜索查询:

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

如果您使用多个关键字进行搜索,那么您可能需要一些评级系统。我的建议是总结每张照片的分数。点将使用此算法计算:

如果照片关键字与搜索关键字相同,则 1
(狗=狗)
否则,如果照片关键字是带有后缀的搜索关键字,则为 0.75
(狗喜欢小狗)
否则,如果搜索关键字是带有后缀的照片关键字,则为 0.75
(小狗喜欢狗)
否则,如果照片关键字是带有前缀的搜索关键字,则为 0.5
(狗喜欢斗牛犬)
否则,如果搜索关键字是带有前缀的照片关键字,则为 0.5
(热狗喜欢狗)
否则,如果照片关键字是带有前缀和后缀的搜索关键字,则为 0.25
(狗喜欢史努比狗)
否则,如果搜索关键字是带有前缀和后缀的照片关键字,则为 0.25
(过分的喜欢狗)

点系数可能是其他的,这只是一个例子。

执行:

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

结果:

评级 ID 名称 标题 描述
1.50 3 garden.jpg 花园花园
0.75 1 mountain.jpg 山地之旅 山地之旅
0.75 2 beach.jpg 在沙滩上 在沙滩上
于 2009-02-17T19:26:19.077 回答
1

对于 Postgres 或 MySQL,您可以查看 Sphinx 进行全文搜索,网址为

http://www.sphinxsearch.com/

各种 Web 框架都有很好的适配器/插件。例如 ThinkingSphinx 在 Ruby on Rails 中表现出色

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

Sphinx 支持对您选择的字段进行全文搜索、增量索引,并且可以很好地扩展。

于 2009-02-14T19:37:40.457 回答
1

您需要决定如何连接多个关键字。如果有人在搜索中键入“keyword1 keyword2”,他们是在寻找与同一张照片相关联的两个关键字(AND 操作)还是在寻找与同一张照片相关联的任一关键字(或两者)(OR手术)。两者都提供怎么样?那么“这个关键字而不是那个其他关键字”呢?等等......

我不清楚 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 表中的条目数相同。它是一个相关的子查询;它不是有效的,但它是有效的。有用的是,该结构可以重复用于 OR 选项:

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),则结构会发生变化。

警告

未经测试的代码。

于 2009-02-14T20:43:33.637 回答
1

几年前我在我的网站上做过这个。我所做的就是将 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

使用 context==unique_weight 的技巧让我可以轻松地进行“字段包含单词”搜索(留给读者练习;),并允许我通过改变字段的权重来“调整”结果顺序。

于 2009-02-20T19:57:14.470 回答
0

你的意思并不完全清楚,但听起来你只是想要:

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.标题/描述) - 然后变成:

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 条目并替换为新条目。

于 2009-02-14T19:28:14.113 回答