1

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

对于很多事情来说,它似乎工作得非常好,但是我最近遇到的一个主要障碍是在涉及真实的人名时 - 简单地说,它在涉及撇号、特殊字符、重音和类似。所以我硬着头皮决定尝试编写自己的触发函数来更新我希望索引的“搜索”字段。

首先,当搜索像 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 以获得类似的结果。

所以,要问问题:按字母顺序组织单词是否更好/必要/更快?

其次......我现在不想问这个问题,但是已经有一种全文方法来查询符合我的标准的名称,我只是不知道吗?

最后,真正令人头疼的是,我的名字分布在从单个表继承的多个表中。我可能会考虑向这个触发器添加一个插入/更新,以更新一个完全独立的“搜索”表。这是不是太过分了?

4

1 回答 1

1

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

  1. 如果搜索者输入了至少两个名字,很可能其中一个会很好(除非他们正在搜索Bjørn O'Leary),其中一个会“匹配”您的全文索引。之后,您可以使用诸如 Levenshtein 距离或其他字符串距离之类的工具以及许多巧妙的工作来正确排序结果。这需要您将搜索更改为对单词进行排序搜索,即Bjørn | O'Leary,这可能会导致数据库获取更多行以进行进一步处理,因此请注意..

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

  3. 如果这对你来说还不够,你当然可以使用同义词词典来处理特殊情况,通过插入一些施瓦辛格的同义词(如SchwarzneggerSchwarzeneger)和一些预处理来处理解析器是你的主要痛苦来源的情况,比如在O'Leary中(例如,预处理会从搜索字符串中删除撇号,就像您的触发器一样)。我确实认为同义词词典也可能很快成为维护痛苦的根源,但即使是一个小的词典也可能对名称搜索有很大帮助。

    • 注意:如果您不想要同义词词典,但如果您确实实现了我在(1)中建议的内容,如果用户搜索“Arnold Schwarznegr”(完全错误),他仍然可能会发现“Arnold Schwarzenegger”非常接近排名结果靠前,因为“施瓦辛格”和“施瓦辛格”之间的 Levenshtein 距离非常小,而阿诺德是全文匹配!

希望它会有所帮助。

编辑:一些可能有用的链接:
非重音过滤字典
Fuzzystrmatch
pg_trgm 模块

于 2013-01-30T23:32:20.477 回答