在PostgreSQL

时间:2016-12-16 13:09:43

标签: sql postgresql full-text-search unique ranking

我目前正在调整我的文字搜索查询,以便为给定的搜索字词获得最佳结果。我正在寻找的是一种排名功能,它只会为搜索字段中的新唯一值提升分数。它也应该比仅仅前缀命中更好地得分。我能够通过庞大的查询获得所需的结果,但是想知道你是否可以使用秩函数实现更优雅的类似结果。我给你举个例子:

CREATE TABLE book (
  id BIGSERIAL NOT NULL PRIMARY KEY,
  title VARCHAR(255) NOT NULL
);

INSERT INTO book (title) VALUES ('Kate Mat');
INSERT INTO book (title) VALUES ('Kate Kate Mate');
INSERT INTO book (title) VALUES ('Cat Mat');

以下是我使用搜索字词'Kate'+'Mat'进行的庞大查询:

SELECT
  title,
  a1 + a2 + b1 + b2 AS score
FROM (
       SELECT
         title,
         CASE WHEN to_tsvector('english', title) @@ to_tsquery('kate:*')
           THEN 1
         ELSE 0
         END AS a1,
         CASE WHEN to_tsvector('english', title) @@ to_tsquery('kate')
           THEN 0.5
         ELSE 0
         END AS a2,
         CASE WHEN to_tsvector('english', title) @@ to_tsquery('mat:*')
           THEN 1
         ELSE 0
         END AS b1,
         CASE WHEN to_tsvector('english', title) @@ to_tsquery('mat')
           THEN 0.5
         ELSE 0
         END AS b2
       FROM book
     ) scoredProducts
ORDER BY score DESC;

#----------------------results-------------------------
title           score
Kate Mat        3        -- exact hit for both terms
Kate Kate Mate  2.5      -- exact hit for 'Kate'. prefix hit for 'Mat'
Cat Mat         1.5      -- exact hit for 'Mat'

这实际上是我想看到的结果顺序。查询的明显问题是我需要针对每个额外的搜索词进行调整。我想要一个更像这样的语法:

SELECT
  title,
  ts_rank(to_tsvector('english', book.title), to_tsquery('kate:* | mat:*')) AS score
FROM book
ORDER BY score DESC;

#----------------------results-------------------------
title           score
Kate Kate Mate  0.0683918      -- prefix hits for both terms
Kate Mat        0.06079271     -- exact hit gets scored less
Cat Mat         0.030396355

令人遗憾的是,它没有提供我想要的订单,因为它比完全独特的点击更能获得多次相同的点击。是否可以编写这样的自定义排名函数?

1 个答案:

答案 0 :(得分:0)

我不太确定我是否理解你的问题,
您可以使用一些基本的SQL关键字和聚合函数来简化查询:

SELECT
  b.title,
  sum( case when to_tsvector('english' , title ) @@ to_tsquery(a.keyword)
            then a.score end ) AS score
  FROM book b
  cross join  ( 
     values
     ( 'kate:*', 1 ),
     ( 'kate', 0.5 ),
     ( 'mat:*', 1 ),
     ( 'mat', 0.5 )
) as a(keyword, score)
group by b.title
ORDER BY score DESC; 

您仍然需要针对每个新搜索字词调整此查询,但现在看起来更容易了。