国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 數(shù)據(jù)庫(kù) > PostgreSQL > 正文

詳細(xì)講解PostgreSQL中的全文搜索的用法

2020-10-29 21:49:50
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

開(kāi)發(fā)Web應(yīng)用時(shí),你經(jīng)常要加上搜索功能。甚至還不知能要搜什么,就在草圖上畫(huà)了一個(gè)放大鏡。

搜索是項(xiàng)非常重要的功能,所以像elasticsearch和SOLR這樣的基于lucene的工具變得很流行。它們都很棒。但使用這些大規(guī)模“殺傷性”的搜索武器前,你可能需要來(lái)點(diǎn)輕量級(jí)的,但又足夠好的搜索工具。

所謂“足夠好”,我是指一個(gè)搜索引擎擁有下列的功能:

  •     詞根(Stemming)
  •     排名/提升(Ranking / Boost)
  •     支持多種語(yǔ)言
  •     對(duì)拼寫(xiě)錯(cuò)誤模糊搜索
  •     方言的支持

幸運(yùn)的是PostgreSQL對(duì)這些功能全支持。


本文的目標(biāo)讀者是:

  •     使用PostgreSQL,同時(shí)又不想安裝其它的搜索引擎。
  •     使用其它的數(shù)據(jù)庫(kù)(比如MySQL),同時(shí)需要更好的全文搜索功能。

本文中我們將通過(guò)下面的表和數(shù)據(jù)說(shuō)明PostgreSQL的全文搜索功能。

 

CREATE TABLE author( id SERIAL PRIMARY KEY, name TEXT NOT NULL);CREATE TABLE post( id SERIAL PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL, author_id INT NOT NULL references author(id) );CREATE TABLE tag( id SERIAL PRIMARY KEY, name TEXT NOT NULL );CREATE TABLE posts_tags( post_id INT NOT NULL references post(id), tag_id INT NOT NULL references tag(id) );INSERT INTO author (id, name) VALUES (1, 'Pete Graham'),   (2, 'Rachid Belaid'),   (3, 'Robert Berry'); INSERT INTO tag (id, name) VALUES (1, 'scifi'),   (2, 'politics'),   (3, 'science'); INSERT INTO post (id, title, content, author_id) VALUES (1, 'Endangered species', 'Pandas are an endangered species', 1 ),   (2, 'Freedom of Speech', 'Freedom of speech is a necessary right missing in many countries', 2),   (3, 'Star Wars vs Star Trek', 'Few words from a big fan', 3); INSERT INTO posts_tags (post_id, tag_id) VALUES (1, 3),   (2, 2),   (3, 1);

這是一個(gè)類(lèi)博客的應(yīng)用。它有post表,帶有title和content字段。post通過(guò)外鍵關(guān)聯(lián)到author。post自身還有多個(gè)標(biāo)簽(tag)。

什么是全文搜索

首先,讓我們看一下定義:

    在文本檢索中,全文搜索是指從全文數(shù)據(jù)庫(kù)中搜索計(jì)算機(jī)存儲(chǔ)的單個(gè)或多個(gè)文檔(document)的技術(shù)。全文搜索不同于基于元數(shù)據(jù)的搜索或根據(jù)數(shù)據(jù)庫(kù)中原始文本的搜索。

    -- 維基百科

這個(gè)定義中引入了文檔的概念,這很重要。當(dāng)你搜索數(shù)據(jù)時(shí),你在尋找你想要找到的有意義的實(shí)體,這些就是你的文檔。PostgreSQL的文檔中解釋地很好。

    文檔是全文搜索系統(tǒng)中的搜索單元。比如,一篇雜質(zhì)文章或是一封郵件消息。

    -- Postgres 文檔

這里的文檔可以跨多個(gè)表,代表為我們想要搜索的邏輯實(shí)體。
 
構(gòu)建我們的文檔(document)

上一節(jié),我們介紹了文檔的概念。文檔與表的模式無(wú)關(guān),而是與數(shù)據(jù)相關(guān),把字段聯(lián)合為一個(gè)有意義的實(shí)體。根據(jù)示例中的表的模式,我們的文檔(document)由這些組成:

  •     post.title
  •     post.content
  •     post的author.name
  •     關(guān)聯(lián)到post的所有tag.name

根據(jù)這些要求產(chǎn)生文檔,SQL查詢(xún)應(yīng)該是這樣的:
 
 

SELECT post.title || ' ' ||   post.content || ' ' ||  author.name || ' ' ||  coalesce((string_agg(tag.name, ' ')), '') as document FROM post JOIN author ON author.id = post.author_id JOIN posts_tags ON posts_tags.post_id = posts_tags.tag_id JOIN tag ON tag.id = posts_tags.tag_id GROUP BY post.id, author.id;     document -------------------------------------------------- Endangered species Pandas are an endangered species Pete Graham politics Freedom of Speech Freedom of speech is a necessary right missing in many countries Rachid Belaid politics Star Wars vs Star Trek Few words from a big fan Robert Berry politics(3 rows)

由于用post和author分組了,因?yàn)橛卸鄠€(gè)tag關(guān)聯(lián)到一個(gè)post,我們使用string_agg()作聚合函數(shù)。即使author是外鍵并且一個(gè)post不能有多個(gè)author,也要求對(duì)author添加聚合函數(shù)或者把a(bǔ)uthor加到GROUP BY中。

我們還用了coalesce()。當(dāng)值可以是NULL時(shí),使用coalesce()函數(shù)是個(gè)很好的辦法,否則字符串連接的結(jié)果將是NULL。


至此,我們的文檔只是一個(gè)長(zhǎng)string,這沒(méi)什么用。我們需要用to_tsvector()把它轉(zhuǎn)換為正確的格式。
 

SELECT to_tsvector(post.title) ||   to_tsvector(post.content) ||  to_tsvector(author.name) ||  to_tsvector(coalesce((string_agg(tag.name, ' ')), '')) as documentFROM postJOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON tag.id = posts_tags.tag_idGROUP BY post.id, author.id;    document -------------------------------------------------- 'endang':1,6 'graham':9 'panda':3 'pete':8 'polit':10 'speci':2,7'belaid':16 'countri':14 'freedom':1,4 'mani':13 'miss':11 'necessari':9 'polit':17 'rachid':15 'right':10 'speech':3,6'berri':13 'big':10 'fan':11 'polit':14 'robert':12 'star':1,4 'trek':5 'vs':3 'war':2 'word':7(3 rows)

這個(gè)查詢(xún)將返回適于全文搜索的tsvector格式的文檔。讓我們嘗試把一個(gè)字符串轉(zhuǎn)換為一個(gè)tsvector。
 

SELECT to_tsvector('Try not to become a man of success, but rather try to become a man of value');

這個(gè)查詢(xún)將返回下面的結(jié)果:
 

        to_tsvector----------------------------------------------------------------------'becom':4,13 'man':6,15 'rather':10 'success':8 'tri':1,11 'valu':17(1 row)

發(fā)生了怪事。首先比原文的詞少了,一些詞也變了(try變成了tri),而且后面還有數(shù)字。怎么回事?

一個(gè)tsvector是一個(gè)標(biāo)準(zhǔn)詞位的有序列表(sorted list),標(biāo)準(zhǔn)詞位(distinct lexeme)就是說(shuō)把同一單詞的各種變型體都被標(biāo)準(zhǔn)化相同的。


標(biāo)準(zhǔn)化過(guò)程幾乎總是把大寫(xiě)字母換成小寫(xiě)的,也經(jīng)常移除后綴(比如英語(yǔ)中的s,es和ing等)。這樣可以搜索同一個(gè)字的各種變體,而不是乏味地輸入所有可能的變體。

數(shù)字表示詞位在原始字符串中的位置,比如“man"出現(xiàn)在第6和15的位置上。你可以自己數(shù)數(shù)看。

Postgres中to_tesvetor的默認(rèn)配置的文本搜索是“英語(yǔ)“。它會(huì)忽略掉英語(yǔ)中的停用詞(stopword,譯注:也就是am is are a an等單詞)。

這解釋了為什么tsvetor的結(jié)果比原句子中的單詞少。后面我們會(huì)看到更多的語(yǔ)言和文本搜索配置。

查詢(xún)

我們知道了如何構(gòu)建一個(gè)文檔,但我們的目標(biāo)是搜索文檔。我們對(duì)tsvector搜索時(shí)可以使用@@操作符,使用說(shuō)明見(jiàn)此處。看幾個(gè)查詢(xún)文檔的例子。
 

> select to_tsvector('If you can dream it, you can do it') @@ 'dream'; ?column?---------- t(1 row) > select to_tsvector('It''s kind of fun to do the impossible') @@ 'impossible';  ?column?---------- f(1 row)

第二個(gè)查詢(xún)返回了假,因?yàn)槲覀冃枰獦?gòu)建一個(gè)tsquery,使用@@操作符時(shí),把字符串轉(zhuǎn)型(cast)成了tsquery。下面顯示了這種l轉(zhuǎn)型和使用to_tsquery()之間的差別。
 

SELECT 'impossible'::tsquery, to_tsquery('impossible'); tsquery | to_tsquery--------------+------------ 'impossible' | 'imposs'(1 row)

但"dream"的詞位與它本身相同。
 

SELECT 'dream'::tsquery, to_tsquery('dream'); tsquery | to_tsquery--------------+------------ 'dream'  | 'dream'(1 row)

從現(xiàn)在開(kāi)始我們使用to_tsquery查詢(xún)文檔。

 SELECT to_tsvector('It''s kind of fun to do the impossible') @@ to_tsquery('impossible');  ?column?---------- t(1 row)

tsquery存儲(chǔ)了要搜索的詞位,可以使用&(與)、|(或)和!(非)邏輯操作符。可以使用圓括號(hào)給操作符分組。
 

> SELECT to_tsvector('If the facts don't fit the theory, change the facts') @@ to_tsquery('! fact');  ?column?---------- f(1 row) > SELECT to_tsvector('If the facts don''t fit the theory, change the facts') @@ to_tsquery('theory & !fact');  ?column?---------- f(1 row) > SELECT to_tsvector('If the facts don''t fit the theory, change the facts.') @@ to_tsquery('fiction | theory');  ?column?---------- t(1 row)

我們也可以使用:*來(lái)表達(dá)以某詞開(kāi)始的查詢(xún)。
 

> SELECT to_tsvector('If the facts don''t fit the theory, change the facts.') @@ to_tsquery('theo:*');  ?column?---------- t(1 row)

既然我們知道了怎樣使用全文搜索查詢(xún)了,我們回到開(kāi)始的表模式,試著查詢(xún)文檔。
 

SELECT pid, p_titleFROM (SELECT post.id as pid,    post.title as p_title,    to_tsvector(post.title) ||     to_tsvector(post.content) ||    to_tsvector(author.name) ||    to_tsvector(coalesce(string_agg(tag.name, ' '))) as document  FROM post  JOIN author ON author.id = post.author_id  JOIN posts_tags ON posts_tags.post_id = posts_tags.tag_id  JOIN tag ON tag.id = posts_tags.tag_id  GROUP BY post.id, author.id) p_search WHERE p_search.document @@ to_tsquery('Endangered & Species');  pid |  p_title-----+-------------------- 1 | Endangered species(1 row)

這個(gè)查詢(xún)將找到文檔中包含Endangered和Species或接近的詞。

語(yǔ)言支持

Postgres 內(nèi)置的文本搜索功能支持多種語(yǔ)言: 丹麥語(yǔ),荷蘭語(yǔ),英語(yǔ),芬蘭語(yǔ),法語(yǔ),德語(yǔ),匈牙利語(yǔ),意大利語(yǔ),挪威語(yǔ),葡萄牙語(yǔ),羅馬尼亞語(yǔ),俄語(yǔ),西班牙語(yǔ),瑞典語(yǔ),土耳其語(yǔ)。

 

SELECT to_tsvector('english', 'We are running'); to_tsvector------------- 'run':3(1 row)SELECT to_tsvector('french', 'We are running');  to_tsvector---------------------------- 'are':2 'running':3 'we':1(1 row)

基于我們最初的模型,列名可以用來(lái)創(chuàng)建tsvector。 假設(shè)post表中包含不同語(yǔ)言的內(nèi)容,且它包含一列l(wèi)anguage。

 
ALTER TABLE post ADD language text NOT NULL DEFAULT('english');

為了使用language列,現(xiàn)在我們重新編譯文檔。
 

SELECT to_tsvector(post.language::regconfig, post.title) ||   to_tsvector(post.language::regconfig, post.content) ||  to_tsvector('simple', author.name) ||  to_tsvector('simple', coalesce((string_agg(tag.name, ' ')), '')) as documentFROM postJOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON tag.id = posts_tags.tag_idGROUP BY post.id, author.id;

如果缺少顯示的轉(zhuǎn)化符::regconfig,查詢(xún)時(shí)會(huì)產(chǎn)生一個(gè)錯(cuò)誤:
 

ERROR: function to_tsvector(text, text) does not exist

regconfig是對(duì)象標(biāo)識(shí)符類(lèi)型,它表示Postgres文本搜索配置項(xiàng)。:http://www.postgresql.org/docs/9.3/static/datatype-oid.html

現(xiàn)在,文檔的語(yǔ)義會(huì)使用post.language中正確的語(yǔ)言進(jìn)行編譯。

我們也使用simple,它也是Postgres提供的一個(gè)文本搜索配置項(xiàng)。simple并不忽略禁用詞表,它也不會(huì)試著去查找單詞的詞根。使用simple時(shí),空格分割的每一組字符都是一個(gè)語(yǔ)義;對(duì)于數(shù)據(jù)來(lái)說(shuō),simple文本搜索配置項(xiàng)很實(shí)用,就像一個(gè)人的名字,我們也許不想查找名字的詞根。 
 

SELECT to_tsvector('simple', 'We are running');  to_tsvector---------------------------- 'are':2 'running':3 'we':1(1 row)


重音字符

當(dāng)你建立一個(gè)搜索引擎支持多種語(yǔ)言時(shí)你也需要考慮重音問(wèn)題。在許多語(yǔ)言中重音非常重要,可以改變這個(gè)詞的含義。Postgres附帶一個(gè)unaccent擴(kuò)展去調(diào)用 unaccentuate內(nèi)容是有用處的。
 

CREATE EXTENSION unaccent;SELECT unaccent('èéê?'); unaccent---------- eeee(1 row)


讓我們添加一些重音的你內(nèi)容到我們的post表中。
 

INSERT INTO post (id, title, content, author_id, language) VALUES (4, 'il était une fois', 'il était une fois un h?tel ...', 2,'french')

如果我們想要忽略重音在我們建立文檔時(shí),之后我們可以簡(jiǎn)單做到以下幾點(diǎn):
 

SELECT to_tsvector(post.language, unaccent(post.title)) ||   to_tsvector(post.language, unaccent(post.content)) ||  to_tsvector('simple', unaccent(author.name)) ||  to_tsvector('simple', unaccent(coalesce(string_agg(tag.name, ' '))))JOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON author.id = post.author_idGROUP BY p.id

這樣工作的話(huà),如果有更多錯(cuò)誤的空間它就有點(diǎn)麻煩。 我們還可以建立一個(gè)新的文本搜索配置支持無(wú)重音的字符。
 

CREATE TEXT SEARCH CONFIGURATION fr ( COPY = french );ALTER TEXT SEARCH CONFIGURATION fr ALTER MAPPINGFOR hword, hword_part, word WITH unaccent, french_stem;


當(dāng)我們使用這個(gè)新的文本搜索配置,我們可以看到詞位

 

SELECT to_tsvector('french', 'il était une fois'); to_tsvector------------- 'fois':4(1 row)SELECT to_tsvector('fr', 'il était une fois'); to_tsvector-------------------- 'etait':2 'fois':4(1 row)

這給了我們相同的結(jié)果,第一作為應(yīng)用unaccent并且從結(jié)果建立tsvector。
 

SELECT to_tsvector('french', unaccent('il était une fois')); to_tsvector-------------------- 'etait':2 'fois':4(1 row)

詞位的數(shù)量是不同的,因?yàn)閕l était une在法國(guó)是一個(gè)無(wú)用詞。這是一個(gè)問(wèn)題讓這些詞停止在我們的文件嗎?我不這么認(rèn)為etait不是一個(gè)真正的無(wú)用詞而是拼寫(xiě)錯(cuò)誤。
 

SELECT to_tsvector('fr', 'H?tel') @@ to_tsquery('hotels') as result; result-------- t(1 row)


如果我們?yōu)槊糠N語(yǔ)言創(chuàng)建一個(gè)無(wú)重音的搜索配置,這樣我們的post可以寫(xiě)入并且我們保持這個(gè)值在post.language的中,然后我們可以保持以前的文檔查詢(xún)。
 

SELECT to_tsvector(post.language, post.title) ||   to_tsvector(post.language, post.content) ||  to_tsvector('simple', author.name) ||  to_tsvector('simple', coalesce(string_agg(tag.name, ' ')))JOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON author.id = post.author_idGROUP BY p.id

如果你需要為每種語(yǔ)言創(chuàng)建無(wú)重音的文本搜索配置由Postgres支持,然后你可以使用gist

我們當(dāng)前的文檔大小可能會(huì)增加,因?yàn)樗梢园o(wú)重音的無(wú)用詞但是我們并沒(méi)有關(guān)注重音字符查詢(xún)。這可能是有用的如有人用英語(yǔ)鍵盤(pán)搜索法語(yǔ)內(nèi)容。


歸類(lèi)

當(dāng)你創(chuàng)建了一個(gè)你想要的搜索引擎用來(lái)搜索相關(guān)的結(jié)果(根據(jù)相關(guān)性歸類(lèi))的時(shí)候,歸類(lèi)可以是基于許多因素的,它的文檔大致解釋了這些(歸類(lèi)依據(jù))內(nèi)容。

    歸類(lèi)試圖處理特定的上下文搜索, 因此有許多個(gè)配對(duì)的時(shí)候,相關(guān)性最高的那個(gè)會(huì)被排在第一個(gè)位置。PostgreSQL提供了兩個(gè)預(yù)定義歸類(lèi)函數(shù),它們考慮到了詞法解釋?zhuān)咏群徒Y(jié)構(gòu)信息;他們考慮到了在上下文中的詞頻,如何接近上下文中的相同詞語(yǔ),以及在文中的什么位置出現(xiàn)和其重要程度。

    -- PostgreSQL documentation

通過(guò)PostgreSQL提供的一些函數(shù)得到我們想要的相關(guān)性結(jié)果,在我們的例子中我們將會(huì)使用他們中的2個(gè):ts_rank() 和 setweight() 。

函數(shù)setweight允許我們通過(guò)tsvector函數(shù)給重要程度(權(quán))賦值;值可以是'A', 'B', 'C' 或者 'D'。
 

SELECT pid, p_titleFROM (SELECT post.id as pid,    post.title as p_title,    setweight(to_tsvector(post.language::regconfig, post.title), 'A') ||     setweight(to_tsvector(post.language::regconfig, post.content), 'B') ||    setweight(to_tsvector('simple', author.name), 'C') ||    setweight(to_tsvector('simple', coalesce(string_agg(tag.name, ' '))), 'B') as document  FROM post  JOIN author ON author.id = post.author_id  JOIN posts_tags ON posts_tags.post_id = posts_tags.tag_id  JOIN tag ON tag.id = posts_tags.tag_id  GROUP BY post.id, author.id) p_searchWHERE p_search.document @@ to_tsquery('english', 'Endangered & Species')ORDER BY ts_rank(p_search.document, to_tsquery('english', 'Endangered & Species')) DESC;

上面的查詢(xún),我們?cè)谖闹胁煌臋诶锩尜x了不同的權(quán)值。post.title的重要程度超過(guò)post.content和tag的總和。最不重要的是author.name。


這意味著如果我們搜索關(guān)鍵詞“Alice”,那么在題目中包含這個(gè)關(guān)鍵詞的文檔就會(huì)排在搜索結(jié)果的前面,在此之后是在內(nèi)容中包含這些關(guān)鍵詞的文檔,最后才是作者名字中包含這些關(guān)鍵詞的文檔.

基于對(duì)文檔各個(gè)部分的權(quán)重分配ts_rank()這個(gè)函數(shù)返回一個(gè)浮點(diǎn)數(shù),這個(gè)浮點(diǎn)數(shù)代表了文檔和查詢(xún)關(guān)鍵詞的相關(guān)性.
 

SELECT ts_rank(to_tsvector('This is an example of document'),     to_tsquery('example | document')) as relevancy; relevancy----------- 0.0607927(1 row)SELECT ts_rank(to_tsvector('This is an example of document'),     to_tsquery('example ')) as relevancy; relevancy----------- 0.0607927(1 row)SELECT ts_rank(to_tsvector('This is an example of document'),     to_tsquery('example | unkown')) as relevancy; relevancy----------- 0.0303964(1 row)SELECT ts_rank(to_tsvector('This is an example of document'),    to_tsquery('example & document')) as relevancy; relevancy----------- 0.0985009(1 row)SELECT ts_rank(to_tsvector('This is an example of document'),     to_tsquery('example & unknown')) as relevancy; relevancy----------- 1e-20(1 row)

但是, 相關(guān)性的概念是模糊的,而且是與特定的應(yīng)用相關(guān). 不同的應(yīng)用可能需要額外的信息來(lái)得到想要的排序結(jié)果,比如,文檔的修改時(shí)間. 內(nèi)建的排序功能如asts_rank只是個(gè)例子. 你可以寫(xiě)出自己的排序函數(shù) 并且/或者 將得到的結(jié)果和其他因素混合來(lái)適應(yīng)你自己的特定需求.

這里說(shuō)明一下, 如果我們想是新的文章比舊的文章更重要,可以講ts_rank函數(shù)的數(shù)值除以文檔的年齡+1(為防止被0除).

優(yōu)化與索引

將一個(gè)表中的搜索結(jié)果優(yōu)化為直線(xiàn)前進(jìn)的. PostgreSQL 支持基于索引的功能,因此你可以用tsvector()函數(shù)方便地創(chuàng)建GIN索引.
 

CREATE INDEX idx_fts_post ON post USING gin(setweight(to_tsvector(language, title),'A') ||    setweight(to_tsvector(language, content), 'B'));

GIN還是GiST索引? 這兩個(gè)索引會(huì)成為與他們相關(guān)的博文的主題. GiST會(huì)導(dǎo)出一個(gè)錯(cuò)誤的匹配,之后需要一個(gè)額外的表行查找來(lái)驗(yàn)證得到的匹配. 另一方面, GIN 可以更快地查找但是在創(chuàng)建時(shí)會(huì)更大更慢.

    一個(gè)經(jīng)驗(yàn), GIN索引適合靜態(tài)的數(shù)據(jù)因?yàn)椴檎沂茄杆俚? 對(duì)于動(dòng)態(tài)數(shù)據(jù), GiST 可以更快的更新. 具體來(lái)說(shuō), GiST索引在動(dòng)態(tài)數(shù)據(jù)上是好用的并且如果單獨(dú)的字(詞位)在100,000以下也是快速的,然而GIN 索引在處理100,000詞位以上時(shí)是更好的但是更新就要慢點(diǎn)了.

    -- Postgres 文檔 : 第12章 全文搜索

在我們的例子中,我們選擇GIN。但是這個(gè)選擇不是一定的,你可以根據(jù)你自己的數(shù)據(jù)來(lái)作出決定。


我們的架構(gòu)例子中有一個(gè)問(wèn)題; 分當(dāng)時(shí)分布在擁有不同權(quán)重的不同表中的. 為了更好的運(yùn)行,通過(guò)觸發(fā)器和物化視圖使得數(shù)據(jù)非規(guī)范化是必要的.

我們并非總是需要非規(guī)范化并且有時(shí)也需要加入基于索引的功能,就像上面所做的那樣. 另外你可以通過(guò)postgres觸發(fā)器 功能tsvector_update_trigger(...)或者tsvector_update_trigger_column(...)實(shí)現(xiàn)相同表的數(shù)據(jù)的非規(guī)范化.參見(jiàn)Postgres文檔以得到更多詳細(xì)的信息.

在我們的應(yīng)用中在結(jié)果返回之前存在著一些可接受的延遲. 這是一個(gè)使用物化視圖將額外索引加載其中的好的情況.
 

CREATE MATERIALIZED VIEW search_index AS SELECT post.id,  post.title,  setweight(to_tsvector(post.language::regconfig, post.title), 'A') ||   setweight(to_tsvector(post.language::regconfig, post.content), 'B') ||  setweight(to_tsvector('simple', author.name), 'C') ||  setweight(to_tsvector('simple', coalesce(string_agg(tag.name, ' '))), 'A') as documentFROM postJOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON tag.id = posts_tags.tag_idGROUP BY post.id, author.id

之后重新索引搜索引擎就是定期運(yùn)行REFRESH MATERIALIZED VIEW search_index這么簡(jiǎn)單.

現(xiàn)在我們可以給物化視圖添加索引.

 
 

CREATE INDEX idx_fts_search ON search_index USING gin(document);

查詢(xún)也變得同樣簡(jiǎn)單.
 

SELECT id as post_id, titleFROM search_indexWHERE document @@ to_tsquery('english', 'Endangered & Species')ORDER BY ts_rank(p_search.document, to_tsquery('english', 'Endangered & Species')) DESC;

如果延遲變得無(wú)法忍受,你就應(yīng)該去研究一下使用觸發(fā)器的替代方法.

建立文檔存儲(chǔ)的方式并不唯一;這取決于你文檔的情況: 單表、多表,多國(guó)語(yǔ)言,數(shù)據(jù)量 ...

Thoughtbot.com 發(fā)表了文章"Implementing Multi-Table Full Text Search with Postgres in Rails" 我建議閱讀以下.
 
拼寫(xiě)錯(cuò)誤

PostgreSQL 提供了一個(gè)非常有用的擴(kuò)展程序pg_trgm。 相關(guān)文檔見(jiàn)pg_trgm doc。
 

CREATE EXTENSION pg_trgm;

pg_trgm支持N元語(yǔ)法如N==3。N元語(yǔ)法比較有用因?yàn)樗梢圆檎蚁嗨频淖址鋵?shí),這就是拼寫(xiě)錯(cuò)誤的定義

主站蜘蛛池模板: 依安县| 大余县| 沁源县| 东明县| 白玉县| 兴城市| 南丹县| 德阳市| 当雄县| 海城市| 平山县| 华池县| 澄城县| 田东县| 米林县| 城固县| 米易县| 玉门市| 祁连县| 比如县| 庆元县| 奉新县| 宣化县| 蒙山县| 丹阳市| 麟游县| 柘城县| 体育| 安图县| 五华县| 佛坪县| 邛崃市| 临清市| 渝中区| 古蔺县| 肇源县| 威信县| 宝应县| 都江堰市| 惠州市| 青川县|