写我自己的全文

时间:2013-01-30 21:12:48

标签: postgresql plpgsql

我一直在玩全文的想法已经有一段时间了,尽管我已经完成了所有的阅读和实验,但我仍然觉得我有很多东西需要了解它的优点/缺点和一般实用性。 / p>

对于很多东西而言,它看起来效果非常好,但是我最近遇到的一个主要问题是,当谈到真正的人名时 - 简单地说,当涉及到撇号,特殊字符时,它拒绝合作,口音等。 所以我咬紧牙关,决定尝试编写自己的触发函数来更新我想索引的“搜索”字段。

首先问题在于为Bjorn之类的名字进行搜索,当你还有一个Bjørn或Björn时,那么 - 或者O'Leary,全文将这个词分成两部分。如果有人在没有撇号的情况下键入名称怎么办?等等。

为了解决这个问题,我目前的努力如下:

CREATE OR REPLACE FUNCTION quote_insert() RETURNS TRIGGER AS $$
DECLARE
  --Set to lowercase and convert any strange vowels or accents to ascii (no Bjørn's, José's etc)
  myString text := to_ascii(convert_to(lower(NEW."name"),'latin1'),'latin1');
  nwString text := '';
  --Split the words up by their spaces and put them in an array
  myArray  text[] := regexp_split_to_array(myString, E'\\s+');
  nArray   text[];
    --These are counters for the secondary array, and for the apostrophe placement
    n int := 0;
    p int;
BEGIN
  FOR i IN 1..array_upper(myArray,1) LOOP
    --If it has an apostrophe (hopefully only one!) split the words up like so: o'leary -> oleary o leary
    IF position('''' in myArray[i]) > 0 
    THEN
      nArray[n] = '''' || regexp_replace(myArray[i], '''', '') || ''':' || n+1 || ' ';
            n = n+1;
      p = position('''' in myArray[i]);
      nArray[n] = '''' || substring(myArray[i],0,p) || ''':' || n+1 || ' ';
      n = n+1;
      nArray[n] = '''' || substring(myArray[i],p+1,length(myArray[i])) || ''':' || n+1 || ' ';
      n = n+1;
    ELSE
      nArray[n] = '''' || myArray[i] || ''':' || n+1 || ' ';
      n = n+1;
    END IF;
  END LOOP;

  --Sort 'nArray' here..........
  --(not even sure if sorting alphabetically is important...)
  --............................

  --Turn the array back into a string
  FOR i IN 0..array_upper(nArray,1) LOOP
    nwString = nwString || nArray[i];
  END LOOP;
  --Set the search field to whatever string is generated
  NEW."search" = trim(nwString);
  RETURN NEW;
END
$$ LANGUAGE 'plpgsql';

我差点忘了,我之前添加了一个非常方便的自定义to_ascii脚本,我从另一个站点提取 -

CREATE FUNCTION to_ascii(bytea, name)
RETURNS text STRICT AS 'to_ascii_encname' LANGUAGE internal;

所以,我的脚本触发器产生类似于全文的文本,唯一的区别是我注意到它是按字母顺序排列的。我不知道是否有必要,或者这样做是否有效,但如果我不得不接受有关分类的建议,那将是受欢迎的。

要比较此脚本的结果和全文,请阅读:

My query (firing trigger function):
INSERT INTO "table" ("name") VALUES ('Bjørn O''Leary') RETURNING "search";
[search]
'bjorn':1 'oleary':2 'o':3 'leary':4

Using fulltext:
SELECT to_tsvector('Bjørn & O''Leary & OLeary'); --Obviously using o'leary and oleary because of different sorting methods
[to_tsvector]
'bjørn':1 'leari':3 'o':2 'oleari':4

如您所见,它不会删除特殊的ø字符,显然我需要插入OLeary以获得类似的结果。

所以,提出问题:按字母顺序排列单词会更好/更必要/更快吗?

其次......我现在不想问这个问题,但是有没有一种全文查询名字的方法,这些名字跟随我的critera,我只是不知道?

最后,真正令人头疼的是,我的名字分布在从单个表继承的几个表中。我可以考虑在此触发器中添加插入/更新,以更新完全独立的“搜索”表。这是推动它太远吗?

1 个答案:

答案 0 :(得分:1)

非常有趣!我一直在写一篇关于使用PostgreSQL进行名称搜索的博客文章,试图解决名称搜索中涉及的一些问题 但是,我解决问题的半解决方案可能对您有用。让我们从以下技巧开始:

  1. 如果搜索者至少输入两个名字,很可能其中一个很好(除非他们正在搜索BjørnO'Leary)其中一个将“匹配”您的全文索引。之后,您可以使用诸如Levenshtein距离或其他弦距离等工具以及大量狡猾的工作来正确订购结果。这需要您更改搜索单词的搜索,即Bjørn| O'Leary ,这可能导致数据库提取更多行以进行进一步处理,因此请注意..

  2. 不仅如此,您还可以解决Bjørn案例:使用unaccent过滤词典!它将“转换”为“Bjorn”,并通过删除重音仍然解决了许多其他常见的名称拼写错误!这是您的触发器尝试修复的部分内容。

  3. 如果这对您来说还不够,您当然可以使用同义词词典来处理特殊情况,方法是为 Schwarzenegger 插入一些同义词(如< em> Schwarznegger 和 Schwarzeneger )以及一些预处理来处理解析器是你的主要痛苦来源的情况,例如 O'Leary (预处理将用于示例从搜索字符串中删除撇号,就像触发器一样)。我确实认为同义词词典很快就会成为维护的痛苦来源,但即使是小词也可能对名称搜索有很大的帮助。

    • 注意:如果您不想要同义词词典但是如果您实现了我在(1)中建议的内容,如果用户搜索“Arnold Schwarznegr”(完全错误),仍然可能会发现“阿诺德施瓦辛格”非常接近排名结果的顶部,因为“施瓦辛格”与“施瓦辛格”之间的Levenshtein距离非常小,阿诺德是一个全文匹配!
  4. 希望它有所帮助。

    编辑:一些可能有用的链接:
    unaccent filtering dictionary
    Fuzzystrmatch
    pg_trgm module