summaryrefslogtreecommitdiff
path: root/contrib/tsearch2/docs/tsearch-V2-intro.html
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tsearch2/docs/tsearch-V2-intro.html')
-rw-r--r--contrib/tsearch2/docs/tsearch-V2-intro.html975
1 files changed, 975 insertions, 0 deletions
diff --git a/contrib/tsearch2/docs/tsearch-V2-intro.html b/contrib/tsearch2/docs/tsearch-V2-intro.html
new file mode 100644
index 0000000000..8375d4ca77
--- /dev/null
+++ b/contrib/tsearch2/docs/tsearch-V2-intro.html
@@ -0,0 +1,975 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+<head>
+ <title>tsearch-v2-intro</title>
+<link type="text/css" rel="stylesheet" href="/~megera/postgres/gist/tsearch/tsearch.css">
+</head>
+
+<body>
+ <div class="content">
+ <h2>Tsearch2 - Introduction</h2>
+
+ <p><a href=
+ "http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/docs/tsearch-V2-intro.html">
+ [Online version]</a> of this document is available.</p>
+
+ <p>The tsearch2 module is available to add as an extension to
+ the PostgreSQL database to allow for Full Text Indexing. This
+ document is an introduction to installing, configuring, using
+ and maintaining the database with the tsearch2 module
+ activated.</p>
+
+ <p>Please, note, tsearch2 module is fully incompatible with old
+ tsearch, which is deprecated in 7.4 and will be obsoleted in
+ 7.5.</p>
+
+ <h3>USING TSEARCH2 AND POSTGRESQL FOR A WEB BASED SEARCH
+ ENGINE</h3>
+
+ <p>This documentation is provided as a short guide on how to
+ quickly get up and running with tsearch2 and PostgreSQL, for
+ those who want to implement a full text indexed based search
+ engine. It is not meant to be a complete in-depth guide into
+ the full ins and outs of the contrib/tsearch2 module, and is
+ primarily aimed at beginners who want to speed up searching of
+ large text fields, or those migrating from other database
+ systems such as MS-SQL.</p>
+
+ <p>The README.tsearch2 file included in the contrib/tsearch2
+ directory contains a brief overview and history behind tsearch.
+ This can also be found online <a href=
+ "http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/">[right
+ here]</a>.</p>
+
+ <p>Further in depth documentation such as a full function
+ reference, and user guide can be found online at the <a href=
+ "http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/docs/">[tsearch
+ documentation home]</a>.</p>
+
+ <h3>ACKNOWLEDGEMENTS</h3>
+
+ <p>Robert John Shepherd originally wrote this documentation for
+ the previous version of tsearch module (v1) included with the
+ postgres release. I took his documentation and updated it to
+ comply with the tsearch2 modifications.</p>
+
+ <p>Robert's original acknowledgements:</p>
+
+ <p>"Thanks to Oleg Bartunov for taking the time to answer many
+ of my questions regarding this module, and also to Teodor
+ Sigaev for clearing up the process of making your own
+ dictionaries. Plus of course a big thanks to the pair of them
+ for writing this module in the first place!"</p>
+
+ <p>I would also like to extend my thanks to the developers, and
+ Oleg Bartunov for all of his direction and help with the new
+ features of tsearch2.</p>
+
+ <h3>OVERVIEW</h3>
+
+ <p>MS-SQL provides a full text indexing (FTI) system which
+ enables the fast searching of text based fields, very useful
+ for websites (and other applications) that require a results
+ set based on key words. PostgreSQL ships with a contributed
+ module called tsearch2, which implements a special type of
+ index that can also be used for full text indexing. Further
+ more, unlike MS' offering which requires regular incremental
+ rebuilds of the text indexes themselves, tsearch2 indexes are
+ always up-to-date and keeping them so induces very little
+ overhead.</p>
+
+ <p>Before we get into the details, it is recommended that you
+ have installed and tested PostgreSQL, are reasonably familiar
+ with databases, the SQL query language and also understand the
+ basics of connecting to PostgreSQL from the local shell. This
+ document isn't intended for the complete PostgreSQL newbie, but
+ anyone with a reasonable grasp of the basics should be able to
+ follow it.</p>
+
+ <h3>INSTALLATION</h3>
+
+ <p>Starting with PostgreSQL version 7.4 tsearch2 is now
+ included in the contrib directory with the PostgreSQL sources.
+ contrib/tsearch2 is where you will find everything needed to
+ install and use tsearch2. Please note that tsearch2 will also
+ work with PostgreSQL version 7.3.x, but it is not the module
+ included with the source distribution. You will have to
+ download the module separately and install it in the same
+ fashion.</p>
+
+ <p>I installed the tsearch2 module to a PostgreSQL 7.3 database
+ from the contrib directory without squashing the original (old)
+ tsearch module. What I did was move the modules tsearch src
+ driectory into the contrib tree under the name tsearchV2.</p>
+
+ <p>Step one is to download the tsearch V2 module :</p>
+
+ <p><a href=
+ "http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/">[http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/]</a>
+ (check Development History for latest stable version !)</p>
+ <pre>
+ tar -zxvf tsearch-v2.tar.gz
+ mv tsearch2 PGSQL_SRC/contrib/
+ cd PGSQL_SRC/contrib/tsearch2
+</pre>
+
+ <p>If you are installing from PostgreSQL version 7.4 or higher,
+ you can skip those steps and just change to the
+ contrib/tsearch2 directory in the source tree and continue from
+ there.</p>
+
+ <p>Then continue with the regular building and installation
+ process</p>
+ <pre>
+ gmake
+ gmake install
+ gmake installcheck
+</pre>
+
+ <p>That is pretty much all you have to do, unless of course you
+ get errors. However if you get those, you better go check with
+ the mailing lists over at <a href=
+ "http://www.postgresql.org">http://www.postgresql.org</a> or
+ <a href=
+ "http://openfts.sourceforge.net/">http://openfts.sourceforge.net/</a>
+ since its never failed for me.</p>
+
+ <p>The directory in the contib/ and the directory from the
+ archive is called tsearch2. Tsearch2 is completely incompatible
+ with the previous version of tsearch. This means that both
+ versions can be installed into a single database, and migration
+ the new version may be much easier.</p>
+
+ <p>NOTE: the previous version of tsearch found in the
+ contrib/tsearch directory is depricated. ALthough it is still
+ available and included within PostgreSQL version 7.4. It will
+ be removed in version 7.5.</p>
+
+ <h3>ADDING TSEARCH2 FUNCTIONALITY TO A DATABASE</h3>
+
+ <p>We should create a database to use as an example for the
+ remainder of this file. We can call the database "ftstest". You
+ can create it from the command line like this:</p>
+ <pre>
+ #createdb ftstest
+</pre>
+
+ <p>If you thought installation was easy, this next bit is even
+ easier. Change to the PGSQL_SRC/contrib/tsearch2 directory and
+ type:</p>
+ <pre>
+ psql ftstest &lt; tsearch2.sql
+</pre>
+
+ <p>The file "tsearch2.sql" holds all the wonderful little
+ goodies you need to do full text indexing. It defines numerous
+ functions and operators, and creates the needed tables in the
+ database. There will be 4 new tables created after running the
+ tsearch2.sql file : pg_ts_dict, pg_ts_parser, pg_ts_cfg,
+ pg_ts_cfgmap are added.</p>
+
+ <p>You can check out the tables if you like:</p>
+ <pre>
+ #psql ftstest
+ ftstest=# \d
+ List of relations
+ Schema | Name | Type | Owner
+ --------+--------------+-------+----------
+ public | pg_ts_cfg | table | kopciuch
+ public | pg_ts_cfgmap | table | kopciuch
+ public | pg_ts_dict | table | kopciuch
+ public | pg_ts_parser | table | kopciuch
+ (4 rows)
+</pre>
+
+ <h3>TYPES AND FUNCTIONS PROVIDED BY TSEARCH2</h3>
+
+ <p>The first thing we can do is try out some of the types that
+ are provided for us. Lets look at the tsvector type provided
+ for us:</p>
+ <pre>
+ SELECT 'Our first string used today'::tsvector;
+ tsvector
+ ---------------------------------------
+ 'Our' 'used' 'first' 'today' 'string'
+ (1 row)
+</pre>
+
+ <p>The results are the words used within our string. Notice
+ they are not in any particular order. The tsvector type returns
+ a string of space separated words.</p>
+ <pre>
+ SELECT 'Our first string used today first string'::tsvector;
+ tsvector
+ -----------------------------------------------
+ 'Our' 'used' 'again' 'first' 'today' 'string'
+ (1 row)
+</pre>
+
+ <p>Notice the results string has each unique word ('first' and
+ 'string' only appear once in the tsvector value). Which of
+ course makes sense if you are searching the full text ... you
+ only need to know each unique word in the text.</p>
+
+ <p>Those examples were just casting a text field to that of
+ type tsvector. Lets check out one of the new functions created
+ by the tsearch2 module.</p>
+
+ <p>The function to_tsvector has 3 possible signatures:</p>
+ <pre>
+ to_tsvector(oid, text);
+ to_tsvector(text, text);
+ to_tsvector(text);
+</pre>
+
+ <p>We will use the second method using two text fields. The
+ overloaded methods provide us with a way to specifiy the way
+ the searchable text is broken up into words (Stemming process).
+ Right now we will specify the 'default' configuration. See the
+ section on TSEARCH2 CONFIGURATION to learn more about this.</p>
+ <pre>
+ SELECT to_tsvector('default',
+ 'Our first string used today first string');
+ to_tsvector
+ --------------------------------------------
+ 'use':4 'first':2,6 'today':5 'string':3,7
+ (1 row)
+</pre>
+
+ <p>The result returned from this function is of type tsvector.
+ The results came about by this reasoning: All of the words in
+ the text passed in are stemmed, or not used because they are
+ stop words defined in our configuration. Each lower case
+ morphed word is returned with all of the positons in the
+ text.</p>
+
+ <p>In this case the word "Our" is a stop word in the default
+ configuration. That means it will not be included in the
+ result. The word "first" is found at positions 2 and 6
+ (although "Our" is a stop word, it's position is maintained).
+ The word(s) positioning is maintained exactly as in the
+ original string. The word "used" is morphed to the word "use"
+ based on the default configuration for word stemming, and is
+ found at position 4. The rest of the results follow the same
+ logic. Just a reminder again ... the order of the 'word'
+ position in the output is not in any kind of order. (ie 'use':4
+ appears first)</p>
+
+ <p>If you want to view the output of the tsvector fields
+ without their positions, you can do so with the function
+ "strip(tsvector)".</p>
+ <pre>
+ SELECT strip(to_tsvector('default',
+ 'Our first string used today first string'));
+ strip
+ --------------------------------
+ 'use' 'first' 'today' 'string'
+</pre>
+
+ <p>If you wish to know the number of unique words returned in
+ the tsvector you can do so by using the function
+ "length(tsvector)"</p>
+ <pre>
+ SELECT length(to_tsvector('default',
+ 'Our first string used today first string'));
+ length
+ --------
+ 4
+ (1 row)
+</pre>
+
+ <p>Lets take a look at the function to_tsquery. It also has 3
+ signatures which follow the same rational as the to_tsvector
+ function:</p>
+ <pre>
+ to_tsquery(oid, text);
+ to_tsquery(text, text);
+ to_tsquery(text);
+</pre>
+
+ <p>Lets try using the function with a single word :</p>
+ <pre>
+ SELECT to_tsquery('default', 'word');
+ to_tsquery
+ -----------
+ 'word'
+ (1 row)
+</pre>
+
+ <p>I call the function the same way I would a to_tsvector
+ function, specifying the 'default' configuration for morphing,
+ and the result is the stemmed output 'word'.</p>
+
+ <p>Lets attempt to use the function with a string of multiple
+ words:</p>
+ <pre>
+ SELECT to_tsquery('default', 'this is many words');
+ ERROR: Syntax error
+</pre>
+
+ <p>The function can not accept a space separated string. The
+ intention of the to_tsquery function is to return a type of
+ "tsquery" used for searching a tsvector field. What we need to
+ do is search for one to many words with some kind of logic (for
+ now simple boolean).</p>
+ <pre>
+ SELECT to_tsquery('default', 'searching|sentence');
+ to_tsquery
+ ----------------------
+ 'search' | 'sentenc'
+ (1 row)
+</pre>
+
+ <p>Notice that the words are separated by the boolean logic
+ "OR", the text could contain boolean operators &amp;,|,!,()
+ with their usual meaning.</p>
+
+ <p>You can not use words defined as being a stop word in your
+ configuration. The function will not fail ... you will just get
+ no result, and a NOTICE like this:</p>
+ <pre>
+ SELECT to_tsquery('default', 'a|is&amp;not|!the');
+ NOTICE: Query contains only stopword(s)
+ or doesn't contain lexem(s), ignored
+ to_tsquery
+ -----------
+ (1 row)
+</pre>
+
+ <p>That is a beginning to using the types, and functions
+ defined in the tsearch2 module. There are numerous more
+ functions that I have not touched on. You can read through the
+ tsearch2.sql file built when compiling to get more familiar
+ with what is included.</p>
+
+ <h3>INDEXING FIELDS IN A TABLE</h3>
+
+ <p>The next stage is to add a full text index to an existing
+ table. In this example we already have a table defined as
+ follows:</p>
+ <pre>
+ CREATE TABLE tblMessages
+ (
+ intIndex int4,
+ strTopic varchar(100),
+ strMessage text
+ );
+</pre>
+
+ <p>We are assuming there are several rows with some kind of
+ data in them. Any data will do, just do several inserts with
+ test strings for a topic, and a message. here is some test data
+ I inserted. (yes I know it's completely useless stuff ;-) but
+ it will serve our purpose right now).</p>
+ <pre>
+ INSERT INTO tblMessages
+ VALUES ('1', 'Testing Topic', 'Testing message data input');
+ INSERT INTO tblMessages
+ VALUES ('2', 'Movie', 'Breakfast at Tiffany\'s');
+ INSERT INTO tblMessages
+ VALUES ('3', 'Famous Author', 'Stephen King');
+ INSERT INTO tblMessages
+ VALUES ('4', 'Political Topic',
+ 'Nelson Mandella is released from prison');
+ INSERT INTO tblMessages
+ VALUES ('5', 'Nursery rhyme phrase',
+ 'Little jack horner sat in a corner');
+ INSERT INTO tblMessages
+ VALUES ('6', 'Gettysburg address quotation',
+ 'Four score and seven years ago'
+ ' our fathers brought forth on this'
+ ' continent a new nation, conceived in'
+ ' liberty and dedicated to the proposition'
+ ' that all men are created equal');
+ INSERT INTO tblMessages
+ VALUES ('7', 'Classic Rock Bands',
+ 'Led Zeppelin Grateful Dead and The Sex Pistols');
+ INSERT INTO tblMessages
+ VALUES ('8', 'My birth address',
+ '18 Sommervile road, Regina, Saskatchewan');
+ INSERT INTO tblMessages
+ VALUES ('9', 'Joke', 'knock knock : who\'s there?'
+ ' I will not finish this joke');
+ INSERT INTO tblMessages
+ VALUES ('10', 'Computer information',
+ 'My computer is a pentium III 400 mHz'
+ ' with 192 megabytes of RAM');
+</pre>
+
+ <p>The next stage is to create a special text index which we
+ will use for FTI, so we can search our table of messages for
+ words or a phrase. We do this using the SQL command:</p>
+ <pre>
+ ALTER TABLE tblMessages ADD idxFTI tsvector;
+</pre>
+
+ <p>Note that unlike traditional indexes, this is actually a new
+ field in the same table, which is then used (through the magic
+ of the tsearch2 operators and functions) by a special index we
+ will create in a moment.</p>
+
+ <p>The general rule for the initial insertion of data will
+ follow four steps:</p>
+ <pre>
+ 1. update table
+ 2. vacuum full analyze
+ 3. create index
+ 4. vacuum full analyze
+</pre>
+
+ <p>The data can be updated into the table, the vacuum full
+ analyze will reclaim unused space. The index can be created on
+ the table after the data has been inserted. Having the index
+ created prior to the update will slow down the process. It can
+ be done in that manner, this way is just more efficient. After
+ the index has been created on the table, vacuum full analyze is
+ run again to update postgres's statistics (ie having the index
+ take effect).</p>
+ <pre>
+ UPDATE tblMessages SET idxFTI=to_tsvector('default', strMessage);
+ VACUUM FULL ANALYZE;
+</pre>
+
+ <p>Note that this only inserts the field strMessage as a
+ tsvector, so if you want to also add strTopic to the
+ information stored, you should instead do the following, which
+ effectively concatenates the two fields into one before being
+ inserted into the table:</p>
+ <pre>
+ UPDATE tblMessages
+ SET idxFTI=to_tsvector('default',coalesce(strTopic,'') ||' '|| coalesce(strMessage,''));
+ VACUUM FULL ANALYZE;
+</pre>
+
+ <p><strong>Using the coalesce function makes sure this
+ concatenation also works with NULL fields.</strong></p>
+
+ <p>We need to create the index on the column idxFTI. Keep in
+ mind that the database will update the index when some action
+ is taken. In this case we _need_ the index (The whole point of
+ Full Text INDEXINGi ;-)), so don't worry about any indexing
+ overhead. We will create an index based on the gist function.
+ GiST is an index structure for Generalized Search Tree.</p>
+ <pre>
+ CREATE INDEX idxFTI_idx ON tblMessages USING gist(idxFTI);
+ VACUUM FULL ANALYZE;
+</pre>
+
+ <p>After you have converted all of your data and indexed the
+ column, you can select some rows to see what actually happened.
+ I will not display output here but you can play around
+ yourselves and see what happened.</p>
+
+ <p>The last thing to do is set up a trigger so every time a row
+ in this table is changed, the text index is automatically
+ updated. This is easily done using:</p>
+ <pre>
+ CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON tblMessages
+ FOR EACH ROW EXECUTE PROCEDURE tsearch2(idxFTI, strMessage);
+</pre>
+
+ <p>Or if you are indexing both strMessage and strTopic you
+ should instead do:</p>
+ <pre>
+ CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON tblMessages
+ FOR EACH ROW EXECUTE PROCEDURE
+ tsearch2(idxFTI, strTopic, strMessage);
+</pre>
+
+ <p>Before you ask, the tsearch2 function accepts multiple
+ fields as arguments so there is no need to concatenate the two
+ into one like we did before.</p>
+
+ <p>If you want to do something specific with columns, you may
+ write your very own trigger function using plpgsql or other
+ procedural languages (but not SQL, unfortunately) and use it
+ instead of <em>tsearch2</em> trigger.</p>
+
+ <p>You could however call other stored procedures from within
+ the tsearch2 function. Lets say we want to create a function to
+ remove certain characters (like the @ symbol from all
+ text).</p>
+ <pre>
+ CREATE FUNCTION dropatsymbol(text)
+ RETURNS text AS 'select replace($1, \'@\', \' \');' LANGUAGE SQL;
+</pre>
+
+ <p>Now we can use this function within the tsearch2 function on
+ the trigger.</p>
+ <pre>
+ DROP TRIGGER tsvectorupdate ON tblmessages;
+ CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON tblMessages
+ FOR EACH ROW EXECUTE PROCEDURE tsearch2(idxFTI, dropatsymbol, strMessage);
+ INSERT INTO tblmessages VALUES (69, 'Attempt for dropatsymbol', 'Test@test.com');
+</pre>
+
+ <p>If at this point you receive an error stating: ERROR: Can't
+ find tsearch config by locale</p>
+
+ <p>Do not worry. You have done nothing wrong. And tsearch2 is
+ not broken. All that has happened here is that the
+ configuration is setup to use a configuration based on the
+ locale of the server. All you have to do is change your default
+ configuration, or add a new one for your specific locale. See
+ the section on TSEARCH2 CONFIGURATION.</p>
+ <pre class="real">
+ SELECT * FROM tblmessages WHERE intindex = 69;
+
+ intindex | strtopic | strmessage | idxfti
+ ----------+--------------------------+---------------+-----------------------
+ 69 | Attempt for dropatsymbol | Test@test.com | 'test':1 'test.com':2
+ (1 row)
+</pre>Notice that the string content was passed throught the stored
+procedure dropatsymbol. The '@' character was replaced with a
+single space ... and the output from the procedure was then stored
+in the tsvector column.
+
+ <p>This could be useful for removing other characters from
+ indexed text, or any kind of preprocessing needed to be done on
+ the text prior to insertion into the index.</p>
+
+ <h3>QUERYING A TABLE</h3>
+
+ <p>There are some examples in the README.tsearch2 file for
+ querying a table. One major difference between tsearch and
+ tsearch2 is the operator ## is no longer available. Only the
+ operator @@ is defined, using the types tsvector on one side
+ and tsquery on the other side.</p>
+
+ <p>Lets search the indexed data for the word "Test". I indexed
+ based on the the concatenation of the strTopic, and the
+ strMessage:</p>
+ <pre>
+ SELECT intindex, strtopic FROM tblmessages
+ WHERE idxfti @@ 'test'::tsquery;
+ intindex | strtopic
+ ----------+---------------
+ 1 | Testing Topic
+ (1 row)
+</pre>
+
+ <p>The only result that matched was the row with a topic
+ "Testing Topic". Notice that the word I search for was all
+ lowercase. Let's see what happens when I query for uppercase
+ "Test".</p>
+ <pre>
+ SELECT intindex, strtopic FROM tblmessages
+ WHERE idxfti @@ 'Test'::tsquery;
+ intindex | strtopic
+ ----------+----------
+ (0 rows)
+</pre>
+
+ <p>We get zero rows returned. The reason is because when the
+ text was inserted, it was morphed to my default configuration
+ (because of the call to to_tsvector in the UPDATE statement).
+ If there was no morphing done, and the tsvector field(s)
+ contained the word 'Text', a match would have been found.</p>
+
+ <p>Most likely the best way to query the field is to use the
+ to_tsquery function on the right hand side of the @@ operator
+ like this:</p>
+ <pre>
+ SELECT intindex, strtopic FROM tblmessages
+ WHERE idxfti @@ to_tsquery('default', 'Test | Zeppelin');
+ intindex | strtopic
+ ----------+--------------------
+ 1 | Testing Topic
+ 7 | Classic Rock Bands
+ (2 rows)
+</pre>
+
+ <p>That query searched for all instances of "Test" OR
+ "Zeppelin". It returned two rows: the "Testing Topic" row, and
+ the "Classic Rock Bands" row. The to_tsquery function performed
+ the correct morphology upon the parameters, and searched the
+ tsvector field appropriately.</p>
+
+ <p>The last example here relates to searching for a phrase, for
+ example "minority report". This poses a problem with regard to
+ tsearch2, as it doesn't index phrases, only words. But there is
+ a way around which doesn't appear to have a significant impact
+ on query time, and that is to use a query such as the
+ following:</p>
+ <pre>
+ SELECT intindex, strTopic FROM tblmessages
+ WHERE idxfti @@ to_tsquery('default', 'gettysburg &amp; address')
+ AND strMessage ~* '.*men are created equal.*';
+ intindex | strtopic
+ ----------+------------------------------
+ 6 | Gettysburg address quotation
+ (1 row)
+ SELECT intindex, strTopic FROM tblmessages
+ WHERE idxfti @@ to_tsquery('default', 'gettysburg &amp; address')
+ AND strMessage ~* '.*something that does not exist.*';
+ intindex | strtopic
+ ----------+----------
+ (0 rows)
+</pre>
+
+ <p>Of course if your indexing both strTopic and strMessage, and
+ want to search for this phrase on both, then you will have to
+ get out the brackets and extend this query a little more.</p>
+
+ <h3>TSEARCH2 CONFIGURATION</h3>
+
+ <p>Some words such as "and", "the", and "who" are automatically
+ not indexed, since they belong to a pre-existing dictionary of
+ "Stop Words" which tsearch2 does not perform indexing on. If
+ someone needs to search for "The Who" in your database, they
+ are going to have a tough time coming up with any results,
+ since both are ignored in the indexes. But there is a
+ solution.</p>
+
+ <p>Lets say we want to add a word into the stop word list for
+ english stemming. We could edit the file
+ :'/usr/local/pgsql/share/english.stop' and add a word to the
+ list. I edited mine to exclude my name from indexing:</p>
+ <pre>
+ - Edit /usr/local/pgsql/share/english.stop
+ - Add 'andy' to the list
+ - Save the file.
+</pre>
+
+ <p>When you connect to the database, the dict_init procedure is
+ run during initialization. And in my configuration it will read
+ the stop words from the file I just edited. If you were
+ connected to the DB while editing the stop words, you will need
+ to end the current session and re-connect. When you re-connect
+ to the database, 'andy' is no longer indexed:</p>
+ <pre>
+ SELECT to_tsvector('default', 'Andy');
+ to_tsvector
+ ------------
+ (1 row)
+</pre>
+
+ <p>Originally I would get the result :</p>
+ <pre>
+ SELECT to_tsvector('default', 'Andy');
+ to_tsvector
+ ------------
+ 'andi':1
+ (1 row)
+</pre>
+
+ <p>But since I added it as a stop word, it would be ingnored on
+ the indexing. The stop word added was used in the dictionary
+ "en_stem". If I were to use a different configuration such as
+ 'simple', the results would be different. There are no stop
+ words for the simple dictionary. It will just convert to lower
+ case, and index every unique word.</p>
+ <pre>
+ SELECT to_tsvector('simple', 'Andy andy The the in out');
+ to_tsvector
+ -------------------------------------
+ 'in':5 'out':6 'the':3,4 'andy':1,2
+ (1 row)
+</pre>
+
+ <p>All this talk about which configuration to use is leading us
+ into the actual configuration of tsearch2. In the examples in
+ this document the configuration has always been specified when
+ using the tsearch2 functions:</p>
+ <pre>
+ SELECT to_tsvector('default', 'Testing the default config');
+ SELECT to_tsvector('simple', 'Example of simple Config');
+</pre>
+
+ <p>The pg_ts_cfg table holds each configuration you can use
+ with the tsearch2 functions. As you can see the ts_name column
+ contains both the 'default' configurations based on the 'C'
+ locale. And the 'simple' configuration which is not based on
+ any locale.</p>
+ <pre>
+ SELECT * from pg_ts_cfg;
+ ts_name | prs_name | locale
+ -----------------+----------+--------------
+ default | default | C
+ default_russian | default | ru_RU.KOI8-R
+ simple | default |
+ (3 rows)
+</pre>
+
+ <p>Each row in the pg_ts_cfg table contains the name of the
+ tsearch2 configuration, the name of the parser to use, and the
+ locale mapped to the configuration. There is only one parser to
+ choose from the table pg_ts_parser called 'default'. More
+ parsers could be written, but for our needs we will use the
+ default.</p>
+
+ <p>There are 3 configurations installed by tsearch2 initially.
+ If your locale is set to 'en_US' for example (like my laptop),
+ then as you can see there is currently no dictionary configured
+ to use with that locale. You can either set up a new
+ configuration or just use one that already exists. If I do not
+ specify which configuration to use in the to_tsvector function,
+ I receive the following error.</p>
+ <pre>
+ SELECT to_tsvector('learning tsearch is like going to school');
+ ERROR: Can't find tsearch config by locale
+</pre>
+
+ <p>We will create a new configuration for use with the server
+ encoding 'en_US'. The first step is to add a new configuration
+ into the pg_ts_cfg table. We will call the configuration
+ 'default_english', with the default parser and use the locale
+ 'en_US'.</p>
+ <pre>
+ INSERT INTO pg_ts_cfg (ts_name, prs_name, locale)
+ VALUES ('default_english', 'default', 'en_US');
+</pre>
+
+ <p>We have only declared that there is a configuration called
+ 'default_english'. We need to set the configuration of how
+ 'default_english' will work. The next step is creating a new
+ dictionary to use. The configuration of the dictionary is
+ completlely different in tsearch2. In the prior versions to
+ make changes, you would have to re-compile your changes into
+ the tsearch.so. All of the configuration has now been moved
+ into the system tables created by executing the SQL code from
+ tsearch2.sql</p>
+
+ <p>Lets take a first look at the pg_ts_dict table</p>
+ <pre>
+ ftstest=# \d pg_ts_dict
+ Table "public.pg_ts_dict"
+ Column | Type | Modifiers
+ -----------------+---------+-----------
+ dict_name | text | not null
+ dict_init | oid |
+ dict_initoption | text |
+ dict_lemmatize | oid | not null
+ dict_comment | text |
+ Indexes: pg_ts_dict_idx unique btree (dict_name)
+</pre>
+
+ <p>The dict_name column is the name of the dictionary, for
+ example 'simple', 'en_stem' or 'ru_stem'. The dict_init column
+ is an OID of a stored procedure to run for initialization of
+ that dictionary, for example 'snb_en_init' or 'snb_ru_init'.
+ The dict_init option is used for options passed to the init
+ function for the stored procedure. In the cases of 'en_stem' or
+ 'ru_stem' it is a path to a stopword file for that dictionary,
+ for example '/usr/local/pgsql/share/english.stop'. This is
+ however dictated by the dictionary. ISpell dictionaries may
+ require different options. The dict_lemmatize column is another
+ OID of a stored procedure to the function used to lemmitize,
+ for example 'snb_lemmatize'. The dict_comment column is just a
+ comment.</p>
+
+ <p>Next we will configure the use of a new dictionary based on
+ ISpell. We will assume you have ISpell installed on you
+ machine. (in /usr/local/lib)</p>
+
+ <p>First lets register the dictionary(ies) to use from ISpell.
+ We will use the english dictionary from ISpell. We insert the
+ paths to the relevant ISpell dictionary (*.hash) and affixes
+ (*.aff) files. There seems to be some question as to which
+ ISpell files are to be used. I installed ISpell from the latest
+ sources on my computer. The installation installed the
+ dictionary files with an extension of *.hash. Some
+ installations install with an extension of *.dict As far as I
+ know the two extensions are equivilant. So *.hash ==
+ *.dict.</p>
+
+ <p>We will also continue to use the english word stop file that
+ was installed for the en_stem dictionary. You could use a
+ different one if you like. The ISpell configuration is based on
+ the "ispell_template" dictionary installed by default with
+ tsearch2. We will use the OIDs to the stored procedures from
+ the row where the dict_name = 'ispell_template'.</p>
+ <pre>
+ INSERT INTO pg_ts_dict
+ (SELECT 'en_ispell',
+ dict_init,
+ 'DictFile="/usr/local/lib/english.hash",'
+ 'AffFile="/usr/local/lib/english.aff",'
+ 'StopFile="/usr/local/pgsql/share/english.stop"',
+ dict_lexize
+ FROM pg_ts_dict
+ WHERE dict_name = 'ispell_template');
+</pre>
+
+ <p>Next we need to set up the configuration for mapping the
+ dictionay use to the lexxem parsings. This will be done by
+ altering the pg_ts_cfgmap table. We will insert several rows,
+ specifying to using the new dictionary we installed and
+ configured for use within tsearch2. There are several type of
+ lexims we would be concerned with forcing the use of the ISpell
+ dictionary.</p>
+ <pre>
+ INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
+ VALUES ('default_english', 'lhword', '{en_ispell,en_stem}');
+ INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
+ VALUES ('default_english', 'lpart_hword', '{en_ispell,en_stem}');
+ INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
+ VALUES ('default_english', 'lword', '{en_ispell,en_stem}');
+</pre>
+
+ <p>We have just inserted 3 records to the configuration
+ mapping, specifying that the lexem types for "lhword,
+ lpart_hword and lword" are to be stemmed using the 'en_ispell'
+ dictionary we added into pg_ts_dict, when using the
+ configuration ' default_english' which we added to
+ pg_ts_cfg.</p>
+
+ <p>There are several other lexem types used that we do not need
+ to specify as using the ISpell dictionary. We can simply insert
+ values using the 'simple' stemming process dictionary.</p>
+ <pre>
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'url', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'host', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'sfloat', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'uri', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'int', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'float', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'email', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'word', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'hword', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'nlword', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'nlpart_hword', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'part_hword', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'nlhword', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'file', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'uint', '{simple}');
+ INSERT INTO pg_ts_cfgmap
+ VALUES ('default_english', 'version', '{simple}');
+</pre>
+
+ <p>Our addition of a configuration for 'default_english' is now
+ complete. We have successfully created a new tsearch2
+ configuration. At the same time we have also set the new
+ configuration to be our default for en_US locale.</p>
+ <pre>
+ SELECT to_tsvector('default_english',
+ 'learning tsearch is like going to school');
+ to_tsvector
+ --------------------------------------------------
+ 'go':5 'like':4 'learn':1 'school':7 'tsearch':2
+ SELECT to_tsvector('learning tsearch is like going to school');
+ to_tsvector
+ --------------------------------------------------
+ 'go':5 'like':4 'learn':1 'school':7 'tsearch':2
+ (1 row)
+</pre>
+
+ <p>In the case that you already have a configuration set for
+ the locale, and you are changing it to your new dictionary
+ configuration. You will have to set the old locale to NULL. If
+ we are using the 'C' locale then we would do this:</p>
+ <pre>
+ UPDATE pg_ts_cfg SET locale=NULL WHERE locale = 'C';
+</pre>
+
+ <p>That about wraps up the configuration of tsearch2. There is
+ much more you can do with the tables provided. This was just an
+ introduction to get things working rather quickly.</p>
+
+ <h3>ADDING NEW DICTIONARIES TO TSEARCH2</h3>
+
+ <p>To aid in the addition of new dictionaries to the tsearch2
+ module you can use another additional module in combination
+ with tsearch2. The gendict module is included into tsearch2
+ distribution and is available from gendict/ subdirectory.</p>
+
+ <p>I will not go into detail about installation and
+ instructions on how to use gendict to it's fullest extent right
+ now. You can read the README.gendict ... it has all of the
+ instructions and information you will need.</p>
+
+ <h3>BACKING UP AND RESTORING DATABASES THAT FEATURE
+ TSEARCH2</h3>
+
+ <p>Believe it or not, this isn't as straight forward as it
+ should be, and you will have problems trying to backup and
+ restore any database which uses tsearch2 unless you take the
+ steps shown below. And before you ask using pg_dumpall will
+ result in failure every time. These took a lot of trial and
+ error to get working, but the process as laid down below has
+ been used a dozen times now in live production environments so
+ it should work fine.</p>
+
+ <p>HOWEVER never rely on anyone elses instructions to backup
+ and restore a database system, always develop and understand
+ your own methodology, and test it numerous times before you
+ need to do it for real.</p>
+
+ <p>To Backup a PostgreSQL database that uses the tsearch2
+ module:</p>
+
+ <p>1) Backup any global database objects such as users and
+ groups (this step is usually only necessary when you will be
+ restoring to a virgin system)</p>
+ <pre>
+ pg_dumpall -g &gt; GLOBALobjects.sql
+</pre>
+
+ <p>2) Backup the full database schema using pg_dump</p>
+ <pre>
+ pg_dump -s DATABASE &gt; DATABASEschema.sql
+</pre>
+
+ <p>3) Backup the full database using pg_dump</p>
+ <pre>
+ pg_dump -Fc DATABASE &gt; DATABASEdata.tar
+</pre>
+
+ <p>To Restore a PostgreSQL database that uses the tsearch2
+ module:</p>
+
+ <p>1) Create the blank database</p>
+ <pre>
+ createdb DATABASE
+</pre>
+
+ <p>2) Restore any global database objects such as users and
+ groups (this step is usually only necessary when you will be
+ restoring to a virgin system)</p>
+ <pre>
+ psql DATABASE &lt; GLOBALobjects.sql
+</pre>
+
+ <p>3) Create the tsearch2 objects, functions and operators</p>
+ <pre>
+ psql DATABASE &lt; tsearch2.sql
+</pre>
+
+ <p>4) Edit the backed up database schema and delete all SQL
+ commands which create tsearch2 related functions, operators and
+ data types, BUT NOT fields in table definitions that specify
+ tsvector types. If your not sure what these are, they are the
+ ones listed in tsearch2.sql. Then restore the edited schema to
+ the database</p>
+ <pre>
+ psql DATABASE &lt; DATABASEschema.sql
+</pre>
+
+ <p>5) Restore the data for the database</p>
+ <pre>
+ pg_restore -N -a -d DATABASE DATABASEdata.tar
+</pre>
+
+ <p>If you get any errors in step 4, it will most likely be
+ because you forgot to remove an object that was created in
+ tsearch2.sql. Any errors in step 5 will mean the database
+ schema was probably restored wrongly.</p>
+ </div>
+</body>
+</html>