【问题标题】:How to do a multiple keyword search?如何进行多关键字搜索?
【发布时间】:2010-10-07 16:02:53
【问题描述】:

我有 2 个表要搜索。搜索照片的关键字、标题和描述。 关键字已被拆分到单独的表中。我的高级搜索将允许搜索所有 3 个,但基本的只是关键字表。

基本表设置:

照片表

  • 照片ID
  • 姓名
  • 标题
  • 说明

WORD2PHOTO 表格

  • WordID
  • 照片ID

一直在尝试视图和存储过程,但没有得到任何好的结果。我从视图中获取我的照片,但在多个记录中。我会在应用程序端进行过滤和那些东西,但我使用的是亚音速并且想使用内置的分页;这完全是另一个问题。

首先,如何搜索多个关键字? 二、如何在标题和描述上添加搜索?

我有一个函数 (f_Split),它将返回我当前要搜索的单词的临时表,

DECLARE @Words TABLE (Word varchar(20))

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

现在我如何使用它生成的表格来获取照片记录? 这几天一直在苦苦挣扎? 感谢您的帮助。

【问题讨论】:

  • 客户端语言是什么?正在使用该 SQL 表的程序/网页? ASP.NET? C#? php? Java 的红宝石? ...所以我们可以更好地帮助您,而不是仅帮助您使用 Sql。
  • 他提到亚音速,所以是.net
  • C# 编写的 Asp.Net 3.5 应用程序。使用亚音速。在 Sql 2005 数据库上。抱歉应该把它放在那里。

标签: sql loops while-loop


【解决方案1】:

假设您有表格 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 在沙滩上 在沙滩上

【讨论】:

    【解决方案2】:

    对于 Postgres 或 MySQL,您可以查看 Sphinx 在

    进行全文搜索

    http://www.sphinxsearch.com/

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

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

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

    【讨论】:

      【解决方案3】:

      您需要决定如何连接多个关键字。如果有人在搜索中键入“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),那么结构就会发生变化。

      警告

      未经测试的代码。

      【讨论】:

        【解决方案4】:

        几年前我在我的网站上做过这个。我所做的就是将 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 的技巧让我可以轻松地进行“字段包含单词”搜索(留给读者作为练习;),并允许我通过改变字段的权重来“调整”结果顺序。

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

          • 将标题和描述分开听起来确实是个好主意。一个侧面问题。如果我确实将所有单词都放在 Word2Photo 表中,那对性能的影响会有多糟糕?那最终会在那个表中出现很多单词吗?
          • 好吧,你可以过滤掉一些词(“the”、“a”等)。但是数据库在这种事情上擅长...
          • 重新计算字数 - 它仍然几乎与仅存储描述+标题两次(有点额外开销)相同。鉴于标准化它的优势,我认为值得一试
          猜你喜欢
          • 2021-08-23
          • 2015-12-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多